Sindbad~EG File Manager
/* common code needed by both exec.c and autosimp.c */
/* M. Beeson, for Mathpert */
/*
6.1.93 extracted from exec.c
10.4.98 modified
6.18.06 made push_multiplicities exported
*/
#define AUTOMODE_DLL
#include <assert.h>
#include <stdlib.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "display1.h"
#include "document.h"
#include "tdefn.h"
#include "checkarg.h"
#include "operator.h"
#include "nextline.h"
#include "ops.h"
#include "trig.h"
#include "calc.h"
#include "prover.h"
#include "automode.h"
#include "probtype.h"
#include "mpminmax.h" /* complement */
#include "algaux.h" /* topflatten */
#include "symbols.h"
#include "bigrect.h"
#include "lterm.h" /* pathlist, cons */
#include "cflags.h" /* get_currenttopic */
#include "optable.h" /* access_optable */
#include "eqn.h" /* econstant */
#include "autosimp.h" /* SaveShowStepState */
#include "pvalaux.h" /* topflatten */
#include "series.h"
static term flatten_multiplicities(term);
static int substleft(term new, term old, term t, term *ans);
static int fixup(actualop code,term arg,term q,term p,term t,term *next);
static int special_subst(term new, term old, term t, term *ans);
static int legal_interval(term t);
static int subcomputation(int linenumber);
static term maxscope;
/* if an operator is applied inside a binding operator, maxscope is
used to record the 'locus' of binding, i.e. the term containing
the binding operator. The result of (this application of)
the operator will then not be used outside this term (although
the operator itself can be APPLIED again outside maxscope).
Maxscope will be initialized to ILLEGAL by exec and one_step, and
set by exec_aux (in menu mode) or autosimp (in auto mode) when
exiting from binding terms (just before releasebinders is called. */
/*_________________________________________________________________*/
pathlist * cons(int x, unsigned short y, pathlist *tail)
/* make a new pathlist with first item x followed by tail */
/* static copy for symbols.dll. If changed modify the original
in select.c */
{ pathlist *ans = mallocate(sizeof(pathlist));
if(ans == NULL)
nospace();
ans->data = x;
ans->functor = y;
ans->next = tail;
return ans;
}
/*_________________________________________________________________*/
pathlist *copy_pathlist(pathlist *source)
{ if(!source)
return NULL;
return cons(source->data,source->functor,copy_pathlist(source->next));
}
/*____________________________________________________________*/
void reset_maxscope(void)
{ SETFUNCTOR(maxscope, ILLEGAL,0);
}
/*____________________________________________________________*/
void set_maxscope(term t)
{ maxscope = t;
}
/*____________________________________________________________*/
term get_maxscope(void)
{ return maxscope;
}
/*____________________________________________________________*/
int nextline(actualop code, term arg, term q, term p, term t, term *next)
/* t is the previous line; the operator 'code' has just transformed
p to q. But we don't just subst(q,p,t,next); because
(1) the operator may be contextsensitive, in which case we want to
substitute only for the correct occurrence
(2) the application of the operator within the scope of a bound
variable may have depended on inferences only valid within that scope,
e.g. in rewriting integral(abs(1-x^2),x,-1,1) => integral(1-x^2,x,-1,1);
we wouldn't want to drop abs in other integrals with different scopes.
This problem is solved using maxscope, documented above. We substitute
only within maxscope, if maxscope is not ILLEGAL.
(3) We want to apply the operator to certain other terms (within maxscope
if it is non-NULL) including at least the right-hand brothers of p if
p is a child inequality of an interval_as_and term, using the same arg.
(4) Even if the operator is not context-sensitive we don't want to
substitute into colored terms (which are the result of previous applications
of the operator whose secondary applications to the same line are being
done when this function is called). Therefore special_subst is used
instead of subst.
See further comments in exec.c just before exec_aux.
Finally, we have to call update_assumptions.
Return 0 for success, 1 for failure. Failure is rare, but can occur,
e.g. if posnum1 was applied to one half on an interval-as-and pair of
inequalities, it won't apply to the other half, so we refuse to use it.
*/
{ term temp,u;
int err;
unsigned short f = FUNCTOR(p);
int change_maxscope = 0;
int problemtype = get_problemtype();
if(problemtype == MINMAX && contains(q,MULTIPLICITY))
q = strip_multiplicities(q);
else if(f == '=' && equals(q,false) && FUNCTOR(t) == AND)
{ *next = false;
return 0;
}
else if(f == '=' &&
(FUNCTOR(q) == OR || equals(q,false) || equals(q,true)) &&
contains(t,MULTIPLICITY)
)
/* we never solve equations inside the scope of a bound variable */
{ special_subst(q,p,t,&temp);
*next = push_multiplicities(temp);
update_assumptions(p,q,next);
return 0;
}
else if(f == '=' && FUNCTOR(q) == MULTIPLICITY && contains(t,MULTIPLICITY))
{ special_subst(q,p,t,&temp);
*next = flatten_multiplicities(temp);
update_assumptions(p,q,next);
return 0;
}
if(JUMPER(code)) /* defined in ops.h */
*next = q;
else if(contextsensitive(code))
{ if(get_mathmode() == AUTOMODE)
{ /* the leftmost occurrence may not necessarily be the correct one
to substitute for, e.g. in bringing (2-i)i to complex polar
form, we want to work on the second 'i'. */
unsigned short *path = get_path();
int err = subterm_at_path(t,path,&u);
if(err || !equals(u,p))
/* assert(0); oops, this can happen! check_problem sets
the default mathmode to AUTOMODE */
substleft(q,p,t,next);
else
pathsub(path,q,t,next);
}
else
substleft(q,p,t,next); /* using the Operations menu */
}
else if(FUNCTOR(maxscope) == ILLEGAL)
{ if(INEQUALITY(f) && contains(t,AND)) /* so in particular !equals(p,t) */
{ err = fixup(code,arg,q,p,t,next); /* apply code to the brothers of p too */
if(err)
return 1;
}
else
special_subst(q,p,t,next);
if(FUNCTOR(q)==OR && FUNCTOR(t) == OR && FUNCTOR(*next)==OR)
*next = topflatten(*next);
if(FUNCTOR(q)==AND && FUNCTOR(t) == AND && FUNCTOR(*next)==AND)
*next = topflatten(*next);
if(problemtype == RELATED_RATES)
{ if( (void *) code == (void *) difeqn &&
FUNCTOR(*next) == OR
)
{ u = ARG(0,*next);
if(FUNCTOR(u) == AND)
u = topflatten(u);
ARGREP(*next,0,u);
SETFUNCTOR(*next,AND,ARITY(*next));
*next = topflatten(*next);
/* Once the equation has been differentiated, t refers to
a specific instant of time and all the equations can be
treated as a normal system of equations. */
}
else if( FUNCTOR(q)==AND && FUNCTOR(*next) == OR &&
FUNCTOR(ARG(0,*next))==AND
)
{ temp = topflatten(ARG(0,*next));
ARGREP(*next,0,temp);
}
}
}
else /* the operator worked within the scope of a bound variable */
{ special_subst(q,p,maxscope,&temp);
special_subst(temp, maxscope,t,next);
change_maxscope = 1;
}
if(equals(t,*next) /* this happens if p is not actually a subterm of t,
see the comments above */
&& (void *) code != (void *) lineupvars
&& (void *) code != (void *) selecteqn
&& (void *) code != (void *) showalleqns
&& (void *) code != (void *) evalatpoint
/* these are the only operators that succeed
without changing the formula */
)
return 1;
if(change_maxscope)
maxscope = temp; /* needed so maxscope is correct if the operator
succeeds again somewhere to the right of
its original focus. */
return 0;
}
/*_____________________________________________________________________*/
MEXPORT_AUTOMODE void update_assumptions(term old, term new, term *next)
/* substitute new for old throughout the list of current assumptions;
if anything changes, store the result as a revised version of
that assumption.
Then, call simplify_assumptions (in prover.c)
When this is called, an operator has been successfully applied to
history[currentline], changing old to new, but currentline hasn't been
incremented yet. Indeed, this is called while we are still in the
midst of traversing the term, and the SAME operator (if not
context-sensitive) may yet be applied at another 'focus', i.e. to another
subterm. Consequently 'binders' may still be in force if the traversal
is in the scope of a bound variable--this is no problem as the bound
variables can't be appearing in the assumptions anyway, unless in a
protected formula.
This function does not check for contradictions among the assumptions
or do anything special if one of the assumptions simplifies to false.
The pointer 'next' is used only in MINMAX problems.
For example: if f(x) = abs(x); when we differentiate it the
assumption x != 0 is generated, but we don't want to keep that as an assumption;
instead its negation should go into the current line as another case
to consider. Otherwise, when for example we consider points where f'(x)
is undefined, the equation x=0 will be rejected as contradicting assumptions,
and we won't find the minimum at x=0. This can't be corrected in individual
operators, as MANY operators might generate assumptions, and logically
SHOULDN'T be: minmax problems are different than other kinds, in that we
definitely DON'T want to make any assumption that x is not equal to
certain values, except that it is in the interval under consideration.
Anyway, in MINMAX problems, we have to remove any assumptions made at the
current line and enlarge the current line (passed as *next) to a disjunction
including the new cases. */
{ int i,j;
unsigned short m,m2,k;
term t,u,ans,x,temp;
int flag = 0; /* set if we need to call simplify_assumptions */
int currentline;
int problemtype = get_problemtype();
int nextassumption = get_nextassumption();
assumption **assumptions = get_assumptions();
if(nextassumption == 0)
return; /* nothing to do so get out fast */
currentline = get_currentline();
SaveShowStepState();
/* lpt can call ssolve, which calls ResetShowStepOperation, so
we have to save and restore the ShowStepOperation if any,
in order that it should not be changed by this function */
for(i=0;i<nextassumption;i++)
{ t = assumptions[i]->prop;
if(assumptions[i]->line == currentline+1)
flag = 1;
special_subst(new,old,t,&u);
if(!equals(t,u))
{ ans = lpt(u);
/* Now push ans onto assumptions[i] */
/* EXCEPT: lpt can possibly have created an AND,
which has to be split. The first conjunct will
go into assumptions[i], and the rest will just be
fresh assumptions.
*/
if(FUNCTOR(ans) != AND && !interval_as_and(ans))
{ push_assumption(ans,i);
flag = 1;
}
else
{ push_assumption(ARG(0,ans),i);
flag = 1;
for(j=1;j<ARITY(ans);j++)
assume(ARG(j,ans));
}
}
}
if(flag)
{ if(problemtype == MINMAX)
{ /* Count the assumptions that have been made at this line.
Put them all into a new term 'newcases'. Decrement
nextassumption, which effectively gets rid of the new
assumption(s). Form the disjunction of the new cases and
the current line and make that the new current line (taking
care to flatten if the old current line is already
a disjunction). Finally remove duplicate disjuncts
from the new current line.
*/
k=0;
x = get_eigenvariable();
for(i=nextassumption-1;i>=0 && assumptions[i]->line==currentline+1;i--)
++k;
assert(k > 0); /*otherwise flag would have been zero */
ans = make_term(OR,(unsigned short)( FUNCTOR(*next) == OR ? ARITY(*next) + k : k+1));
temp = make_term(OR,k);
if(FUNCTOR(*next) == OR)
{ /* copy the old cases into the first args of ans */
for(i=0;i<ARITY(*next);i++)
ARGREP(ans,i,ARG(i,*next));
}
else
{ ARGREP(ans,0,*next);
i=1;
}
/* Now put the new assumptions (negated) into the other args */
m = m2 = 0;
for(j=0;j<k;j++)
{ u = assumptions[nextassumption-1-j]->prop;
if(contains(u,FUNCTOR(x)))
{ ARGREP(ans,i+m,complement(u));
++m;
}
else
{ ARGREP(temp,m2,u);
++m2;
}
}
SETFUNCTOR(ans,OR,i+m);
assert(nextassumption >= k);
set_nextassumption((unsigned short) (nextassumption-k)); /* get rid of these assumptions */
if(m2)
{ for(j=0;j<m2;j++)
assume(ARG(j,temp));
}
RELEASE(temp);
remove_dups(ans,next); /* this will be the new current line */
}
else
simplify_assumptions(new);
/* does any old assumption simplify in
light of the most recent assumption(s) ? */
}
RestoreShowStepState();
}
/*___________________________________________________________*/
MEXPORT_AUTOMODE int specialop(term arg, actualop code)
/* return 1 if the next history[currentline] should not reflect the
actual results of the operator, else return 0. */
{ if( (void *) code == (void *) evalatpoint
|| (void *) code == (void *) checknumerically
|| (void *) code == (void *) solvenumerically
|| (void *) code == (void *) testlimit
)
return 1;
if(FUNCTOR(arg) != ILLEGAL &&
( (void *) code == (void *) evaluatesigmatorational
|| (void *) code == (void *) evaluatesigmatodecimal
|| (void *) code == (void *) integratenumerically
)
)
return 1;
return 0;
}
/*______________________________________________________________*/
MEXPORT_AUTOMODE term strip_multiplicities(term t)
/* remove all MULTIPLICITY functors from t */
{ unsigned short n;
int i;
term ans;
if(ATOMIC(t))
return t;
if(FUNCTOR(t) == MULTIPLICITY)
return strip_multiplicities(ARG(0,t));
if(!contains(t,MULTIPLICITY))
return t;
/* MULTIPLICITY functors are never deeply buried so this will
not waste much time. At most they are in an OR of equations
two levels deep. */
n = ARITY(t);
ans = make_term(FUNCTOR(t),n);
for(i=0;i<n;i++)
ARGREP(ans,i,strip_multiplicities(ARG(i,t)));
return ans;
}
/*_______________________________________________________________*/
MEXPORT_AUTOMODE term push_multiplicities(term t)
/* push MULTIPLICITY in through OR and return the result;
drop MULTIPLICITY around 'false' or 'true'
*/
{ unsigned short f = FUNCTOR(t);
unsigned short n;
term ans,mm,u,temp;
int i;
if(f == MULTIPLICITY)
{ u = ARG(0,t);
mm = ARG(1,t);
if(FUNCTOR(u) == OR)
{ n = ARITY(u);
ans = make_term(OR,n);
for(i=0;i<n;i++)
{ temp = make_term(MULTIPLICITY,2);
ARGREP(temp,0,ARG(i,u));
ARGREP(temp,1,mm);
ARGREP(ans,i,temp);
}
return ans;
}
if(equals(u,false) || equals(u,true))
return u;
return t;
}
if(FUNCTOR(t) == OR)
{ n = ARITY(t);
ans = make_term(OR,n);
for(i=0;i<n;i++)
ARGREP(ans,i,push_multiplicities(ARG(i,t)));
return topflatten(ans);
}
return t;
}
/*______________________________________________________________*/
static int substleft(term new, term old, term t, term *ans)
/* substitute new for leftmost occurrence which is not COLOR'ed
(and only this occurrence) of old in t getting ans */
/* (the head of) *ans will not change;
space must be allocated for (the head of) *ans before subst is called.
You pass it the address of a term, and it
uses the head of that term, creating new space for the args. */
/* *ans will be in fresh space (except for the head of *ans)
Return value nonzero means *ans is not equal to t;
return value 0 means *ans is equal to t;
this is used to make sure that subst does preserve the .info fields
of unchanged subterms. */
{ int change=0;
unsigned i,j;
unsigned short k,n = ARITY(t);
unsigned short nargs;
unsigned short f = FUNCTOR(t);
term temp;
if( equals(t,old))
{ copy(new,ans);
return (equals(new,old) ? 0 : 1);
}
if( ATOMIC(t) )
{ copy(t,ans);
return 0;
}
if(COLOR(t))
{ copy(t,ans);
return 0;
}
*ans = make_term(f,n);
if(COLOR(t))
SETCOLOR(*ans,COLOR(t));
if(f == INTEGRAL && n == 4 && IMPROPER(t))
SETIMPROPER(*ans);
for(i=0;i<n;i++)
{ if(!change)
change = substleft(new,old,ARG(i,t),ARGPTR(*ans)+i);
else
ARGREP(*ans,i,ARG(i,t));
}
if(!change)
ans->info = t.info;
/* Now *ans is the unflattened result of the substitution,
but we may still need to flatten the answer */
if(f == '/' && SOME_INFINITESIMAL(t))
copy_infinitesimal_markers(t,ans);
if(f != '+' && f != '*' && f != AND && f != OR)
return change; /* no need to flatten */
/* count how many args the flattened term will have */
for(i=j=0;i<n;i++)
{ if(FUNCTOR(ARG(i,*ans)) == f)
j += ARITY(ARG(i,*ans));
else
++j;
}
if(j==n)
return change; /* no need to flatten */
nargs = (unsigned short) j; /* number of args of flattened term to be created */
temp = *ans;
*ans = make_term(f,nargs);
for(i=j=0;i<n;i++)
{ if(FUNCTOR(ARG(i,temp)) == f)
/* copy the args of ARG(i,temp) into the appropriate args of *ans,
namely j, j+1,...,j+ARITY(ARG(i,temp)) */
{ unsigned short color = COLOR(ARG(i,temp));
for(k=0;k<ARITY(ARG(i,temp));k++)
{ *(ARGPTR(*ans) + j + k)=ARG(k,ARG(i,temp));
if(color)
SETCOLOR(ARG(j+k,*ans),color);
}
j += ARITY(ARG(i,temp));
}
else
{ *(ARGPTR(*ans) + j) = ARG(i,temp);
++j;
}
}
RELEASE(temp); /* allocated by the first call to make_term */
return change;
}
/*________________________________________________________________*/
static int path_to_leftmost(term t, term p, pathlist **ans)
/* return in *ans the path to the leftmost occurrence of p in t,
if there is one, returning 0 for success. If there is no
occurrence of p in t, return 1, in which case *ans is garbage.
All the nodes on the returned path will be created
on the document heap, using cons.
*/
{ int i,err;
pathlist *temp;
unsigned short n;
if(equals(p,t))
{ *ans = NULL;
return 0;
}
if(ATOMIC(t))
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ err = path_to_leftmost(ARG(i,t),p,&temp);
if(!err)
{ *ans = cons(i,FUNCTOR(t),temp);
return 0;
}
}
return 1;
}
/*________________________________________________________________*/
static int fixup(actualop code,term arg,term q,term p,term t, term *next)
/* code has just transformed p in t to q; p is an inequality
and t contains AND. If p is a (left) child of an interval_as_and term,
apply code to the brother of p too, then make the two substitutions
producing next. Return 0 for success.
*/
{ /* The main problem is to get our hands on the parent of p in t */
pathlist *path_to_p, *marker,*last, *penultimate;
term s,u,temp;
char buffer[MAXREASONSTRING];
int err = path_to_leftmost(t,p,&path_to_p);
assert(!err); /* it is assumed that p is a subterm of t */
last = NULL;
temp = t;
/* Now make temp = the parent of p in t */
for(marker=path_to_p;marker && (marker->data >= 0);marker=marker->next)
{ penultimate = last;
if(penultimate)
{ assert(!ATOMIC(temp) && penultimate->data < ARITY(temp));
temp = ARG(penultimate->data,temp);
}
last = marker;
}
if(interval_as_and(temp))
{ /* In case (*code) calls set_pathtail, we have to restore it to the
present value. */
unsigned short savetail[MAXTAIL];
pathcopy(savetail,get_pathtail());
err = (*code)(ARG(1,temp),arg,&s,buffer);
if(err)
return 1;
set_pathtail(savetail);
u = and(q,s);
if(interval_as_and(u))
{ if(legal_interval(temp) && !legal_interval(u))
return 1;
special_subst(u,temp,t,next);
}
else
{ RELEASE(u);
u = and(s,q); /* needed if changesigns was applied */
HIGHLIGHT(u);
if(interval_as_and(u))
special_subst(u,temp,t,next);
else
return 1; /* don't destroy an interval_as_and */
}
}
else
special_subst(q,p,t,next);
return 0;
}
/*_________________________________________________________________*/
static int special_subst(term new, term old, term t, term *ans)
/* This is a copy of 'subst' in speed.c, modified so that
it refuses to substitute into a term with the COLOR bit set
in its info field. It is used only in nextline. Return 0
if something is changed, but in any case *ans is sensible.
*/
{ int i,change=0;
unsigned short j,n = ARITY(t);
unsigned short k,nargs;
unsigned short f = FUNCTOR(t);
term temp;
if( equals(t,old))
{ copy(new,ans);
return (equals(new,old) ? 0 : 1);
}
if(ATOMIC(t) || COLOR(t))
{ copy(t,ans);
return 0;
}
if(f == MULTIPLICITY && equals(old,ARG(0,t)) && equals(new,false))
{ /* don't generate multiplicity(false,n); just generate false. */
*ans = false;
return 0;
}
*ans = make_term(f,n);
if(f == INTEGRAL && n == 4 && IMPROPER(t))
SETIMPROPER(*ans);
if(COLOR(t))
SETCOLOR(*ans,COLOR(t));
for(i=0;i<n;i++)
change += special_subst(new,old,ARG(i,t),ARGPTR(*ans)+i);
if(!change)
ans->info = t.info;
/* Now *ans is the unflattened result of the substitution,
but we may still need to flatten the answer */
if( f == '/' && SOME_INFINITESIMAL(t))
/* t is a fraction with infinitesimal denominator; substitutions
done into t will always preserve the infinitesimal-denominator
property and must be so labelled */
copy_infinitesimal_markers(t,ans);
if(f != '+' && f != '*')
return change; /* no need to flatten */
/* else go on and flatten the answer */
/* count how many args the flattened term will have */
for(i=j=0;i<n;i++)
{ if(FUNCTOR(ARG(i,*ans)) == f)
j += ARITY(ARG(i,*ans));
else
++j;
}
if(j==n)
return change; /* no need to flatten */
nargs = j; /* number of args of flattened term to be created */
temp = *ans;
*ans = make_term(f,nargs);
for(i=j=0;i<n;i++)
{ if(FUNCTOR(ARG(i,temp)) == f)
/* copy the args of ARG(i,temp) into the appropriate args of *ans,
namely j, j+1,...,j+ARITY(ARG(i,temp)) */
{ unsigned short color = COLOR(temp);
if(!color)
color = COLOR(ARG(i,temp));
for(k=0;k<ARITY(ARG(i,temp));k++)
{ *(ARGPTR(*ans) + j + k) = ARG(k,ARG(i,temp));
if(color)
SETCOLOR(ARG(j+k,*ans),color);
}
j += ARITY(ARG(i,temp));
}
else
{ *(ARGPTR(*ans) + j) = ARG(i,temp);
if(COLOR(temp))
SETCOLOR(ARG(j,*ans),COLOR(temp));
++j;
}
}
RELEASE(temp); /* allocated by the first call to make_term */
return change;
}
/*_______________________________________________________________________*/
static term flatten_multiplicities(term t)
/* multiplicity(multiplicity(x,2),3) => multiplicity(x,6) for example,
anywhere within t */
{ unsigned short n;
unsigned short f;
int i,err;
term p,q;
term ans;
if(ATOMIC(t))
return t;
f = FUNCTOR(t);
if(f == MULTIPLICITY && FUNCTOR(ARG(0,t))== MULTIPLICITY)
{ ans = make_term(MULTIPLICITY,2);
ARGREP(ans,0, ARG(0,ARG(0,t)));
p = product(ARG(1,t),ARG(1,ARG(0,t)));
err = value(p,&q);
if(err)
q = p;
ARGREP(ans,1,q);
return ans;
}
n = ARITY(t);
ans = make_term(f,n);
for(i=0;i<n;i++)
ARGREP(ans,i, flatten_multiplicities(ARG(i,t)));
return ans;
}
/*_________________________________________________________________*/
static int subcomputation(int linenumber)
/* return 1 if history[linenumber] was generated by a
specialop, 0 otherwise. So if it returns 0, then
history[linenumber] resulted by simplifying the
original problem, hence modulo the current assumptions it
is a consequence of the original problem if that was
a proposition.
*/
{ controldata d;
get_controldata(&d);
return specialop(zero,access_optable(d.opseq[linenumber].men)[d.opseq[linenumber].choice-1]);
}
/*__________________________________________________________________*/
static term neg_ineq(term t)
/* assuming t is an inequality, return another inequality equivalent
to the negation of t, e.g. if t is 0 < x, return x�0.
There's another static copy of this in prover.c
*/
{ unsigned short f = FUNCTOR(t);
unsigned short g;
int i = 0; /* used to get args switched on <, LE, GE, > , but not on = and NE */
term ans;
switch(f)
{ case '<' : g = LE; break;
case LE : g = '<'; break;
case GE : g = '>'; break;
case '>' : g = GE ; break;
case NE : g = '='; i=1; break;
case '=' : g = NE; i=1; break;
default: assert(0);
}
ans = make_term(g,2);
ARGREP(ans,i,ARG(1,t));
ARGREP(ans,(i ? 0: 1),ARG(0,t));
return ans;
}
/*_____________________________________________________________________*/
MEXPORT_AUTOMODE int inconsistent(term t)
/* return 1 if proposition t contradicts one of the lines
of the computation so far, as when we have a=0 as one
line and try to divide by a, so t comes to this
function as a != 0.
Return 0 if we can't easily see a contradiction.
Is not used when solving inequalities, as it
may permit reducing terms to false when they
contradict the inequality to be derived, thus causing
restrictions which should be kept in the assumptions
to disappear.
*/
{ int currentline = get_currentline();
int problemtype = get_problemtype();
term u;
int i;
unsigned short f = FUNCTOR(t);
if(!SOLVETYPE(problemtype) || !INEQUALITY(f))
return 0;
if(problemtype == INEQUALITIES)
return 0;
if(problemtype == MINMAX)
return 0; /* when not all the initial operations have been used,
you could have x=0 and then later on x=2, x= -1 are
added, and x = 2 is rejected for contradicting x = 0. */
for(i=0;i<=currentline;i++)
{ if(subcomputation(i))
continue;
u = history(i);
if(implies_instantly(u,neg_ineq(t)))
return 1;
}
return 0; /* can't do anything */
}
/*_______________________________________________________________________*/
static int legal_interval(term t)
/* t is an interval_as_and, that is, has the form a <= c <= b.
Return 1 if a and b are econstant, 0 otherwise.
*/
{ term a,b;
unsigned short f,g;
if(FUNCTOR(t) != AND)
return 0;
f = FUNCTOR(ARG(0,t));
g = FUNCTOR(ARG(1,t));
if(f != LE && f != '<')
return 0;
if(g != LE && g != '<')
return 0;
a = ARG(0,ARG(0,t));
b = ARG(1,ARG(1,t));
if(!econstant(a))
return 0;
if(!econstant(b))
return 0;
return 1;
}
/*_________________________________________________________________________*/
MEXPORT_AUTOMODE int pathsub(unsigned short *path, term u, term t, term *next)
/* substitute u for the term at the specified path in t, getting
*next. Return 0 for success. If the specified path isn't a path
in t, return 1.
Subranges are indicated in path by using SUBRANGE in the functor
position, e.g. (+,1,SUBRANGE,2) to indicate a two-summand subrange.
*/
{ term v;
unsigned short n,min,max;
unsigned short f = FUNCTOR(t);
int i,k,err;
if(path[0]==0 || path[1] == 0)
/* If the path was generated by autosimp with dir == UP, and the
focus wasn't atomic, the functor of the focus is at the end of
the path and the following path[2n+1] is zero, so when we
get to the end of the path on a recursive call, we arrive here
with path[0] nonzero and path[1] == 0.
*/
{ *next = u;
return 0;
}
if(path[2] == SUBRANGE)
{ min = (unsigned short) (path[1]-1);
max = (unsigned short) (path[3]-1);
if(path[3] == 0 || max <= min)
return 1; /* assert(0) */
n = ARITY(t);
*next = make_term(f,(unsigned short) (n-(max-min)));
for(i=0;i<n;i++)
ARGREP(*next,i, i < min ? ARG(i,t) : i==min ? u : ARG(i-(max-min),t));
if(FUNCTOR(u) == f)
*next = topflatten(*next);
return 0;
}
if(ATOMIC(t))
return 1;
if(FUNCTOR(t) != path[0])
return 1;
if(path[1] > ARITY(t))
return 1;
k = path[1]-1;
err = pathsub(path+2,u,ARG(k,t),&v);
if(err)
return 1;
n = ARITY(t);
*next = make_term(f,n);
if(f == INTEGRAL && n == 4 && IMPROPER(t))
SETIMPROPER(*next); /* nothing you do to the integrand makes the integral proper */
for(i=0;i<n;i++)
ARGREP(*next,i,i==k ? v : ARG(i,t));
if(f == '/' && SOME_INFINITESIMAL(t))
copy_infinitesimal_markers(t,next);
if(f != '+' && f != '*' && f != AND && f != OR)
return 0; /* no need to flatten */
if(FUNCTOR(u) != f)
return 0;
*next = topflatten(*next);
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists