Sindbad~EG File Manager
/* M. Beeson, for Mathpert */
/* selection of operators to 'do with' a list of selected terms */
/*
2.5.95 original date
3.30.99 last modified
6.17.00 commented out status(difdefint...
3.8.01 added call to diveqn when deciding whether to show diveqn on trig identities
3.10.01 added diveqn at the end
6.18.06 made on_path exported
*/
#define AUTOMODE_DLL
#include <math.h> /* abs */
#include <string.h> /* memset */
#include <assert.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "display1.h"
#include "document.h"
#include "checkarg.h"
#include "ops.h"
#include "trig.h"
#include "calc.h"
#include "bigrect.h"
#include "lterm.h"
#include "selectop.h"
#include "automode.h"
#include "cflags.h"
#include "exec.h" /* erasecolors, finish_exec */
#include "probtype.h"
#include "ops.h"
#include "trig.h"
#include "calc.h"
#include "series.h"
#include "deval.h"
#include "dowith.h"
#include "algaux.h" /* strongnegate */
#include "prover.h" /* interval_as_and */
#include "tdefn.h"
#include "autosimp.h" /* SaveShowStepState */
#include "polynoms.h" /* POLYnomial */
#include "relrates.h" /* used */
#include "pvalaux.h" /* subterm */
static int brothers(pathlist *a, pathlist *b, unsigned short *f);
static int isdenom(pathlist *p);
static int on_factor_path(pathlist *p, unsigned short f);
static int oksub(term t, term arg);
static int okwriteaspoly(term t, term arg);
static int pure_mathematical(term t);
int simple_path(pathlist *p);
static int badpoly(term t, term x);
static int clist(ltermlist *selected);
static int lseminumerical(lterm t);
/*_________________________________________________________________*/
static int lseminumerical(lterm t)
/* like numerical but for lterms instead of terms, return 1
if there is no variable in t */
{ unsigned short i,n;
if(OBJECT(t))
return 1;
if(ISATOM(t) && (FUNCTOR(t) == PI_ATOM || FUNCTOR(t) == 'e'))
return 1;
if(ISATOM(t) && FUNCTOR(t) == 'i' && get_complex())
return 1;
if(ISATOM(t))
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(!lseminumerical(LARG(i,t)))
return 0;
}
return 1;
}
/*_________________________________________________________________*/
MEXPORT_AUTOMODE int l_length(ltermlist *p)
/* return the length of an ltermlist */
{ ltermlist *marker;
int k=0;
for(marker=p;marker;marker=marker->next)
++k;
return k;
}
/*_________________________________________________________________*/
MEXPORT_AUTOMODE ltermlist * lcons(lterm *x, ltermlist *tail)
/* make a new ltermlist with first item x followed by tail */
{ ltermlist *ans = mallocate(sizeof(ltermlist));
if(ans == NULL)
nospace();
ans->data = *x;
ans->next = tail;
return ans;
}
/*__________________________________________________________________*/
MEXPORT_AUTOMODE void dowith(term active, ltermlist *selected, actualop *o, int *nops)
/* Fill array o with operators that
could be applied to active using the selected term as argument, or
using a term made from the selected term or terms as argument.
It is assumed that the path in the selected terms is a path in 'active'.
(active is passed as history[activeline] and the selected terms come from
select_term, so this assumption will be fulfilled.) Return in *nops
the number of operators put into array o.
If there is more than one selected term, they must be one of
following:
(1) all children of the same sum, product, AND, or OR (counting the args
of negative summands as brothers of the other summands, and counting
subranges as children).
(2) as args to muleqn, different denominators of fractions which are
children either of an equation or inequality or of a sum which is a child
of an equation or inequality (or the arg of a negation which is a child
or an equation or ineq or a sum which is a child of an equation or ineq.)
(3) as args to diveqn, either a child of an eqn or ineq, or a factor
of such a child (counting subranges as children)
*/
{ unsigned short f = FUNCTOR(active);
unsigned short n = ARITY(active);
int dir;
int k,eqnnumber,leftcount,rightcount,count,negcount;
short savenvariables,savenextassumption,saveeigen;
unsigned short h;
unsigned nextperm;
int problemtype = get_problemtype();
int intervalflag = 0;
int i=0;
lterm s;
defn d;
term t,arg,ss;
int diveqnflag = 0;
pathlist *p, *q, *qq;
ltermlist *temp, *marker;
int invisiblesub_flag = 0; /* prevent duplicate entry for invisiblesub */
char buffer[DIMREASONBUFFER];
term w,dummy;
POLYnomial pp;
int currenttopic = get_currenttopic();
if(selected == NULL)
{ *nops = i;
return;
}
s = selected->data; /* the (first) selected term */
p = s.path;
if(p==NULL)
{ /* selected term is the whole of active */
*nops = i;
return; /* You can't do anything WITH the whole line */
}
if(ATOMIC(s) && problemtype == SOLVE_EQUATION && selected->next == NULL &&
p->functor != AND && p->functor != OR
/* if this is a system, diveqn will get thrown in again when the
recursive call to dowith is made, and we'll get a duplicate entry
if we don't prevent it here.
*/
)
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
else if(problemtype == SOLVE_EQUATION &&
p->next != NULL && p->next->next != NULL &&
p->next->next->next == NULL &&
p->functor != AND && p->functor != OR
)
{ /* a coefficient of a sum on one side of the equation */
o[i] = diveqn; ++i;
diveqnflag = 1;
}
if(FUNCTOR(s) == CONSTANTOFINTEGRATION &&
!contains(active,INTEGRAL)
// && status(seriesdifint) == KNOWN
)
{ o[i] = eliminatecofi; ++i;
}
if(ISATOM(s) &&
(FUNCTOR(active) != AND || interval_as_and(active)) &&
/* without this, these operations will get put in twice,
because under AND, we call dowith recursively on the args */
FUNCTOR(active) != OR && /* ditto */
selected->next == NULL
)
{ /* if s is a defined variable, throw in unwinddefinition */
int nextdefn = get_nextdefn();
for(k=0;k<nextdefn;++k)
{ d = get_defn(k);
if(contains(d.reverse ? d.right : d.left,FUNCTOR(s)))
{ o[i] = unwinddefinition; ++i;
break;
}
}
if(k==nextdefn)
{ /* not a defined variable */
if(problemtype == LINEAR_EQUATIONS ||
problemtype == LINEAR_EQUATION ||
(problemtype == RELATED_RATES && used(DIFEQN))
)
{ /* are there more variables than equations? */
/* If so show 'Regard variable as constant' */
int m;
term context = history(get_currentline());
int selected_equation = get_selected_equation();
if(selected_equation < 0 && selected_equation <= ARITY(context))
/* in theory the last condition must always be satisfied,
but don't crash if for some reason it's not. */
context = ARG(abs(selected_equation)-1,context);
if(FUNCTOR(context) == AND)
m = ARITY(context);
else if(FUNCTOR(context) == '=')
m = 1;
else
m = -1;
if(m > 0 && get_nvariables() > m && FUNCTOR(context) == AND)
{ o[i] = regardvarasconst; ++i;
}
if(!substforvar(context,abstract(s),&dummy,buffer))
{ o[i] = substforvar; ++i;
}
}
}
if(problemtype == SOLVE_EQUATION && status(reversesub) >= KNOWN)
{ if(
( currenttopic == _cubic_one_root ||
currenttopic == _complex_cubics
) &&
!makepoly(history(get_activeline()),get_eigenvariable(),&pp)
)
{ o[i] = ZERO(ARG(2,pp)) ? viete : eliminatequadraticterm; ++i;
}
if(ISEXISTENTIALVAR(s) && TYPE(s) == INTEGER && currenttopic == _complex_cubics)
{ o[i] = translatevar; ++i;
}
else
{ o[i] = reversesub; ++i;
}
}
}
if(f==AND && FUNCTOR(s)== DIFF && currenttopic == _related_rates && used(DIFEQN))
{ /* if this derivative is the left side of an equation... */
q = p->next;
if(q->functor == '=' && q->data == 0)
{ o[i] = eliminatederivative; ++i;
}
}
if(f == AND && interval_as_and(active))
{ /* an interval is treated much as an equation
or inequality would be. */
if(p == NULL)
/* the whole interval is selected */
{ *nops = i;
return; /* You can't do anything WITH the whole interval */
}
dir = (int) p->data; /* which inequality the selected term is in */
w = ARG(dir,active); /* the inequality */
f = FUNCTOR(w); /* LE or '<' */
p = p->next; /* the path from this inequality */
if(p==NULL)
{ *nops = i;
return; /* You can't do anything with one of the
inequalities either */
}
w = ARG(p->data,w); /* the arg of the inequality containing the selected term */
q = p->next; /* The path from w */
if(q==NULL || /* the whole side was selected */
(FUNCTOR(w) == '+' && q->next == NULL) /* a summand was selected */
)
{ o[i] = f == '<' ? subeqn1 : subeqn2; ++i;
*nops = i;
return; /* all you can do is subtract this term */
}
if(FUNCTOR(w) == '+' && q->next != NULL &&
NEGATIVE(ARG(q->data,w)) && /* a negative summand is selected */
q->next->next == NULL
)
{ o[i] = f == '<' ? addeqn1 : addeqn2; ++i;
*nops = i;
return; /* all you can do is subtract this term */
}
if(FUNCTOR(w) == '/' && q->next == NULL && q->data == 1) /* a denominator was selected */
{ o[i] = mulineq; ++i;
*nops = i;
return; /* all you can do is multiply by this term */
}
/* Now selected is a subterm of one side of one inequality */
/* Execution will pass to the case of an inequality below. */
active = ARG(dir,active); /* the inequality rather than the interval_as_and */
intervalflag = dir + 1;
}
else if(f == AND || f == OR) /* a system of equations */
{ /* Check for the selection of a child (or a subrange) of one of the sides. */
if(p->next && p->next->data < 0)
{ /* user has selected two or more adjacent equations */
*nops = i;
return; /* You can't do anything WITH two adjacent equations */
}
for(eqnnumber = 0; eqnnumber < n; eqnnumber++)
{ /* First check if the whole equation is selected */
count = 0;
for(marker = selected; marker; marker=marker->next)
{ t = abstract(marker->data);
if(marker->data.path->data == eqnnumber)
{ if(f == AND &&
marker->data.path->next == NULL &&
/* whole equation is selected */
get_problemtype() == LINEAR_EQUATIONS
)
{ if(equals(ARG(0,t),ARG(1,t)))
{ o[i] = dropeqn; ++i;
/* This operation doesn't actually need an
arg, but it works on the whole system,
so putting it here enables you to use it
when you select an equation to be dropped.
*/
*nops = i;
/* What else would you want to do
with an identity? */
return;
}
o[i] = addselectedeqn; ++i;
o[i] = subselectedeqn; ++i;
o[i] = mulselectedeqn; ++i;
o[i] = divselectedeqn; ++i;
o[i] = addmulselectedeqn; ++i;
o[i] = submulselectedeqn; ++i;
o[i] = swapselectedeqn; ++i;
o[i] = solveselectedeqn; ++i;
if(get_selected_equation()==0)
{ o[i] = selecteqn; ++i;
}
}
else
++count;
}
}
if(!count)
continue; /* no subterms of this equation are selected */
/* Make temp into a list of all selected terms that are
subterms of this same equation */
temp = NULL;
for(marker=selected; marker; marker=marker->next)
{ if(marker->data.path->data != eqnnumber)
continue;
temp = lcons(&marker->data,temp);
if(marker->data.path->functor==MATRIX)
/* a system of equations bblocked as a matrix with
one equation per row. */
temp->data.path = temp->data.path->next->next;
/* remove the MATRIX and the VECTOR from the path
so the path refers to the equation */
else
temp->data.path = temp->data.path->next;
/* so in temp, the paths refer to the equation,
not to the system of equations */
}
/* Now select all the operators that could use the
selected subterm(s) as args to work on the equation */
if(temp != NULL)
{ dowith(ARG(eqnnumber,active),temp,o+i,&k);
i += k;
}
}
*nops = i;
return;
}
if(f == '=' && contains(active,MATRIX) && /* a matrix equation */
p->next && p->next->next /* and the selected term is strictly within one side */
)
{ /* If the user has selected a row of a matrix, and the
matrix in question is one side of the equation or a child
of one side (and the side is a product), then the
operations addrowsselected etc. from the linear_equations_by_selection
menu are applicable. */
dir = p->data; /* 0 or 1 to tell left or right side of the equation */
assert(dir == 0 || dir == 1);
q = p->next; /* now q is the path from the side */
if(q->functor == '*')
q = q->next; /* now p is the path from a factor of one side */
if( q->functor == MATRIX && /* matrix fills one side of the equation */
q->next == NULL /* one row is selected */
)
{ o[i] = addselectedrow; ++i;
o[i] = subselectedrow; ++i;
o[i] = divselectedrow; ++i;
o[i] = mulselectedrow; ++i;
o[i] = addmulselectedrow; ++i;
o[i] = submulselectedrow; ++i;
o[i] = swapselectedrow; ++i;
o[i] = dropzerorow; ++i;
o[i] = dropduplicaterow; ++i;
}
*nops = i;
return;
}
if(f == MULTIPLICITY)
{ /* make a copy of the ltermlist selected, at least at toplevel
and adjust the paths to be relative to the equation, not the
MULTIPLICITY term */
ltermlist *selected2 = NULL;
lterm *uu;
for(marker = selected; marker; marker=marker->next)
{ uu = (lterm *) mallocate(sizeof(lterm));
if(!uu)
{ nospace();
return;
}
*uu = marker->data;
uu->path = uu->path->next; /* adjust the path as specified above */
selected2 = lcons(uu,selected2);
}
dowith(ARG(0,active),selected2,o,nops);
return;
}
savenvariables = get_nvariables();
saveeigen = get_eigenindex();
savenextassumption = get_nextassumption();
if(INEQUALITY(f)) /* working on an equation or inequality */
{ if(get_problemtype() == INTEGRATION && status(intsub) == LEARNING)
{ /* first lesson in integration by substitution, equations
for the substitution arise, but we're not solving
them. Get out of here. */
*nops = i;
return;
}
dir = p->data; /* 0 or 1 to tell left or right side */
if(dir != 0 && dir != 1)
assert(0);
p = p->next; /* Now p is the path from the side */
if(p==NULL && /* whole side was selected */
selected->next && /* and another term also was selected */
selected->next->data.path->next == NULL
/* and the other term is the other side of the same equation */
)
{ /* Both sides of the equation were separately selected */
/* This can only mean user wants to swap the sides */
switch(f)
{ case '=':
o[i] = swapeqns; ++i;
break;
case '>':
o[i] = reversegreaterthan; ++i;
break;
case '<':
o[i] = reverselessthan; ++i;
break;
case LE:
o[i] = reversele; ++i;
break;
case GE:
o[i] = reversege; ++i;
break;
}
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
/* Now it's not the case that both sides are selected */
if(p==NULL && selected->next==NULL)
{ /* exactly one side is selected */
/* This could mean any number of things */
if(!cancelterm(active,abstract(selected->data),&dummy,buffer))
/* don't show cancelterm unless it's going to work */
/* If it works, it works on equations or inequalities */
{ o[i] = cancelterm; ++i;
}
switch(f)
{ case '=':
o[i] = subeqn; ++i;
if(clist(selected) && !diveqnflag)
{ if(problemtype != TRIG_IDENTITY)
{ o[i] = diveqn; ++i;
}
else if(selected->next == NULL &&
!diveqn(active,abstract(selected->data),&dummy, buffer)
)
/* when verifying identities, only show diveqn
if it's legal to cancel that term */
{ o[i] = diveqn; ++i;
}
}
/* but not muleqn or addeqn */
break;
case '<':
case '>':
o[i] = subeqn1; ++i;
o[i] = divineq; ++i;
break;
case LE:
case GE:
o[i] = subeqn2; ++i;
o[i] = divineq; ++i;
/* but not muleqn2 */
break;
}
ss = abstract(selected->data);
if(oksub(active,ss))
{ o[i] = makesubstitution; ++i;
if(test_invisiblesub(ss))
{ o[i] = invisiblesub; ++i;
}
}
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
/* Now check if one side of the equation is among the selected terms */
for(marker=selected;marker; marker=marker->next)
{ qq = marker->data.path;
if(intervalflag)
qq = qq->next;
if(qq->next ==NULL)
break;
}
if(marker)
{ /* Yes, one whole side has been selected and also some
other term. This can only mean transfereqn. Check
that the other side has functor '+' and the selected
other terms are children */
dir = qq->data;
if(FUNCTOR(ARG(dir,active)) != '+')
{ *nops = i; /* useless selection */
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
for(marker = selected; marker ; marker=marker->next)
{ p = marker->data.path;
if(intervalflag)
p = p->next;
if(p->next == NULL)
continue; /* this is the whole side */
p = p->next;
if(p->next == NULL)
continue; /* OK, this is a child of the other side */
if(p->next->next && p->next->data <0)
continue; /* OK, this is a subrange */
if(p->next->next && FUNCTOR(ARG(p->data,ARG(dir,active))) == '-')
continue; /* OK, this is a negative child */
*nops =i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return; /* too deep */
}
for(marker = selected; marker ; marker=marker->next)
{ p = marker->data.path;
if(intervalflag)
p = p->next;
if(p->next == NULL)
continue; /* This is the whole side */
p = p->next;
if(p->next == NULL)
continue; /* OK, this is a child of the other side */
if(p->next->data < 0)
continue; /* a subrange */
if(p->next->next == NULL &&
FUNCTOR(ARG(p->data,ARG(dir,active))) != '-'
)
continue; /* a negation */
*nops =i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return; /* too deep */
}
switch(f)
{ case '=':
o[i] = transfer1; ++i;
break;
case '<':
case '>':
o[i] = transferstrictineq; ++i;
o[i] = transferineq; ++i;
}
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
/* Now neither side of the equation is among the selected terms */
/* Check for the case in which all selected terms are denominators */
for(marker=selected;marker;marker=marker->next)
{ p = marker->data.path;
if(intervalflag)
p = p->next;
if(!isdenom(p))
break;
}
if(!marker)
{ switch(f)
{ case '=':
if(clist(selected))
{ /* don't show muleqn if there's only one selected term and it's
not in a denominator. */
if(selected->next == NULL)
{ pathlist *marker = selected->data.path;
while(marker)
{ if(marker->data == 1 && marker->functor == '/')
break;
marker=marker->next;
}
if(marker != NULL)
{ o[i] = muleqn; ++i;
}
}
if(problemtype != TRIG_IDENTITY && !diveqnflag)
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
else if(selected->next == NULL && !diveqnflag &&
!diveqn(active,abstract(selected->data),&dummy, buffer)
)
/* when verifying identities, only show diveqn
if it's legal to cancel that term */
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
}
break;
case '<':
case '>':
case LE:
case GE:
o[i] = mulineq; ++i;
o[i] = mulineqsq; ++i;
break;
}
if(!invisiblesub_flag)
ss = abstract(selected->data);
if(!invisiblesub_flag && oksub(active,ss))
{ o[i] = makesubstitution; ++i;
if(test_invisiblesub(ss))
{ o[i] = invisiblesub; ++i;
}
}
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
if(!invisiblesub_flag)
ss = abstract(selected->data);
if(!invisiblesub_flag &&
selected->next == NULL &&
oksub(active,ss)
)
{ /* only one term selected, and not an equation,
inequality, or AND or OR; and no limits,
integrals, or derivatives in the line at hand. */
o[i] = makesubstitution; ++i;
if(test_invisiblesub(ss))
{ o[i] = invisiblesub; ++i;
}
#if 0
/* Put this unused assignment back if another test for
invisiblesub_flag is added below */
else
invisiblesub_flag = -1;
#endif
}
/* Check for the case in which only immediate children of the sides
have been selected */
leftcount = rightcount = negcount = 0;
for(marker=selected; marker; marker = marker->next)
{ p = marker->data.path;
if(intervalflag)
p = p->next;
dir = p->data;
w = ARG(dir,active); /* the side of the equation */
h = FUNCTOR(w);
p = p->next;
if(h == '-' && p->data == -1 && clist(selected))
/* example, -5 selected in -5x */
{ if(!diveqnflag)
{ o[i] = f == '=' ? diveqn : divineq; ++i;
diveqnflag = 1;
}
break;
}
if(h == '-' && p->next == NULL)
{ switch(f)
{ case '=' :
o[i] = addeqn; ++i;
if(problemtype != TRIG_IDENTITY && !diveqnflag)
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
else if(selected->next == NULL && !diveqnflag &&
!diveqn(active,abstract(selected->data),&dummy, buffer)
)
/* when verifying identities, only show diveqn
if it's legal to cancel that term */
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
break;
case '<':
case '>':
o[i] = addeqn1; ++i;
break;
case GE:
case LE:
o[i] = addeqn2; ++i;
break;
}
}
if(h == '*' && p->next == NULL && f == '=' && !diveqnflag)
{ if(problemtype != TRIG_IDENTITY )
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
else if(
!diveqn(active,abstract(selected->data),&dummy, buffer)
)
/* If it will work to divide by the first term of the product
that's enough to show the operator */
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
}
if(p->data < 0 || p->data >= ARITY(w))
assert(0);
if(p->next != NULL)
{ /* still could be ok if it's a negation or a subrange */
if(p->next->data >= 0) /* not a subrange */
{ w = ARG(p->data,w);
if(FUNCTOR(w) != '-' || p->next->next != NULL)
break;
++negcount;
}
if(dir)
rightcount += p->next->data < 0 ? -p->next->data - p->data : 1;
else
leftcount += p->next->data < 0 ? -p->next->data - p->data : 1;
}
else
{ if(dir)
++rightcount;
else
++leftcount;
}
}
if(marker) /* Some deeper subterm selected */
{ if(f == '=' && !diveqnflag)
{ if(problemtype != TRIG_IDENTITY)
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
else if( !diveqn(active,abstract(selected->data),&dummy, buffer))
/* when verifying identities, only show diveqn
if it's legal to cancel that term; if a list of terms
has been selected, here we test only if it's legal to
divide by the first term; that's enough to justify showing
the operation.
*/
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
}
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
/* Now we've selected leftcount children on the left, and
rightcount children on the right (counting subranges as 1) */
k = rightcount + leftcount; /* total number of selected children */
switch(f)
{ case '=' :
if(negcount == k && (h == '+' || h == f))
/* throw in addeqn if all the selected terms were args of negations */
{ o[i] = addeqn; ++i;
}
else if(negcount == 0 && h == '+')
{ o[i] = subeqn; ++i;
}
else if(h == '+')
{ o[i] = addeqn; ++i;
o[i] = subeqn; ++i;
}
else if(( h == '*' || h == '/' ) && clist(selected))
{ if(!diveqnflag)
{ if(problemtype != TRIG_IDENTITY)
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
else if(selected->next == NULL &&
!diveqn(active,abstract(selected->data),&dummy, buffer)
)
{ o[i] = diveqn; ++i;
diveqnflag = 1;
}
}
if(FUNCTOR(s) == SQRT)
{ o[i] = muleqn; ++i;
}
}
else if(h == '/' && dir == 1 && clist(selected))
{ o[i] = muleqn; ++i;
}
if(f == '=')
{ /* Now, what about cancelterm and cancelfactor?
Show them if they will work. */
int err;
term q;
char localbuf[DIMREASONBUFFER];
if(k==1)
arg = abstract(selected->data);
else
SETFUNCTOR(arg,ILLEGAL,0);
if(FUNCTOR(ARG(0,active)) == '+' || FUNCTOR(ARG(1,active)) == '+')
{ err = cancelterm(active,arg,&q,localbuf);
if(!err)
{ o[i] = cancelterm; ++i;
}
}
if(FUNCTOR(ARG(0,active)) == '*' || FUNCTOR(ARG(1,active)) == '*')
{ err = cancelfactor(active,arg,&q,localbuf);
if(!err)
{ o[i] = cancelfactor; ++i;
}
}
}
break;
case '<' :
case '>' :
if(negcount == k && (h == '+' || h == f))
/* use addeqn if all the selected terms were args of negations */
/* The h==f clause applies when one side of the inequality is
a negation and you select the arg of the negation */
{ o[i] = addeqn1; ++i;
}
else if(negcount == 0 && h == '+' )
{ o[i] = subeqn1; ++i;
}
else if(h == '+')
{ o[i] = addeqn1; ++i;
o[i] = subeqn1; ++i;
}
else if(h == '*' || h == '/' )
{ o[i] = divineq; ++i;
if(FUNCTOR(s) == SQRT)
{ o[i] = mulineq; ++i; /* (x+3) sqrt f(x) < 0 for example */
}
}
else if(h == '/' && dir == 1)
{ o[i] = mulineq; ++i;
}
break;
case LE :
case GE :
if(negcount == k && (h == '+' || h == f))
/* use addeqn if all the selected terms were args of negations */
{ o[i] = addeqn2; ++i;
}
else if(negcount == 0 && h == '+')
{ o[i] = subeqn2; ++i;
}
else if(h == '+')
{ o[i] = addeqn2; ++i;
o[i] = subeqn2; ++i;
}
else if(h == '*')
{ o[i] = divineq; ++i;
}
else if(h == '/' && dir == 1)
{ o[i] = mulineq; ++i;
}
break;
}
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
if(on_factor_path(p,INTEGRAL) && !OBJECT(s) &&
status(integratebyparts) >= LEARNING
)
{ o[i] = integratebyparts; ++i;
}
if(f == '+' &&
problemtype == INTEGRATION &&
FUNCTOR(selected->data) == ATAN &&
FUNCTOR(LARG(0,selected->data)) == TAN &&
/* and the path only contains -, *, /,...*/
p != NULL && simple_path(p->next)
)
{ o[i] = atantan2; ++i;
}
if(on_path(p,INTEGRAL) && !OBJECT(s))
{ /* The selected term is inside an integral and not a number */
if(status(intsub) == LEARNING && !ISATOM(s))
{ o[i]= choosesubstitution; ++i;
}
if(!ISATOM(s))
{ o[i] = intsub; ++i;
}
}
else if(on_path(p,LIMIT) && !ATOMIC(s) && !lseminumerical(s))
/* Because of the 'else', if s occurs inside an integral inside a limit,
as in working an improper integral, you can't use it
for changelimitvariable
*/
{ o[i] = changelimitvariable; ++i;
}
else if(!invisiblesub_flag && oksub(active,ss=abstract(selected->data)))
{ o[i] = makesubstitution; ++i;
if(test_invisiblesub(ss))
{ o[i] = invisiblesub; ++i;
}
/* else
invisiblesub_flag = -1;
(never used, so commented out; put back if
another test for invisible_flag is added below.) */
}
nextperm = get_nextperm();
if(okwriteaspoly(active,abstract(selected->data)))
{ o[i] = writeaspoly; ++i;
}
reset_nextperm(nextperm);
*nops = i;
set_nvariables(savenvariables);
set_eigenvariable(saveeigen);
set_nextassumption(savenextassumption);
return;
}
/*__________________________________________________________________*/
MEXPORT_AUTOMODE int make_arg(ltermlist *selected, term *arg)
/* selected is a (nonempty) list of terms selected
by the user. The user has chosen an operator presented
by 'dowith'. This function must produce an 'arg' to be
used by the chosen operator. It must produce a legal arg
every time an operator is listed by dowith. [Note: the
use of abstract results in *arg using fresh space--this is
essential to avoid common subterms in *arg and the current line
to which the operator with this arg will be applied.]
Return 0 for success, 1 for failure. The
return value 0 is checked by an assertion, so if dowith
lists an operator, make_arg better succeed!
Specifically:
(1) If there's just one selected term, that's the arg, finished.
(2) If the selected terms are all brothers, make the term
with the parent functor having all the selected terms as children.
This is slightly complicated by counting children of negations
occurring as args of sums as brothers of the other summands, and
by the fact that a selected term can be a subrange term.
(3) If the selected terms are all denominators of fractions and the
active line is an equation, then the arg is the product of those
denominators (and the operation better be multiplyeqn)
*/
{ ltermlist *marker;
unsigned short f,m;
int k;
term u;
pathlist *p;
assert(selected); /* this better not be called on an empty list */
if(!selected->next)
{ /* There is only one selected term. It has to be the arg */
*arg = abstract(selected->data);
return 0;
}
/* Now there is more than one selected term */
/* Check for the case they are all brothers */
p = selected->data.path;
m = 1;
for(marker=selected->next; marker; marker=marker->next)
{ if(!brothers(marker->data.path, p,&f))
break;
++m;
}
if(marker==NULL)
{ /* they WERE all brothers */
*arg = make_term(f,m); /* there were m selected terms */
for(k=0,marker=selected;marker;marker=marker->next,++k)
{ /* check for a negation or subrange */
u = abstract(marker->data);
ARGREP(*arg,m-1-k,u); /* the selected terms are in reverse order,
so using m-1-k instead of k, we get
them to come out in the order they were
selected. */
}
if(k != m)
assert(0);
return 0;
}
/* Now check for the case in which all selected terms are denominators */
m = 0;
for(marker=selected;marker;marker=marker->next)
{ if(!isdenom(marker->data.path))
break;
++m;
}
if(!marker)
{ /* they all were denominators, so make *arg the product of all
selected terms */
*arg = make_term('*', m);
for(marker=selected,k=0;marker;marker=marker->next,k++)
{ u = abstract(marker->data);
ARGREP(*arg,m-1-k,u);
}
if(k!=m)
assert(0);
return 0;
}
return 1;
}
/*______________________________________________________________________*/
static int brothers(pathlist *a, pathlist *b, unsigned short *f)
/* return 1 if a and b are 'brother' paths, i.e. either
(1) have the same length and differ in the last place only, or
(2) one or both end in a negative 'data' and after truncating
that entry they are brothers, or
(2) the 'functor' field in the last entry is '-', the preceding
functor field is '+', and truncating the last entry they are brothers.
(This allows negations as subterms of '+' to be brothers with
the other summands)
*f is instantiated to + or * if 1 is returned.
*/
{ pathlist *marker1, *marker2;
for(marker1=a,marker2=b;marker1 && marker2; marker1=marker1->next,marker2=marker2->next)
{ if(marker1->data != marker2->data)
{ if(marker1->next == NULL && marker2->next == NULL)
{ *f = marker1->functor;
return 1; /* literal brothers */
}
*f = '+';
if(marker2->next == NULL)
{ if(marker1->functor == '+' &&
marker1->next->functor == '-' &&
marker1->next->next == NULL
)
return 1; /* a is an arg of a negation, b is not */
if(marker1->next->data < 0) /* a is a subrange */
return 1;
return 0;
}
if(marker1->next == NULL)
{ if(marker2->functor == '+' &&
marker2->next->functor == '-' &&
marker2->next->next == NULL
)
return 1; /* b is an arg of a negation, a is not */
if(marker2->next->data < 0) /* b is a subrange */
return 1;
return 0;
}
if( marker1->functor == '+' &&
marker1->next->next == NULL && marker2->next->next == NULL &&
marker1->next->functor == '-' &&
marker2->next->functor == '-'
)
return 1; /* The 2 and 3 in a - 2 + b - 3 for example */
return 0;
}
}
assert(0); /* it must return from within the loop */
return 0;
}
/*______________________________________________________________________*/
static int isdenom(pathlist *p)
/* return 1 if the last item in p has '/' in the functor field */
{ pathlist *marker,*last;
for(marker=p;marker;marker=marker->next)
{ last = marker;
}
return last->functor == '/' ? 1 : 0;
}
/*_____________________________________________________________________*/
MEXPORT_AUTOMODE int on_path(pathlist *p, unsigned short f)
/* return 1 if functor f occurs on pathlist p, 0 if not */
{ pathlist *marker;
for(marker=p;marker;marker=marker->next)
{ if(marker->functor == f)
return 1;
}
return 0;
}
/*_____________________________________________________________________*/
static int on_factor_path(pathlist *p, unsigned short f)
/* return 1 if functor f occurs on pathlist p, followed only by
'*', '/' where the path goes into the numerator, '^' where the
path goes into the base. */
{ pathlist *marker;
int flag = 0;
for(marker=p;marker;marker=marker->next)
{ if(flag)
{ switch(marker->functor)
{ case '*' :
continue;
case '/' :
if(marker->data == 0) /* going into numerator */
continue;
return 0;
case '^' :
if(marker->data == 0) /* going into base */
continue;
return 0;
case INTEGRAL:
if(marker->data == 0) /* going into integrand */
continue;
return 0;
default:
return 0;
}
}
if(marker->functor == f)
flag = 1;
}
return flag;
}
/*___________________________________________________________________*/
static int oksub(term t, term arg)
/* will it work to write t as a function of arg? Return 1
if it will, 0 if it won't, and don't leave any new variables,
assumptions, or let_defns around, and don't use up any
memory, and don't change the eigenvariable.
*/
{ char buffer[DIMREASONBUFFER];
term u;
unsigned nextperm;
int err,nvariables,nextdefn,saveit;
unsigned short nextassumption;
unsigned short f = FUNCTOR(t);
int savenparameters = get_nparameters();
void *savenode;
if(ATOMIC(arg))
return 0;
if(contains(t,LIMIT) || contains(t,DIFF) || contains(t,INTEGRAL))
return 0;
if(contains(arg,AND) || contains(arg,OR))
return 0;
if(INEQUALITY(f) &&
(
(equals(ARG(0,t),arg) && ZERO(ARG(1,t))) ||
(equals(ARG(1,t),arg) && ZERO(ARG(0,t)))
)
)
return 0;
err = pure_mathematical(arg);
if(err)
return 0; /* without further ado, arg contains
'<' or '=' or LE for example */
saveit = get_eigenindex();
savenode = heapmax();
nvariables = get_nvariables();
nextdefn = get_nextdefn();
nextassumption = get_nextassumption();
nextperm = get_nextperm();
SaveShowStepState();
err = makesubstitution(t,arg,&u,buffer);
RestoreShowStepState();
reset_nextperm(nextperm); /* recover space used by makesubstitution to
store 'u=arg' */
set_nvariables(nvariables);
set_eigenvariable(saveit);
set_nextdefn(nextdefn);
set_nextassumption(nextassumption);
set_nparameters(savenparameters);
reset_heap(savenode);
return err ? 0 : 1;
}
/*___________________________________________________________________*/
static int okwriteaspoly(term t, term arg)
/* will it work to write t as a polynomial in arg? Return 1
if it will, 0 if it won't, and don't leave any new variables,
assumptions, or let_defns around, and don't use up any
memory, and don't change the eigenvariable. And don't
actually TRY writeaspoly on extremely complicated terms because
it runs out of memory!
*/
{ char buffer[DIMREASONBUFFER];
term u;
unsigned nextperm;
int err,nvariables,nextdefn,saveit;
unsigned short nextassumption;
unsigned short f = FUNCTOR(t);
int savenparameters = get_nparameters();
void *savenode;
if(ATOMIC(arg))
return 0;
if(contains(t,LIMIT) || contains(t,DIFF) || contains(t,INTEGRAL))
return 0;
if(INEQUALITY(f) &&
(
(equals(ARG(0,t),arg) && ZERO(ARG(1,t))) ||
(equals(ARG(1,t),arg) && ZERO(ARG(0,t)))
)
)
return 0;
err = pure_mathematical(arg);
if(err)
return 0; /* without further ado, arg contains
'<' or '=' or LE for example */
if(badpoly(t,arg))
return 0; /* This saves a lot of out-of-memory trouble when
this is called on large rational expressions. */
saveit = get_eigenindex();
savenode = heapmax();
nvariables = get_nvariables();
nextdefn = get_nextdefn();
nextassumption = get_nextassumption();
nextperm = get_nextperm();
err = writeaspoly(t,arg,&u,buffer);
reset_nextperm(nextperm); /* recover space used by makesubstitution to
store 'u=arg' */
set_nvariables(nvariables);
set_eigenvariable(saveit);
set_nextdefn(nextdefn);
set_nextassumption(nextassumption);
set_nparameters(savenparameters);
reset_heap(savenode);
return err ? 0 : 1;
}
/*______________________________________________________________________*/
static int pure_mathematical(term t)
/* similar to 'mathematical' in chkinput.c but
disallows not only logical functors and inequalities,
but also IF. Returns zero for success,
nonzero for non-pure_mathematical terms.
*/
/* if t contains a nonnumerical functor (including LIMIT--but INTEGRAL
and DIFF are ok) , return an
index of an error message, else return 0.
*/
{ unsigned short f,n;
int i,err;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
switch(f)
{ case '<' : /* deliberate fall-through */
case '>' :
case LE :
case GE :
case NE : return 35;
case '=' : return 39;
case VECTOR :
case MATRIX : return 36;
case AND: return 37;
case OR:
case NOT:
case ARROW:
case SEQ:
case EVEN1:
case ODD1: return 38;
case LIMIT: return 78;
}
if(f == IF)
return 1; /* it only matters that it's nonzero */
for(i=0;i<n;i++)
{ err = pure_mathematical(ARG(i,t));
if(err)
return err;
}
return 0;
}
/*______________________________________________________________________*/
int simple_path(pathlist *p)
/* return 1 if p only contains -, *, /, and at / goes into the
numerator */
{ pathlist *marker;
unsigned short f;
for(marker = p; marker; marker=marker->next)
{ f = marker->functor;
if(f != '-' && f != '*' && f != '/')
return 0;
if(f == '/' && p->data != 0)
return 0;
}
return 1;
}
/*_________________________________________________________________*/
static int badpoly(term t, term x)
/* x isn't necessarily a variable. Return 1 if we don't even want
to try writing t as a polynomial in x. If, for example, t contains
denominators involving x, or transcendental functions of x. Or,
if x is a complicated term.
*/
{ unsigned short n,f;
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(x) == '*' && ARITY(x) > 2)
return 1;
if(FUNCTOR(x) == '*' && (!ATOMIC(ARG(0,x)) || !ATOMIC(ARG(1,x))))
return 1;
if(FUNCTOR(x) == '+' && ARITY(x) > 2)
return 1;
if(FRACTION(t))
{ if(ISATOM(x) && contains(ARG(1,t),FUNCTOR(x)))
return 1;
if(equals(x,ARG(1,t)) || subterm(x,ARG(1,t)))
return 1;
return 0;
}
n = ARITY(t);
f = FUNCTOR(t);
if(n == 1 && f != '-' &&
((ISATOM(x) && contains(t,FUNCTOR(x))) || (!ISATOM(x) && subterm(x,t)))
)
return 1;
for(i=0;i<n;i++)
{ if(badpoly(ARG(i,t),x))
return 1;
}
return 0;
}
/*________________________________________________________________*/
static int clist(ltermlist *selected)
/* return 1 if either the problemtype is not LINEAR_EQUATIONS,
or every lterm in the list 'selected' is constant. Return 0
otherwise. */
{ ltermlist *marker;
if(get_problemtype() != LINEAR_EQUATIONS)
return 1;
for(marker = selected; marker; marker = marker->next)
{ if(!constant(abstract(marker->data)))
return 0;
}
return 1;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists