Sindbad~EG File Manager
/* MathXpert's code for definitions and assumptions */
/* M. Beeson
Original date 1.4.91
modified 3.30.99
3.30.00 modified unwinddefinition to cope with MULTIPLICITY terms
4.23.13 modified line 486 in make_substitution
9.26.14 changed static truncate() to truncate3() to avoid an error message
8.27.17 added saveeigen in makedefinition
10.22.23 corrected comma to semicolon line 685
*/
#include <string.h>
#include <assert.h>
#include "globals.h"
#include "ops.h"
#include "pvalaux.h"
#include "autosub.h"
#include "prover.h" /* contains_bound */
#include "symbols.h"
#include "mathmode.h" /* get_substitutionflag */
#include "maxsub.h" /* maximal_sub */
#include "factor.h" /* factor */
#include "solvelin.h" /* solve_linear_ineq_for */
#include "probtype.h"
#include "psubst.h"
#include "errbuf.h"
#include "order.h" /* common_variables */
#include "ssolve.h" /* solved */
#include "autosimp.h" /* SetShowStepArg */
#include "eqn.h" /* econstant */
#include "islinear.h" /* is_linear */
#include "polynoms.h" /* makepoly */
#include "dispfunc.h" /* atom_string */
#include "dcomplex.h" /* ceval needs this */
#include "ceval.h" /* complexnumerical */
#include "mpmem.h" /* permcopy */
#include "mstring.h" /* mstring */
#include "exec.h" /* erasecolors */
static int atomsin_free(term, term **);
static void vvaux2(term, term **, int *, int *);
static int tc_aux(term, term);
static term replace_negexps(term, term);
static void concoct_reason(term arg, char *reason);
static void truncate3(char *buffer, char *reason);
static int unviete(term t, term arg, term *next, char *reason);
static int almost_equals(term a, term b);
static int too_simple_aux(term t, term u, term arg);
static term getVietaVariable(void);
/*______________________________________________________________________*/
int autosubstitution(term t, term arg, term *next, char *reason)
/* computer makes substitution */
{ SETFUNCTOR(arg,ILLEGAL,0);
return makesubstitution(t,arg,next,reason);
}
/*______________________________________________________________________*/
int makesubstitution2(term t, term arg, term *next, char *reason)
/* write it as a function of ? and ? */
/* Used only in term selection or menu mode */
{ if(FUNCTOR(arg) != AND || ARITY(arg) != 2)
return 1;
return makesubstitution(t,arg,next,reason);
}
/*______________________________________________________________________*/
int reversesub(term t, term arg, term *next, char *reason)
/* Make a substitution x = f(u) where x is an existing variable
and u is a new variable (or variables).
Arg is passed as the equation x = f(u).
Checkarg ensures that the left side is an existing variable.
The right side can be arbitrary.
t must be an equation; this can't be used on inequalities or
when simplifying.
*/
{ term x,v,vperm;
term *atomlist;
int err,i,nvars;
term *varlist;
varinf *varinfo;
char buf[128];
int savenvariables;
if(FUNCTOR(t) != '=' || FUNCTOR(arg) != '=')
return 1;
x = ARG(0,arg);
v = ARG(1,arg);
if(!ISATOM(x))
return 1;
savenvariables = get_nvariables();
nvars = variablesin(ARG(1,arg),&atomlist);
vaux(v); /* add new variables in v to varlist */
if(ISEXISTENTIALVAR(x))
{ /* make all new variables existential too */
nvars = get_nvariables();
varlist = get_varlist();
varinfo = get_varinfo();
for(i=savenvariables; i<nvars; i++)
{ SETEXISTENTIALVAR(varlist[i]);
varinfo[i].scope = EXISTENTIAL;
}
}
HIGHLIGHT(v);
permcopy(v,&vperm);
reverse_let(x,vperm);
psubst(v,x,t,next);
/* You might lose solutions if the expression x = f(u) is not
invertible */
if(!is_linear_in(ARG(1,arg),atomlist[0]))
{ commentbuf(0,english(1581));
/* !Solutions may be lost if the substitution does not take all possible values */
set_nvariables(savenvariables);
return 1;
}
strcpy(reason, atom_string(x));
strcat(reason, " = ");
err = mstring(v,buf);
if(err || strlen(buf) > 18)
{ strcat(reason,"f(");
for(i=0;i<nvars;i++)
{ strcat(reason,atom_string(atomlist[i]));
if(i < nvars-1)
strcat(reason, ",");
}
strcat(reason,")");
free2(atomlist);
return 0;
}
strcat(reason,buf);
return 0;
}
/*______________________________________________________________________*/
int makesubstitution(term t, term arg, term *next, char *reason)
/* This operator can be called from menu mode under 'user makes a
substitution', or under 'computer makes a substitution', or from
auto mode.
When called from menu mode under 'user makes a substitution':
arg is a term left=right (or AND of such equations)
which the user wants entered as a let_defn. checkarg has already determined
that for each such equation, left is a new atom, i.e. isn't in the
current expression and the current expression doesn't depend on it .
If arg is not ILLEGAL and is not an '=' or an AND, then this
operator constructs a new variable for the user; this is needed
to use the Term Selection Interface
*/
/* We have to eliminate at least one variable by writing the current
expression as a function of the 'right' terms. Note that this must
be done simultaneously, e.g. x=r cos t , y=r sin t, on the expression
r cos t + r sin t; doing it one at a time won't eliminate anything. */
/* When (in menu mode) user chooses 'computer makes substitution',
this function is called with arg an ILLEGAL term; and of course when
it is called in auto mode the same will be true. In auto mode, however,
this is called only when substitutionflag==VISIBLESUBS. */
/* You must not be able to eliminate a bound variable with this operation,
e.g. substituting u = n+1 in an indexed sum with bound variable n. Originally
(before psubst was used) this was automatic, since you can't substitute
u for a compound term in an atomic term; but you CAN pubst it, and get u-1
in the position of the bound variable, so this must be ruled out by
fiat. */
/* Last but not least, we have to set eigenvariable to the new variable,
provided ARG(1,arg) contains the present eigenvariable.
'let' used to reset the eigenvariable, and the defn structure still keeps
track of 'oldeigen', and eigenvariable is restored by unwinddefinition, but
invisiblesub does NOT want eigenvariable reset, so we have to do it here. */
{ unsigned short f = FUNCTOR(arg);
unsigned short g;
int i,nn,mm,rval;
int saveit = get_nvariables();
short savenextassumption = get_nextassumption();
int nextdefn;
int problemtype;
term *atomlist1, *atomlist2;
term temp,temp2,l,r,u,v,w,mid,x,q;
unsigned short n = ARITY(arg);
int err;
int saveeigen;
int flag=0; /* set when some variable is eliminated (when arg is an AND) */
if(f == ILLEGAL)
/* f == ILLEGAL in menumode when user has chosen
'computer makes a substitution' */
{ void *startnode;
term leftcopy,rightcopy;
startnode = heapmax();
err = autosub(t,&arg,&mid);
if(!err)
{ /* reject previously attempted substitutions
and substitutions that will be immediately unwound */
nextdefn = get_nextdefn();
g = FUNCTOR(t);
for(i=0;i<nextdefn;i++)
{ temp = get_defn(i).right;
if(equals(temp,ARG(1,arg)))
{ err = 1;
break;
}
if(FUNCTOR(temp) == AND &&
FUNCTOR(ARG(1,arg)) == AND &&
equals(ARG(0,ARG(1,arg)),ARG(1,temp)) &&
equals(ARG(1,ARG(1,arg)),ARG(0,temp))
)
break; /* This sub is (p,q) and (q,p) was tried already */
}
if(i == nextdefn && INEQUALITY(g))
{ term u,v;
u = ARG(0,t);
v = ARG(1,t);
if(FUNCTOR(u) == '^' && ISINTEGER(ARG(1,u)) &&
equals(ARG(1,arg),ARG(0,u)) && econstant(v)
)
err = 1; /* on arg^2 = const, don't substitute u = arg */
}
}
if(err) /* whether through failure of autosub or because the
substitution it generated had already been tried */
{ reset_heap(startnode); /* searching for a subst could use a lot of space */
return 1; /* can't find a substitution */
}
if(get_polyvalnegexpflag() == 1 || !ISATOM(ARG(0,arg)))
*next = mid;
else
*next = replace_negexps(ARG(0,arg),mid);
if(ISATOM(ARG(0,arg)) && too_simple(*next,ARG(0,arg),ARG(1,arg)))
{ set_nvariables(saveit);
set_nextassumption(savenextassumption);
return 1;
}
permcopy(ARG(0,arg),&leftcopy);
permcopy(ARG(1,arg),&rightcopy);
err = let(leftcopy,rightcopy); /* enter it in the defns array */
if(err)
{ set_nvariables(saveit);
set_nextassumption(savenextassumption);
return 1;
}
set_valuepointers(next);
if(contains(ARG(1,arg),FUNCTOR(get_eigenvariable())))
set_eigenvariable(get_nvariables()-1);
concoct_reason(arg,reason);
if(subterm(ARG(1,arg),t))
SetShowStepArg(ARG(1,arg));
return 0;
}
else if(f == '=') /* and arg was given by user,
or by fractionalsubstitution
or by eliminatequadratic in cubics.c */
{ term leftcopy,rightcopy;
u = ARG(0,arg); /* the new variable */
nn = atomsin(ARG(1,arg),&atomlist1);
x = get_eigenvariable();
w = ARG(1,arg); /* the expression on the right of the definition */
if(contains(t,INTEGRAL) && !contains(w,INTEGRAL))
return 1; /* stop incorrect substitutions for non-constant
pieces of an integrand, while allowing
substitutions for an entire integral or term
containing it. */
if(nn==0)
{ errbuf(0, english(548));
/* It wouldn't help to substitute for a constant. */
free2(atomlist1);
return 1;
}
for(i=0;i<nn;i++)
{ /* look for a variable that occurs bound in t and fail if there is one */
int boundflag = bound_in(atomlist1[i],t);
int error;
if(boundflag)
{ switch(boundflag)
{ case SUM:
error = 1824;
break;
case PRODUCT:
error = 1825;
break;
case INTEGRAL:
error = 1826;
break;
case DIFF:
error = 1827;
break;
case LIMIT:
error = 1828;
break;
default:
error = 1829;
break;
}
errbuf(0,english(error));
/* Substitution is illegal because it contains the
... summation variable, product variable,
variable of integration, variable of differentiation,
the limit variable, a bound variable. */
free2(atomlist1);
return 1;
}
}
free2(atomlist1);
permcopy(u,&leftcopy);
permcopy(ARG(1,arg),&rightcopy);
err = let(leftcopy,rightcopy); /* this needs to be done before psubst */
if(err)
{ set_nvariables(saveit);
set_nextassumption(savenextassumption);
return 1;
}
HIGHLIGHT(u);
/* so the new variable will come out in yellow */
saveeigen = get_eigenindex();
set_eigenvariable(get_defn(get_nextdefn()-1).oldeigen); /* let changed it to the new var */
if(!complexnumerical(rightcopy))
SETDEPENDENT(u); /* for benefit of sortargs */
/* let has set dependency info in the
varlist, but this local u isn't affected. */
if(!contains(w,'^') && contains(w,FUNCTOR(x)) &&
is_linear_in(w,x) && !solve_linear_ineq_for(arg,x,&v)
)
/* example, u = x + 2 in a cubic polynomial f(x);
psubst may not work, we must solve for x in terms of u.
But if it does, it will be simpler to use it, so try
it first.
*/
{ rval = psubst(u,ARG(1,arg),t,&q);
if(contains(q,FUNCTOR(x)))
{ if(FUNCTOR(v) == '=' && equals(x,ARG(0,v)))
subst(ARG(1,v),x,q,&mid);
else if(FUNCTOR(v) == '=' && equals(x,ARG(1,v)))
subst(ARG(0,v),x,q,&mid);
else
return 1; /* terms containing x cancelled out, and
solve_linear_ineq_for "solves" it by returning an equation saying the
constant term (which may involve other variables) is zero. */
}
else
mid = q;
}
else
rval = psubst(u,ARG(1,arg),t,&mid);
/* was any variable eliminated? */
nn = atomsin_free(t,&atomlist1);
mm = atomsin_free(mid,&atomlist2);
free2(atomlist1);
free2(atomlist2);
if( !
(mm < nn /* two or more variables were eliminated */
|| (mm==nn && contains(mid,FUNCTOR(u)))
/* so mid must NOT contain some OLD variable, or mm would exceed nn */
)
)
/* no variable eliminated entirely; but if the only place
the variable is left is in the second argument of DIFF,
we want to accept it anyway. */
{ int nextdefn = get_nextdefn();
errbuf(0, english(549));
/* That substitution would not eliminate a variable */
errbuf(1, english(550));
/* so afterwards you would have MORE variables. */
set_eigenvariable(get_defn(nextdefn-1).oldeigen);
/* eigenvariable was changed by 'let' */
--nextdefn; /* because we called 'let' above */
set_nextdefn(nextdefn);
set_nvariables(saveit);
set_nextassumption(savenextassumption);
return 1;
}
if(rval == 1)
{ /* fractional exponent was created. This can cause loss of
some solutions in equation solving. Check whether it did.
If so don't do it. */
err = infer(domain(ARG(1,arg)));
if(err || get_nextassumption() > savenextassumption)
{ set_nvariables(saveit);
set_nextassumption(savenextassumption);
nextdefn = get_nextdefn();
set_eigenvariable(get_defn(nextdefn-1).oldeigen);
--nextdefn;
set_nextdefn(nextdefn);
errbuf(0,english(1592));
/* That substitution would require making an assumption,
which might cause you to lose some solutions. */
return 1;
}
problemtype = get_problemtype();
if(SOLVETYPE(problemtype))
{ /* check whether a new assumption has been made or not */
if(get_nextassumption() > savenextassumption)
commentbuf(0,english(1548));
/* !Solutions may be lost due to new assumptions made at this step. */
}
}
if(get_polyvalnegexpflag() == 1 || contains_neg_exp(t))
*next = mid;
else
*next = replace_negexps(u,mid);
set_valuepointers(next);
if(contains(ARG(1,arg),FUNCTOR(x)))
set_eigenvariable(get_nvariables()-1);
concoct_reason(arg,reason);
SetShowStepArg(ARG(1,arg));
SetShowStepOperation(makesubstitution);
set_eigenvariable(saveeigen);
return 0;
}
else if (f==AND) /* and arg was given by user */
{ /* Now prepare the terms to be entered in the defns array */
term leftcopy,rightcopy;
l = make_term(AND,n);
r = make_term(AND,n);
/* example: user input u= y-1, v = y+1;
we need to create ((u,v),(y-1,y+1)) */
for(i=0;i<n;i++)
{ u = ARG(i,arg);
assert(FUNCTOR(u) == '=');
ARGREP(l,i,ARG(0,u));
ARGREP(r,i,ARG(1,u));
}
permcopy(l,&leftcopy);
permcopy(r,&rightcopy);
err = let(leftcopy,rightcopy);
if(err)
{ set_nvariables(saveit);
set_nextassumption(savenextassumption);
return 1;
}
/* We do the "reverse substitutions" one at a time, not minding if
the old variables don't disappear until the end */
/* Example: in case t = r cos \theta + r sin \theta we first get
temp2 = x + r sin \theta with err=1, then we get x+y with err = 0,
so flag is set to 1 */
if(get_nvariables() + n > MAXDEPENDS)
{ errbuf(0, english(53));
/* I can't handle so many variables! */
errbuf(1, english(552)); /* Sorry about that. */
return 1;
}
assert(FUNCTOR(arg)==AND);
copy(t,&temp); /* fresh space so temp can be destroyed */
/* example: u = y-1, v = y+1. If we just call psubst,
only u will appear in the answer. */
for(i=0;i<n;i++)
{ u = ARG(0,ARG(i,arg));
v = ARG(1,ARG(i,arg));
HIGHLIGHT(u);
subst(u,v,temp,&temp2);
SETCOLOR(u,0);
destroy_term(temp);
temp = temp2;
}
for(i=0;i<n;i++)
{ u = ARG(0,ARG(i,arg));
v = ARG(1,ARG(i,arg));
HIGHLIGHT(u);
psubst(u,v,temp,&temp2);
SETCOLOR(u,0);
/* determine if any variable was eliminated */
nn = atomsin(temp,&atomlist1);
mm = atomsin(temp2,&atomlist2);
if(mm < nn || (mm==nn && contains(temp2,FUNCTOR(ARG(0,ARG(i,arg))))))
flag = 1; /* some variable eliminated */
free2(atomlist1);
free2(atomlist2);
destroy_term(temp);
temp = temp2;
}
if(flag == 0) /* failure */
{ destroy_term(temp);
set_nvariables(saveit);
set_nextassumption(savenextassumption);
nextdefn = get_nextdefn();
set_eigenvariable(get_defn(nextdefn-1).oldeigen);
--nextdefn;
set_nextdefn(nextdefn);
return 1;
}
*next = temp;
return 0;
}
else
{ /* using Term Selection, this operator should invent
the new variable */
u = getnewvar(t,"uvpqrstxyz");
if(FUNCTOR(u) == ILLEGAL)
{ errbuf(0, english(1448)); /* Too many subscripted variables, can't make more */
return 1;
}
vaux(u);
return makesubstitution(t,equation(u,arg),next,reason);
}
}
/*_______________________________________________________________________*/
int unwinddefinition(term t, term arg, term *next, char *reason)
/* In auto or menu mode, or if arg is not an atom,
expand the most recently made let-defn such that the left side of
one of its equations appears in t; return 0 for
success (so *next is instantiated) and 1 for failure (*next is garbage).
If this operator is chosen by the user in Term Selection mode,
arg will be a defined atom. Unwind the definition of that atom.
(The operator can also be called by ssolve, while the mode is Term Selection
mode, with an arg which isn't an atom.)
'toplevel' prevents exec from applying this to subterms; but
reduce(), which is called by lpt() and in turn by infer and refute,
calls this on subterms, because when trying to determine if atoms are defined,
it must expand definitions. */
{ int i,j,k;
defn u;
unsigned short f;
term safecopy;
term VietaVariable = getVietaVariable();
int err;
char buffer[81];
term temp,temp2;
int nextdefn = get_nextdefn();
if(nextdefn == 0)
return 1;
if(FUNCTOR(t) == MULTIPLICITY)
{ err = unwinddefinition(ARG(0,t),arg, &temp,reason);
if(err)
return 1;
*next = make_term(MULTIPLICITY,2);
ARGREP(*next,0,temp);
ARGREP(*next,1,ARG(1,t));
return 0;
}
j=0;
if(get_mathmode() == SELECTIONMODE && ISATOM(arg) && FUNCTOR(arg) != ILLEGAL)
{ f = FUNCTOR(arg);
for(i=nextdefn-1;i >=0;--i)
{ u = get_defn(i);
if(contains(u.reverse ? u.right : u.left,f))
break;
}
if(i < 0)
return 1;
if(u.reverse)
return unviete(t,arg,next,reason);
if(ISATOM(u.left))
{ f = FUNCTOR(u.left);
err = contains_bound(t,f);
if(FUNCTOR(t) == '=' &&
FUNCTOR(ARG(0,t)) == INTEGRAL &&
equals(ARG(1,ARG(0,t)),u.left) &&
!contains(ARG(1,t),INTEGRAL)
)
/* example: integral(cos(ln x),x), substitute u = ln x,
get integral(e^u cos u), do it twice by parts and solve,
you get integral(e^u cos u) = ... and you want to
get rid of u, getting the answer in terms of x again.
If you don't intercept this case, the following code
won't permit you to unwind u without evaluating the
integral. We just drop the left-hand side of the
equation.
*/
return unwinddefinition(ARG(1,t),arg,next,reason);
if( err)
{ char temp[128];
strcpy(temp,english(537));
/* You must first evaluate the */
strcat(temp, err==INTEGRAL ?
english(538) /* integral*/
: err == DIFF ? english(539) /* derivative */
: err == SUM ? english(540) /* sum */
: err == LIMIT ? english(541) /* limit */
: english(542) /* indexed product */
);
errbuf(0,temp);
return 1; /* can't unwind a bound variable */
}
}
}
else /* menu or auto mode, or called by ssolve */
{ for(i=nextdefn-1;i>=0;--i)
{ u = get_defn(i);
if(u.permanent) /* don't expand 'permanent' definitions */
/* This won't make trouble in calculating domains,
e.g. if y = 1/x, the assumption x!=0 was
generated when or before y was defined */
continue;
if(u.reverse && common_variables(u.right,t))
return unviete(t,arg,next,reason); /* the only legitimate way
to eliminate a reverse_let defined variable. */
if(u.reverse)
continue; /* don't unwind reverse definitions as the NEW
variable is on the right. */
if(ISATOM(u.left))
{ f = FUNCTOR(u.left);
err = contains_bound(t,f);
if(FUNCTOR(t) == '=' &&
FUNCTOR(ARG(0,t)) == INTEGRAL &&
equals(ARG(1,ARG(0,t)),u.left) &&
!contains(ARG(1,t),INTEGRAL)
)
/* example: integral(cos(ln x),x), substitute u = ln x,
get integral(e^u cos u), do it twice by parts and solve,
you get integral(e^u cos u) = ... and you want to
get rid of u, getting the answer in terms of x again.
If you don't intercept this case, the following code
won't permit you to unwind u without evaluating the
integral. We just drop the left-hand side of the
equation.
*/
return unwinddefinition(ARG(1,t),arg,next,reason);
if(FUNCTOR(t) == '=' &&
FUNCTOR(ARG(0,t)) == DIFF &&
equals(ARG(0,ARG(0,t)),u.left) &&
!contains(ARG(1,t),DIFF)
)
/* dy/dx = f(x,y); we want to expand the definition
of y on the right but not on the left. */
{ err = unwinddefinition(ARG(1,t),arg,&temp,reason);
if(!err)
{ *next = equation(ARG(0,t),temp);
return 0;
}
}
if( err)
{ char temp[128];
strcpy(temp, english(537));
/* You must first evaluate the */
strcat(temp, err==INTEGRAL ?
english(538) /* integral*/
: err == DIFF ? english(539) /* derivative */
: err == SUM ? english(540) /* sum */
: err == LIMIT ? english(541) /* limit */
: english(542) /* indexed product */
);
errbuf(0,temp);
continue; /* can't unwind a bound variable */
}
}
else
f = FUNCTOR(ARG(j,u.left));
if(contains(t,f))
break; /* out of the for-loop */
if(FUNCTOR(u.left)==AND && j < ARITY(u.left) - 1)
++j;
else
j=0;
}
}
if(i<0)
return 1;
/* now one of the atoms defined in u occurs free in t */
set_eigenvariable(get_defn(i).oldeigen); /* reset 'eigenvariable' to its value before this definition was made */
if(FUNCTOR(u.left)==AND)
{ /* because of the restriction in the comments on 'let', it
doesn't matter if we do the substitution serially or
simultaneously */
copy(t,&temp);
copy(u.right,&safecopy); /* because undo will destroy u.right */
for(j=0;j<ARITY(u.left);j++)
{ HIGHLIGHT(ARG(j,safecopy));
subst(ARG(j,safecopy),ARG(j,u.left),temp,&temp2);
if(contains(temp2,ABSFUNCTOR))
setprimes(temp,&temp2);
/* destroy_term(temp) causes a crash here, because
temp and temp2 overlap in copies of ARG(j,safecopy) */
temp = temp2;
}
*next = temp;
}
else
{ copy(u.right,&safecopy);
HIGHLIGHT(safecopy);
subst(safecopy,u.left,t,next);
if(contains(*next,ABSFUNCTOR))
setprimes(t,next);
if(FUNCTOR(*next) == OR && ARITY(*next) == 2 && FUNCTOR(safecopy) == '^' &&
equals(ARG(1,safecopy),three) &&
equals(ARG(0,safecopy),VietaVariable)
)
{ /* ARG(0,safecopy) is the variable introduced by the Vieta substitution? If so, keep only ONE of the
two solutions. */
*next = ARG(0,*next);
commentbuf(0,"We keep only one of the two solutions, since each of the two will generate the same three solutions of the cubic equation.");
}
}
strcpy(reason, english(543)); /* defn of */
mstring(u.left,buffer); /* works nicely even if it is an AND */
k = (int) strlen(buffer);
if(k < 15)
strcat(reason,buffer);
else
strcpy(reason, english(544)); /* expand defn */
SetShowStepArg(u.left);
return 0;
}
/*_______________________________________________________________________*/
int invisiblesub(term t, term arg, term *next, char *reason)
/* corresponds to menu text "write it as a function of ..." */
/* similar to makesubstitution, but no new variable will
be introduced; the term will simply be grouped appropriately; example,
x^4 -y^4 written as a function of x^2, y^2, will be (x^2)^2 - (y^2)^2.
Since no new variable is introduced, if it's only a matter of
substitution we want to fail. Only if associativity or the power law
is used to regroup terms to make t a function of arg does this succeed.
Invisibly, a new variable is created, and a let-definition is entered,
with .visible field marked 0. The occurrences of 'arg' in *next are
marked 'protected' so they won't be simplified by automode
until definitions are unwound.
In auto mode, if the substitution turns out to involve a negative
exponent which wasn't there before, it should be made an explicit substitution.
This operator can't deal with a double substitution. If autosub
returns a double substitution it is made explicit instead.
*/
{ int i,flag,err,nn;
int nextdefn;
int saveeigenindex = get_eigenindex();
short savenextassumption = get_nextassumption();
int savenvariables = get_nvariables();
term newvar,u,temp,leftcopy,rightcopy;
void *startnode;
term *atomlist1;
char mu[2] = {MU,0};
char buffer[128];
char buffer2[128];
if(FUNCTOR(arg) == ILLEGAL)
{ if(get_substitutionflag() != INVISIBLESUBS)
return 1;
if(FUNCTOR(t) != '+')
return 1; /* called in auto mode only on sums we hope to factor */
startnode = heapmax();
err = autosub(t,&arg,&temp);
if(err)
{ reset_heap(startnode);
return 1;
}
nextdefn = get_nextdefn();
for(i=0;i<nextdefn;i++) /* reject previously attempted substitutions */
{ if(equals(get_defn(i).right,ARG(1,arg)))
{ err = 1;
break;
}
}
if(err)
{ reset_heap(startnode);
return 1;
}
assert(FUNCTOR(arg) == '=');
if(get_problemtype() == FACTOR)
{ /* don't go on unless temp actually factors */
err = factor(temp,&u);
ResetShowStep(); /* factor may call SetShowStepOperation */
if(err)
return 1;
}
if(FUNCTOR(ARG(0,arg))==AND) /* a double substitution */
{ /* make it explicit after all */
permcopy(ARG(0,arg),&leftcopy);
permcopy(ARG(1,arg),&rightcopy);
let(leftcopy,rightcopy);
*next = temp;
nextdefn = get_nextdefn();
get_defns()[nextdefn-1].visible = 1;
concoct_reason(arg,reason);
return 0;
}
else
{ u = ARG(1,arg); /* autosub returns arg in the form newvar = u */
newvar = ARG(0,arg);
}
if(tc_aux(newvar,temp) || /* negative or fractional exponents of the new variable required */
contains(arg, ROOT) || /* or the substitution itself involves a ROOT or SQRT */
contains(arg, SQRT)
)
/* then make it an explicit substitution after all, there's no
hope of finishing it with a negative exponent on a complicated
expression */
{ int oldflag = get_substitutionflag();
int nvariables = get_nvariables();
--nvariables; /* get rid of 'MU' introduced by autosub */
set_nvariables(nvariables);
newvar = getnewvar(t,"uvwpqst");
if(FUNCTOR(newvar) == ILLEGAL)
{ errbuf(0,english(1448));
/* Too many subscripted variables, can't make more. */
return 1;
}
arg = equation(newvar,u);
set_substitutionflag(VISIBLESUBS);
saveeigenindex = get_eigenindex();
err = makesubstitution(t,arg,next,reason);
set_substitutionflag(oldflag);
if(err)
{ set_eigenvariable(saveeigenindex);
set_nvariables(nvariables);
set_nextassumption(savenextassumption);
return err;
}
/* Now check that the resulting sum is really going to factor */
err = factor(*next,&temp);
ResetShowStep(); /* factor may call SetShowStepOperation */
if(err)
{ set_eigenvariable(saveeigenindex);
set_nvariables(nvariables);
set_nextassumption(savenextassumption);
return 1;
}
}
/* Now we're in auto mode, and we're not going to make the
substitution explicit, but we DON'T want to actually do it
unless the resulting sum really is going to factor */
if(!contains(temp,'^'))
/* example, temp = u - 2 + sqrt(u). */
return 1;
err = factor(temp,&temp);
ResetShowStep(); /* factor may call SetShowStepOperation */
if(err)
{ set_nextassumption(savenextassumption);
set_nvariables(savenvariables);
set_eigenvariable(saveeigenindex);
return 1;
}
/* too bad the result of factoring it is wasted, but
we aren't going to show it now. */
}
else /* menu mode or term selection mode */
{ assert(FUNCTOR(arg) != AND); /* no double substitutions here */
newvar = getnewvar(t,mu);
if(FUNCTOR(newvar) == ILLEGAL)
{ errbuf(0,english(1448));
/* Too many subscripted variables, can't make more. */
return 1;
}
u = arg;
arg = equation(newvar,arg);
}
/* Check for illegal bound variables */
nn = atomsin(ARG(1,arg),&atomlist1);
for(i=0;i<nn;i++)
{ /* look for a variable that occurs bound in t and fail if there is one */
int boundflag = bound_in(atomlist1[i],t);
int error;
if(boundflag)
{ switch(boundflag)
{ case SUM:
error = 1824;
break;
case PRODUCT:
error = 1825;
break;
case INTEGRAL:
error = 1826;
break;
case DIFF:
error = 1827;
break;
case LIMIT:
error = 1828;
break;
default:
error = 1829;
break;
}
errbuf(0,english(error));
/* Substitution is illegal because it contains the
... summation variable, product variable,
variable of integration, variable of differentiation,
the limit variable, a bound variable. */
free2(atomlist1);
return 1;
}
}
free2(atomlist1);
PROTECT(u);
flag = psubst(newvar,u,t,&temp);
if(!flag)
return 1;
HIGHLIGHT(u);
subst(u,newvar,temp,next);
if(equals(t,*next))
{ errbuf(0,english(1515));
/* It's already written as a function of that expression. */
return 1;
}
/* check if we are looping by making a substitution that has been
made before, e.g. on x^4 + 4x^2 + 1, we set u = x^2 and then
simplify back to the same expression again. So don't repeat it. */
nextdefn = get_nextdefn(); /* if in menu mode, it wasn't set yet */
for(i=0;i<nextdefn;i++)
{ if(equals(arg,get_defn(i).right))
return 1; /* fail right there */
}
/* OK, haven't tried this one before */
permcopy(ARG(0,arg),&leftcopy);
permcopy(ARG(1,arg),&rightcopy);
let(leftcopy,rightcopy);
nextdefn = get_nextdefn();
set_eigenvariable(get_defn(nextdefn-1).oldeigen);
/* The previous line is here because let used to reset the eigenvariable.
I think this is superfluous, but it's working, so hands off. */
get_defns()[nextdefn-1].visible = 0;
strcpy(reason, english(547)); /* write as function of */
/* e.g. 3(t-1) + t => 4(t-1) + 1 */
mstring(ARG(1,arg),buffer);
truncate3(buffer, buffer2);
strcat(reason,buffer2);
return 0;
}
/*_______________________________________________________*/
static int atomsin_free(term t, term **atomlist)
/* return in **atomlist an array of all atoms occurring in t
(without duplicates) NOT including e,pi_term, and 'i' if they occur;
the dimension of the array is the return value; space is
allocated by this function. BUT, unlike atomsin, DON'T count
atoms occurring in the bound-variable position of DIFF, INTEGRAL,
SUM, or LIMIT.
*/
{
int nvars = 0; /* number of atoms so far put in atomlist */
int maxvars = 10; /* space allocated for atomlist */
*atomlist = (term *) callocate(maxvars,sizeof(term));
vvaux2(t,atomlist,&nvars,&maxvars); /* similar to vaux */
return nvars;
}
/*__________________________________________________________*/
static void vvaux2(term t, term **atomlist, int *nvars, int *maxvars)
/* continue adding new variables to atomlist, which has dimension
*maxvars; *nvars is the dimension so far used. But DON'T count
variables in the bound positions of SUM, INTEGRAL, DIFF.
If it isn't big enough, make more space */
{ int i;
unsigned short a = FUNCTOR(t);
if(ISATOM(t))
{ if(a==PI_ATOM || (a== 'i' && TYPE(t) != INTEGER) || a == 'e' ||
a == INFINITYFUNCTOR || a==LEFT || a==RIGHT
)
return; /* don't count these */
/* determine if t is already in varlist */
for(i=0;i < *nvars;i++)
{ if(FUNCTOR((*atomlist)[i]) == a)
break; /* already there */
}
if(i== *nvars) /* wasn't already there, must add it */
{ if( *nvars < *maxvars -2) /* go ahead and add it */
{ (*atomlist)[*nvars] = t;
++ *nvars;
}
else /* get more space and then add it */
{ *maxvars += 10;
*atomlist = (term *) reallocate(*atomlist, *maxvars * sizeof(term));
(*atomlist)[*nvars] = t;
++ *nvars;
}
}
return;
}
if(OBJECT(t))
return ;
/* so now it's a compound term */
if(a==DIFF || a==SUM || a==INTEGRAL)
{ for(i=0;i<ARITY(t);i++)
{ if(i!=1)
vvaux2(ARG(i,t),atomlist,nvars,maxvars);
}
}
else
for(i=0;i<ARITY(t);i++)
vvaux2(ARG(i,t),atomlist,nvars,maxvars);
}
/*________________________________________________________________________*/
static int tc_aux(term a, term p)
/* return 1 if term p contains negative or fractional powers of a,
zero if not. */
{ unsigned short n;
int i;
if(ATOMIC(p))
return 0;
if(FUNCTOR(p) == '^' && equals(ARG(0,p),a))
{ if(NEGATIVE(ARG(1,p)))
return 1;
if(FUNCTOR(ARG(1,p)) == '/')
return 1;
return tc_aux(a,ARG(1,p));
}
n = ARITY(p);
for(i=0;i<n;i++)
{ if(tc_aux(a,ARG(i,p)))
return 1;
}
return 0;
}
/*_____________________________________________________________________*/
static term replace_negexps(term a, term t)
/* replace negative powers of a in t by fractions */
{ unsigned short n;
int i;
term ans;
if(ATOMIC(t) || PROTECTED(t))
return t;
if(FUNCTOR(t) == '^' && equals(a,ARG(0,t)) && NEGATIVE(ARG(1,t)))
return reciprocal(make_power(a,ARG(0,ARG(1,t))));
n = ARITY(t);
ans = make_term(FUNCTOR(t),n);
for(i=0;i<n;i++)
ARGREP(ans,i,replace_negexps(a,ARG(i,t)));
return ans;
}
/*________________________________________________________________*/
#define TRUNCATE (MAXREASONSTRING + 2)
static void truncate3(char *buffer, char *reason)
/* put TRUNCATE characters of buffer in reason; if buffer
has more characters, put only TRUNCATE-3 of them and then ... */
{ char a[4];
if(strlen(buffer) < TRUNCATE)
strcpy(reason, buffer);
else
{ memcpy(a,buffer+TRUNCATE-2,4);
buffer[TRUNCATE+1] = '\0';
buffer[TRUNCATE-2] = buffer[TRUNCATE-1] = buffer[TRUNCATE] = '.';
strcpy(reason,buffer);
}
}
/*_______________________________________________________________*/
static void concoct_reason(term arg, char *reason)
/* arg is a definition suitable for 'let' . Make a
reason string out of it */
{ unsigned short f;
char buffer[DIMREASONBUFFER];
char buffer2[DIMREASONBUFFER];
int k,k2;
assert(FUNCTOR(arg) == '=');
f = FUNCTOR(ARG(0,arg));
if(f == AND)
{ unsigned short n = ARITY(arg);
assert(n == 2); /* double substitutions only */
mstring(equation(ARG(0,ARG(0,arg)),ARG(0,ARG(1,arg))), buffer);
mstring( equation(ARG(1,ARG(0,arg)),ARG(1,ARG(1,arg))), buffer2);
k = (int) strlen(buffer);
k2 = (int) strlen(buffer2);
if(k+k2 <= TRUNCATE)
{ strcpy(reason,buffer);
strcat(reason,k+k2 == TRUNCATE-1 ? "," : ", ");
strcat(reason,buffer2);
}
else
{ truncate3(buffer, reason);
memset(reason+k,TRUNCATE-k,32); /* pad first reason line with spaces */
truncate3(buffer2, reason+22);
}
return;
}
mstring(arg,buffer); /* don't bother truncating, just let it run off-screen
if it must, or let display_reason break it. */
strncpy(reason,buffer,DIMREASONBUFFER);
}
/*________________________________________________________________*/
static int stopmaxsub_aux(term u, term v)
/* return 1 if u is linear in v or if u has
the form f(linear-in-v), e.g. log(sqrt(v + v))
or if u is a product of linear functions of v,
or a quotient of linear functions of v, or is an
irreducible quadratic in v; but not if the constant
term involves another variable, as in (u-a)(u+a).
It is presumed that v is an atom.
We may assume there aren't two identical
linear-in-v subterms (or maxsub would have found
that subterm instead of the term for which v was
substituted). It is presumed that u does contain v.
This is used in stopmaxsub below.
*/
{ unsigned short n = ARITY(u);
term w;
int i,count,flag,nvars;
term *atomlist;
if(is_linear_in(u,v))
return 1;
if(FRACTION(u) && is_linear_in(ARG(0,u),v) && is_linear_in(ARG(1,u),v))
return 1;
if(FUNCTOR(u) == '+' && ARITY(u) == 3 && irreducible(u))
return 1;
count = 0;
for(i=0;i<n;i++)
{ w = ARG(i,u);
if(contains(w,FUNCTOR(v)))
{ flag = i;
++count;
}
}
if(count > 1)
{ if(FUNCTOR(u) != '*')
return 0;
for(i=0;i<n;i++)
{ if(!is_linear_in(ARG(i,u),v))
return 0;
}
/* It's a product of linear functions of v; don't return 1
just yet because we have to trap (u-a)(u+a) */
nvars = variablesin(u,&atomlist);
free2(atomlist);
if(nvars > 1)
return 0;
return 1;
}
return stopmaxsub_aux(ARG(flag,u),v);
}
/*_________________________________________________________________*/
static int contains_sqrtsq(term t)
/* return 1 if t has a subterm of the form (sqrt x)^2 or
root(n,x)^n
*/
{ unsigned short n,f;
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^')
{ f = FUNCTOR(ARG(0,t));
if(f == SQRT && equals(ARG(1,t),two))
return 1;
if(f == ROOT && equals(ARG(1,t),ARG(0,ARG(0,t))))
return 1;
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_sqrtsq(ARG(i,t)))
return 1;
}
return 0;
}
/*_________________________________________________________________*/
static int stopmaxsub(term t, term arg)
/* Return 1 to prevent the use of maximalsub.
Specifically:
Return 1 if !SOLVETYPE(problemtype) and arg is linear.
Return 1 if t is linear in arg, or a product of linear functions of arg,
or an equation or inequality whose right side is zero and
left is linear in arg or a product of linear functions of arg.
Also, it should return 1 on f(linear(arg)). Example
sqrt(arg + arg), as in sqrt(2x^2 + 2x^2).
Also, it returns 1 on arg^n = constant, since the next step after
[u = sqrt(constant), u = - sqrt constant] will be to unwind u.
Otherwise return 0.
Also, return 1 if t is an irreducible quadratic in arg, for
example arg^2 + arg + 1.
maxsub is used before any simplification is done; otherwise we miss
some possible and good substitutions, particularly involving fractions.
But on the other hand there is at least one example in which the expression
contains (sqrt x)^2 where u = sqrt x isn't ideal, and the problem goes
better by first changing (sqrt x)^2 to x. So, we check for a subterm of
this form and stop maxsub on such problems.
*/
{ unsigned short f = FUNCTOR(t);
term u,v,newvar;
int rval;
int problemtype;
int nvariables;
if(contains_sqrtsq(t))
return 1;
problemtype = get_problemtype();
if(!SOLVETYPE(problemtype) && !is_linear(arg))
/* is_linear returns 0 if its arg is linear; the name is misleading! */
return 1;
if(contains(t,INTEGRAL))
return 1;
if(INEQUALITY(f))
{ u = ARG(0,t);
v = ARG(1,t);
if(FUNCTOR(u) == '^' && INTEGERP(ARG(1,u)) && equals(ARG(0,u),arg) && econstant(v))
return 1;
if(!ZERO(ARG(1,t)))
return 0;
return stopmaxsub(u,arg);
}
nvariables = get_nvariables();
newvar = getnewvar(t,"uvw");
vaux(newvar); /* add it to varlist */
subst(newvar,arg,t,&u);
rval = stopmaxsub_aux(u,newvar);
set_nvariables(nvariables); /* get rid of newvar */
return rval;
}
/*_________________________________________________________________*/
int maximalsub(term t, term arg, term *next, char *reason)
/* make a substitution u = f(x) where every occurrence of x in
t is in an occurrence of f(x), and f(x) is not just t or x itself,
and at least two occurrences of x in t are in occurrences of f(x).
This is an automode_only operator, brother of makesubstitution,
which is applied only at toplevel in automode.
If t is an OR, however, it works separately on the equations,
until it finds an equation it applies to, and then it checks
whether the resulting substitution also works to eliminate
all and more than one occurrence of the variable in the
other equations, in which case it uses it there too.
We force it to fail if the result of the substitution
is a product of linear functions of u, such as (u-2)(u+3),
or a single linear function of u, or if the result is
an equation of such a thing to zero, and under other conditions
set forth in stopmaxsub.
On an interval_as_and term it should just fail outright, as
technically such a term is and(lessthan(a,x),lessthan(x,b)) so
if x is not an atom, it is automatically going to be found as a
substitution by maximalsub, which will then immediately have to
be unwound.
*/
{ int i,err,nextdefn,k,flag=0;
unsigned short n;
term u,v,leftcopy,rightcopy,temp,q;
int nvariables = get_nvariables();
if(FUNCTOR(t) == AND && interval_as_and(t))
return 1; /* see above */
if(FUNCTOR(t) == OR)
{ n = ARITY(t);
for(k=0;k<n;k++)
{ if(!maximal_sub(ARG(k,t),&arg) && !stopmaxsub(ARG(k,t),arg))
break; /* found an equation it works on */
}
if(k==n)
return 1; /* it didn't work on any equation */
}
else
{ err = maximal_sub(t,&arg); // this finds the substitution and puts it in arg.
if(err)
return 1;
if(NEGATIVE(arg))
{ arg = ARG(0,arg);
if(ATOMIC(arg))
return 1;
}
set_valuepointers(&arg); /* maximal_sub just uses MAKE_ATOM
so the value pointers are dangling */
if(stopmaxsub(t,arg))
return 1;
}
/* reject previously used definitions, otherwise we
will make the same definition over and over with a new
letter each time */
/* Indeed, reject subterms of previous definitions, as
it looks silly to use u = ax+b and then unwind it,
and then use v = ax. */
nextdefn = get_nextdefn();
for(i=0;i<nextdefn;i++)
{ q = get_defn(i).right;
if(equals(q,arg))
return 1; /* this definition already used */
if(almost_equals(q,arg))
return 1; /* e.g., root(4,x) and x^(1/4) */
if(subterm(arg,q))
return 1; /* see above. */
}
u = getnewvar(t,"uvwpqst");
if(FUNCTOR(u) == ILLEGAL)
{ errbuf(0, english(1448));
/* Too many subscripted variables, can't make more. */
return 1;
}
HIGHLIGHT(u);
SETDEPENDENT(u); /* for benefit of sortargs */
set_dependency_info(u,arg); /* u must be shown in varinfo to depend on
something, now that SETDEPENDENT(u) has been done, because
addcompare1 will suffer an assertion failure if u is marked
DEPENDENT but varinfo doesn't correspond by showing some
dependency. */
set_valuepointers(&u);
err = enlarge_maxsub(t,u,arg,&temp);
/* Example: e^2x - e^-(2x), we have now found u = 2x, but
it looks silly not to go ahead and find u = e^2x.
*/
if(!err)
{ arg = temp;
flag = 1;
}
if(FUNCTOR(t) == OR)
{ *next = make_term(OR,n);
for(i=0;i<n;i++)
{ subst(u,arg,ARG(i,t),&v);
if(noccurs(u,v) > 1)
ARGREP(*next,i,v);
else
ARGREP(*next,i,ARG(i,t));
}
}
else if(!flag)
subst(u,arg,t,next); /* not psubst, subst will work */
else
psubst(u,arg,t,next); /* use psubst if enlarge_maxsub was used */
/* But, at the last moment fail if the result will be so
simple that the next step will just be to unwind the definition.
For example if the result is linear in u, or the quotient of
two linear functions of u. */
if(too_simple(*next,u,arg))
{ set_nvariables(nvariables);
return 1;
}
if(get_polyvalnegexpflag() != 1 && !contains_neg_exp(t))
*next = replace_negexps(u,*next);
/* OK, now make the let_definition */
permcopy(u,&leftcopy);
permcopy(arg,&rightcopy);
let(leftcopy,rightcopy);
set_eigenvariable(get_nvariables()-1);
concoct_reason(equation(leftcopy,rightcopy),reason);
SetShowStepArg(arg);
SetShowStepOperation(makesubstitution);
return 0;
}
/*_____________________________________________________________________*/
int too_simple(term t, term u, term arg)
/* u is an atom, t is the result of substituting u for arg in some
equation or inequality, or OR of equations or inequalities.
Return 1 if t is so simple that the next step will
be to unwind the definition of u. It checks for a
linear function of u or a product of linear functions of u,
or a quotient of functions one of whose args is linear and
the other either linear or product of linear.
If the problemtype is not a SOLVETYPE, it also rejects a polynomial
in u, or the case when arg is a power or root of an atom or an integer
times a power or root of an atom.
It also catches the case arg = root or sqrt of x and t is a power
of u.
It also returns 1 if there is only one occurrence of u in t.
*/
{ unsigned short f = FUNCTOR(t);
int i,n;
if(f == OR)
{ for(i=0;i<ARITY(t);i++)
{ if(!too_simple(ARG(i,t),u,arg))
return 0;
}
return 1; /* example, [a < 6-x < b, 6-x < 0], don't use u = 6-x */
}
n = noccurs(u,t);
if(n < 2)
return 1;
if(interval_as_and(t) &&
!contains(ARG(0,ARG(0,t)),FUNCTOR(u)) &&
!contains(ARG(1,ARG(1,t)),FUNCTOR(u)) &&
noccurs(u,ARG(1,ARG(0,t))) < 2
)
return 1; /* in a < u < b, u technically occurs twice, but we
want to count that as only one occurence. */
if(INEQUALITY(f))
{ if(ZERO(ARG(1,t)))
return too_simple(ARG(0,t),u,arg);
if(ZERO(ARG(0,t)))
return too_simple(ARG(1,t),u,arg);
/* Example: u^2-2 < - 3u is NOT too_simple, e.g. if u = x^2-9
this becomes an impossible mess without substitution. So, we
can't just say return 1 if both sides are too_simple, as the
one-occurrence clause will apply to the sides taken separately. */
/* Besides, even without the two-occurrence rule, we don't want to
prevent u = sqrt(x+5) in 10/sqrt(x+5) = sqrt(x+5)-3, which
comes to 10/u = u-3, both sides of which are too_simple if
considered alone. */
if(is_linear_in(ARG(0,t),u) && is_linear_in(ARG(1,t),u))
return 1;
}
return too_simple_aux(t,u,arg);
}
/*_________________________________________________________________________*/
static int too_simple_aux(term t, term u, term arg)
/* Complete the work of too_simple above, without the
requirement that u occur more than once.
*/
{ term p,q,poly,c,s,w;
int problemtype;
int j;
char buffer[DIMREASONBUFFER];
if(is_linear_in(t,u))
return 1;
/* reject u = root(4,x^2) and the like */
if(FUNCTOR(u) == ROOT)
{ if(!rootofpower2(u,zero,&w,buffer))
return 1;
if(!rootofpower4(u,zero,&w,buffer))
return 1;
if(!powerofroot(u,zero,&w,buffer))
return 1;
}
problemtype = get_problemtype();
if(!SOLVETYPE(problemtype))
{ if(FUNCTOR(arg) == '*')
{ ratpart2(arg,&c,&s);
arg = s;
}
else
s = arg;
if(FUNCTOR(s) == '^' && ISATOM(ARG(0,s)))
return 1;
if(FUNCTOR(s) == SQRT && ISATOM(ARG(0,s)))
return 1;
if(FUNCTOR(s) == ROOT && ISATOM(ARG(1,s)))
return 1;
if(FUNCTOR(s) == '+' &&
!makepoly(t,get_eigenvariable(),&poly)
)
return 1;
if(FUNCTOR(s) == '*')
{ /* reject for example u u^2 */
for(j=0;j<ARITY(t);j++)
{ p = ARG(j,t);
while(FUNCTOR(p) == '^' && NUMBER(ARG(1,p)))
p = ARG(0,p); /* ignore (u^2)^(1/3) for example */
if(ISATOM(p))
continue;
break;
}
if(j == ARITY(t))
return 1; /* all factors were atoms or powers of atoms */
}
}
if(FRACTION(t))
{ p = ARG(0,t);
q = ARG(1,t);
if(is_linear_in(p,u))
{ if(is_linear_in(q,u))
return 1;
if(FUNCTOR(q) == '*' && ARITY(q) == 2 &&
is_linear_in(ARG(0,q),u) && is_linear_in(ARG(1,q),u)
)
return 1;
}
if(FUNCTOR(p) == '*' && ARITY(p) == 2 &&
is_linear_in(ARG(0,p),u) && is_linear_in(ARG(1,p),u) &&
is_linear_in(q,u)
)
return 1;
}
if(FUNCTOR(t) == '^' && equals(u,ARG(0,t)) &&
(
(FUNCTOR(arg) == SQRT && ISATOM(ARG(0,arg))) ||
(FUNCTOR(arg) == ROOT && ISATOM(ARG(1,arg)))
)
)
return 1;
return 0;
}
/*_____________________________________________________________________*/
static int unviete(term t, term arg, term *next, char *reason)
/* eliminate a reverse-let_defined variable. This can only
be done to an equation or an OR of equations, and only after
the equations are solved.
*/
{ term x,y,u,v;
int i,nextdefn;
defn d;
unsigned short n;
if(FUNCTOR(t) != OR && FUNCTOR(t) != '=')
return 1;
n = ARITY(t);
if(FUNCTOR(arg) != ILLEGAL && ISATOM(arg))
y = arg;
else
y = get_eigenvariable();
if(!solved(t,y))
{ errbuf(0, english(1583));
/* Equations must be solved for the new variable. */
return 1;
}
/* Find which reverse definition has a right side containing y */
nextdefn = get_nextdefn();
for(i=nextdefn-1; i >= 0;i--)
{ d = get_defn(i);
if(d.reverse && contains(d.right,FUNCTOR(y)))
break;
}
if(i < 0)
return 1;
x = d.left;
u = d.right;
if(FUNCTOR(t) == OR)
{ *next = make_term(OR,n);
for(i=0;i<n;i++)
{ if(FUNCTOR(ARG(i,t)) != '=')
continue; /* conceivably it might be 'true' or 'false' */
psubst(ARG(1,ARG(i,t)),y,u,&v);
ARGREP(*next,i,equation(x,v));
}
}
else /* FUNCTOR(t) == '=' */
{ psubst(ARG(1,t),y,u,&v);
*next = equation(x,v);
}
HIGHLIGHT(*next);
strcpy(reason, english(1584)); /* eliminate */
strcat(reason,atom_string(y));
SetShowStepArg(y);
return 0;
}
/*_____________________________________________________________*/
int trigdoublesub(term t, term arg, term *next, char *reason)
/* substitute u,v for expressions in trig functions. */
/* If there are exactly two distinct trig args in t, not both atoms,
involving two distinct (real) variables (together) and each arg involves at
least one variable, then make a double substitution
for those two expressions. The user is not asked to supply an argument.
When counting variables, integer existential variables such as m in
sin(t + m pi_term) don't count.
*/
{ term a,b,u,v,d;
int err,nvars;
term *atomlist;
int nargs = trigargs(t,&a,&b);
if(nargs != 2)
return 1;
if(ISATOM(a) && ISATOM(b))
return 1; /* don't just change the names of the variables */
nvars = real_variables(sum(a,b),&atomlist);
if(nvars < 2)
return 1;
free2(atomlist);
nvars = variablesin(a,&atomlist);
free2(atomlist);
if(!nvars)
return 1; /* a is constant */
nvars = variablesin(b,&atomlist);
free2(atomlist);
if(!nvars)
return 1; /* b is constant */
u = getnewvar(t,"uvpqrstxyz");
v = getnewvar(t,"vpqrstxyz");
d = and(equation(u,a),equation(v,b));
err = makesubstitution(t,d,next,reason);
if(err)
return 1;
strcpy(reason, english(1831)); /* substitute u,v... */
return 0;
}
/*___________________________________________________________________________*/
static int almost_equals(term a, term b)
/* return 1 if a = sqrt(x) and b = x^(1/2), etc.
*/
{ if(FUNCTOR(a) == SQRT &&
FUNCTOR(b) == '^' && ONEHALF(ARG(1,b)) &&
equals(ARG(0,a),ARG(0,b))
)
return 1;
if(FUNCTOR(a) == ROOT && FUNCTOR(b) == '^' &&
FRACTION(ARG(1,b)) &&
equals(ARG(0,a),ARG(1,ARG(1,b))) &&
equals(ARG(1,a),ARG(0,b))
)
return 1;
return 0;
}
/*_________________________________________________________________________*/
static term getVietaVariable(void)
/* Return the 'y' introduced by the Vieta substitition, or false if there is no such variable */
{ int i;
defn u;
int nextdefn = get_nextdefn();
for(i=nextdefn-1;i>=0;--i)
{ u = get_defn(i);
if(u.reverse && FUNCTOR(u.right) == '+' && ARITY(u.right) == 2 && ISATOM(ARG(0,u.right)))
return ARG(0,u.right);
}
return falseterm;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists