Sindbad~EG File Manager
/* pre-associated operations in Mathpert
M. Beeson
1.7.91 Original date
6.28.99 modified
1.5.00 added code to block addfractions on complex numbers
1.13.00 modified stop_orderfactors
1.13.00 added complex_addfractions_conditions
1.13.00 added code near "secrecip"
1.18.00 modified stop_orderterms and stop_orderfactors
2.3.00 modified stop_orderterms for complex equation topics
2.3.00 added (trigexpandflag & 1) as a condition for using the doublecos operations
2.25.00 modified exponential_factor to not count linear exponents
6.16.00 modified intsub_in_preops
3.8.01 added common_factor and code that uses it.
3.9.01 added secsqminustansq etc.
6.22.04 don't use rootofpower etc. on complex arguments
8.27.04 added contains_big_power and code that calls it in stop_cancelgcd.
1.27.06 added sgrecip2 and sgrecip3 and modified conditions for sgfract1 and sgfract2
changed trigargsgcd1 at the ISATOM line to return 0 instead of 1.
*/
#define AUTOMODE_DLL
#include <assert.h>
#include <string.h>
#include <math.h> /* fabs, needed by ISZERO */
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "document.h"
#include "tdefn.h"
#include "checkarg.h" /* for operator typedef */
#include "ops.h" /* for prototypes of operators */
#include "operator.h"
#include "trig.h"
#include "calc.h"
#include "series.h"
#include "prover.h"
#include "polynoms.h"
#include "probtype.h"
#include "exec.h"
#include "algaux.h"
#include "order.h"
#include "automode.h"
#include "eqn.h"
#include "mplimits.h"
#include "trigtran.h"
#include "symbols.h"
#include "cflags.h" /* set_factorflags */
#include "deriv.h" /* derivative */
#include "automode.h" /* select_trigeval_op */
#include "pvalaux.h" /* isinteger, squareofone, expandable_sum */
#include "trigpoly.h" /* trigpolyargs2 */
#include "autoeqn.h" /* inhibit_transfer */
#include "autosimp.h" /* get_whichpass, get_path, get_pathlength */
#include "simpsums.h" /* collect */
#include "cancel.h" /* cancel */
#include "autosum.h" /* immediate_comdenom */
#include "autoeqn.h" /* different_sqrts */
#include "preprod.h" /* special_dif_of_squares */
#include "preops.h"
#include "autotrig.h" /* set_sincosflag */
#include "simpprod.h" /* expandable */
#include "pda.h" /* equal_mod_order */
#include "deval.h" /* seminumerical */
#include "fremark.h" /* contains_zero_denom */
#include "dcomplex.h" /* ceval.h needs it */
#include "ceval.h" /* complexnumerical */
#include "maxsub.h" /* maximal_sub */
#include "scontrol.h" /* series_preops */
static int stopcancel(term t);
static int contains_fract(term u);
static int contains_deriv(term u, term x);
static int trigfactor(term t);
static int suppress_trig_expansion(term u, term x);
static int intsub_on_sum(term, term);
static int logtrig(term);
static int bivariate(term);
static int perfect_power(long x, long n);
static int perfect_square(long);
static int blockintlinearity(term u, term x);
static int block_regroupterms(term t);
static int trignegative(term t);
static int count_abs(term t);
static int stop_cancelgcd(term t);
static int stop_differenceofsquares(term t);
static int local_contains_trig(term t);
static int commondenom_in_preops(term t);
static int stop_maxsub(term u);
static int trigproducts(term t);
static int trigargsgcd1(term t);
static int trigargsgcd(term t, term *gcd);
static unsigned short tpf_aux(term t, unsigned short a, unsigned short b);
int contains_power_of_sum(term t);
static unsigned short twologs(term t);
static int forced_logcollect(unsigned short f, term t);
static int contains_odd_power(term t, unsigned short f);
static int contains_even_power(term t, unsigned short f);
static int contains_complex_exponentials(term t);
static int stoplogofpower(term u);
static int trigalg_same(term u, term x);
static int exponential_factor(term t,term x);
static int stop_arith(term t);
static int obvious_trig(term t);
static int complex_addfractions_conditions(term t);
static int common_factor(term u, term v);
/*_____________________________________________________________________*/
static int trigpolyflag;
int get_trigpolyflag(void)
{ return trigpolyflag;
}
void set_trigpolyflag(int n)
/* used to initialize trigpolyflag in one_step. */
{ trigpolyflag = n;
}
/*_____________________________________________________________________*/
/* pre_ops and post_ops specify the details of the simplification algorithm
by telling what ops are 'pre-associated' and 'post-associated' with
each functor and its daughter functors. */
/* o is an array of operators, empty at the beginning, but
presumed to have space for at least MAXOPS operators to be
returned, and MAXOPS better be large enough to hold the largest answer.
nops will be returned as the number of operators placed into array o. */
void pre_ops(term t, actualop *o, int *nops)
{ unsigned short f = FUNCTOR(t);
unsigned short n = ARITY(t);
char buffer[DIMREASONBUFFER];
static int polyvalflag; /* set to 1 when pathlength = 0 if polyvalop is used */
int currentline = get_currentline();
int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
int factorflag, logcollectflag, trigexpandflag, radicalflag;
static int toplevel_arithmetic;
unsigned short g,h;
const int intflag = get_intflag();
const int indexflag = get_indexsumflag();
const int seriesflag = get_seriesflag();
int statusflag,numberflag,k;
term u,v,num,denom,x,a,b,gcd,w,q;
aflag arithflag = get_arithflag();
int pathlength = get_pathlength();
unsigned short const *path = get_path();
unsigned short tflag=0;
unsigned short hypertflag = 0;
int trigcount,j,i=0;
int topflag = (pathlength == 0 || (pathlength == 2 && path[0] == '-'));
unsigned short localtrigflag;
term temp;
int changelimitvarflag = 0;
if(problemtype == MINMAX && currentline == 0) /* get started */
{ if(FUNCTOR(t) == '=' && constant(ARG(1,t)))
{ o[i] = functionisconstant; ++i;
}
o[i] = addcriticalpoints; ++i;
}
if(ATOMIC(t))
{ *nops = i;
return;
}
factorflag = get_factorflag();
trigexpandflag = get_trigexpandflag();
radicalflag = get_radicalflag();
logcollectflag = get_logcollectflag();
if(f == DIFF)
{ if(!depends(ARG(0,t),ARG(1,t)))
{ o[i] = difconstant; ++i;
/* before trying to simplify ARG(0,t); especially if ARG(0,t)
is an integral! */
}
g = FUNCTOR(ARG(0,t));
if(g == INTEGRAL)
{ o[i] = fundamentaltheorem2; ++i;
/* get it in the list before polyvalop, otherwise e.g.
a constant can be pulled out of the integral and the
opportunity to use the fundamental theorem is missed */
}
if((g == SEC || g == TAN || g == CSC) &&
ISATOM(ARG(0,ARG(0,t))) &&
problemtype != DIFFERENTIATE_FROM_DEFN
)
{ o[i] = g == SEC ? difsec : g == TAN ? diftan : difcsc; ++i;
} /* before sec goes to 1/cos, which screws up trig integrals */
}
if(pathlength == 0)
toplevel_arithmetic = 0;
if(pathlength == 0 && problemtype == TRIG_IDENTITY)
{ term *atomlist;
int nvariables = variablesin(t,&atomlist);
free2(atomlist);
if(nvariables > 1)
{ o[i] = trigdoublesub; ++i;
}
if(nvariables)
{ if(!stop_maxsub(t))
{ o[i] = maximalsub; ++i;
}
if(get_whichpass() > 1 || !trigargsgcd1(t))
/* First try to solve it without substitutions. In some
cases, for example sin^4 (x/2) + cos^4(x/2) = ...,
a substitution u = x/2 will ruin things. Besides, if the
identity can be done with half-angle operations, it's
somewhat more esthetic not to use a substitution.
But e.g. on 2 sin(x/2) cos(x/2) = sin x, it makes
good sense to use u = x/2, rather than express
sin(x/2) as a square root and make assumptions.
However, an identity which involves e.g. sin (8x) and
trig functions of 2x, should begin by u = 2x, so in such
a case we do NOT want to wait till whichpass > 1. That's
what trigargsgcd is for.
*/
{ set_substitutionflag(VISIBLESUBS);
localtrigflag = 0;
set_trigflag(t,&localtrigflag);
if(localtrigflag)
/* don't make substitutions in purely algebraic identities.
Otherwise we get several useless substitutions after the first
sometimes. */
{ o[i] = makesubstitution; ++i;
}
}
}
}
if(pathlength == 0 &&
problemtype != LINEAR_EQUATION &&
problemtype != LINEAR_EQUATIONS &&
(
(SOLVETYPE(problemtype) && !solved(t,get_eigenvariable())) ||
problemtype == FACTOR ||
problemtype == SIMPLIFY
) &&
!mvpoly(t) &&
!contains_bound_vars(t)
)
{ if(f == OR)
{ o[i] = trueeqn; ++i;
o[i] = spliteqn; ++i; /* try it before maximalsub */
o[i] = spliteqn2; ++i;
}
if(problemtype != IMPLICIT_DIFF &&
f != '=' &&
/* when solving equations it will be called in pre_equation */
!stop_maxsub(t)
)
{ o[i] = maximalsub; ++i;
}
}
if(pathlength == 0 && CALCULUS_TOPIC(currenttopic) && currenttopic != _minmax)
{ o[i] = undefinedpart; ++i;
}
if(pathlength == 0 &&
currenttopic != _evaluate_numerically &&
currenttopic != _numerical_exponents &&
currenttopic != _alg1_exponents &&
currenttopic != _add_numerical_fractions &&
currenttopic != _numerical_radicals &&
currenttopic != _alg1_radicals
)
{ if(INEQUALITY(f) && SOLVETYPE(problemtype) &&
FUNCTOR(ARG(0,t)) == '^' && FUNCTOR(ARG(1,t)) == '^' &&
equals(ARG(0,ARG(0,t)),ARG(0,ARG(1,t))) && /* 2^x = 2^3 for example */
INTEGERP(ARG(0,ARG(0,t))) && INTEGERP(ARG(0,ARG(1,t))) &&
(numerical(ARG(0,t)) || numerical(ARG(1,t)))
)
{ if(f == '=')
{ o[i] = logbeqn; ++i; /* don't use arithmetic */
}
else
{ solve_ineq(t, i,o,nops);
return;
}
}
else if(INEQUALITY(f) && SOLVETYPE(problemtype) &&
FUNCTOR(ARG(0,t)) == '^' && FUNCTOR(ARG(1,t)) == '^' &&
isinteger(ARG(1,ARG(0,t))) && isinteger(ARG(1,ARG(1,t))) &&
!polygcd(ARG(1,ARG(0,t)),ARG(1,ARG(1,t)),&gcd) && !ONE(gcd) &&
(f == '=' || isodd(gcd) || econstant(ARG(1,t)) || econstant(ARG(0,t)))
)
{ if(equals(gcd,two) && f == '=')
{ o[i] = sqrteqn; ++i;
}
else if(f == '=')
{ o[i] = rooteqn; ++i; /* example: x^3 = 3^6; don't use arithmetic */
}
else
{ /* inequalities */
solve_ineq(t,i,o,nops);
return;
}
}
else if(!stop_arith(t) && !contains_big_exponents(t))
{ o[i] = arithmetic; ++i;
if(arithflag.complex)
{ o[i] = weakcomplexarithmetic; ++i;
}
toplevel_arithmetic = 1;
}
}
if(status(polyvalop) >= KNOWN && pathlength == 0 &&
/* pathlength == 0 ensures that this operator, which itself
traverses the term, is not tried repeatedly on subterms */
!(FUNCTOR(t) == INTEGRAL && (FUNCTOR(ARG(0,t)) == '*' || FRACTION(ARG(0,t)))) &&
!(FUNCTOR(t) == INTEGRAL && NEGATIVE(ARG(0,t))) && /* use intminus instead */
/* don't simplify an integrand e^(-t)ln(1+e^(-t))/(1+e^(-t)) because you'll
lose the chance to find u = (1+e^(-t)) */
!(FUNCTOR(t) == INTEGRAL && !contains(ARG(0,t),FUNCTOR(ARG(1,t)))) &&
/* example, integral(-c,x); use intconst rather than imitating intminus
as polyval would do. */
!contains(t,LIMIT) && /* work on the limits first; some may be undefined.*/
!contains_big_exponents(t) &&
( currenttopic != _complex_trig || !local_contains_trig(t)) &&
/* when doing complex trig, wait to simplify until all trig functions
have been expressed in complex form */
( currenttopic != _binomial_theorem || !contains_power_of_sum(t)) &&
!SOLVETYPE(problemtype) && /* it's in pre_equation after substitutions */
!obvious_trig(t) && /* a(cos^2 x + sin^2 x) + b should not be expanded */
FUNCTOR(t) != SUM && /* don't simplify sum((a/b)^n,...) to sum(a^n/b^n) */
!iscomplex(t) /* don't simplify i/i^2 to 1/i; polyvalop doesn't do complex simplification */
)
{ o[i] = polyvalop; ++i;
polyvalflag = 1;
}
else if(pathlength == 0)
{ if(SOLVETYPE(problemtype) &&
problemtype != LINEAR_EQUATION &&
problemtype != LINEAR_EQUATIONS
)
polyvalflag = 1;
else
polyvalflag = 0; /* so polyvalflag is reset when pathlength == 0 */
}
switch(f)
{ case '-' :
g = FUNCTOR(ARG(0,t));
if(g == '-')
{ o[i] = doubleminus; ++i;
}
else if(g == '+' && (pathlength < 2 || path[pathlength-2] != ROOT))
/* root(3,-(a+b)) should become - root(3,a+b), not root(3,-a-b) */
/* What about under an even root? I hope it doesn't matter much,
because from here I can't tell if the root is even or odd. To
fix this I would have to have another flag, oddrootflag, which
was set and restored in autosimp as we go into and out of
ROOT terms. It doesn't seem worth the trouble. */
{ o[i] = pushminusin; ++i;
}
if(g == '/' && get_complex() &&
(complexexpression(ARG(0,ARG(0,t))) || complexexpression(ARG(1,ARG(0,t))))
)
{ o[i] = complexform; ++i;
/* example, -(2+i)/2i goes to -1/2 + i */
}
break;
case '+' :
o[i] = dropzero; ++i;
/* catch x^(2+3) so the 2+5 changes to 5 right away */
if(pathlength >= 2 && path[pathlength-2] == '^' &&
path[pathlength-1] == 2 && numerical(t)
)
{ o[i] = arithmetic; ++i;
}
if(CALCULUS_TOPIC(currenttopic) && contains(t,INFINITY))
{ o[i] = infinityplusinfinity; ++i;
o[i] = infinityminusinfinity; ++i;
}
if(pathlength == 2 && path[0] == '=' && currenttopic != _complete_the_square)
{ /* see if the other side of the equation is zero */
q = history(get_activeline());
if(FUNCTOR(q) == '=' && ZERO(ARG(path[1]==1 ? 1 : 0, q)))
{ o[i] = contentfactor; ++i;
/* example: sin x cos x + sin^2 x = 0.
If we don't factor it now, sin^2 will become 1-cos^2
before we get to autosum, and we can't solve it. */
}
/* Example: sin x cos x + 1-cos^2 x.
We need to apply 1-cos^2 x = sin^2 x. This should
ONLY be done if the result is going to factor,
otherwise we risk an infinite regress changing
cos^2 to sin^2 and vice-versa. */
if(!sinsquare2(t,zero,&w,buffer) && !content_factor(w,&a,&b))
{ o[i] = sinsquare2; ++i;
}
if(!sinsquare3(t,zero,&w,buffer) && !content_factor(w,&a,&b))
{ o[i] = sinsquare3; ++i;
}
}
if(pathlength == 4 && path[0] == OR && path[2] == '=')
{ q = history(get_activeline());
if(FUNCTOR(q) == OR)
{ q = ARG(path[1]-1,q);
if(FUNCTOR(q) == '=' && ZERO(ARG(path[3]==1 ? 1 : 0,q)))
{ o[i] = contentfactor; ++i;
}
if(!sinsquare2(q,zero,&w,buffer) && !content_factor(w,&a,&b))
{ o[i] = sinsquare2; ++i;
}
if(!sinsquare3(q,zero,&w,buffer) && !content_factor(w,&a,&b))
{ o[i] = sinsquare3; ++i;
}
}
}
if(almost_algebraic(t) && !iscomplex(t))
/* example, (x - 1/2 - sqrt(17)/2) */
{ o[i] = polyvalop; ++i;
}
if(get_complex() && status(polyvalop) >= KNOWN && !complexexpression(t) && mvpoly2(t))
{ o[i] = polyvalop; ++i;
/* simplify polynomials in i, e.g. under SQRT */
/* but don't simplify expressions already of the form u + vi */
}
if(commondenom_in_preops(t))
{ if(status(commondenomandsimp)>= KNOWN)
{ o[i] = commondenomandsimp; ++i;
}
else
{ o[i] = commondenom; ++i;
}
}
if(!seminumerical(t) && /* don't factor algebraic numbers */
(currenttopic == _factor_by_grouping ||
currenttopic == _advanced_factoring ||
currenttopic == _cubic_one_root ||
currenttopic == _complex_cubics
) &&
( !get_complex() || !contains_complex_exponentials(t))
)
{ if(ARITY(t) == 3)
{ o[i] = factorbygrouping; ++i;
/* Must come before regroupterms.
Example: h^2k^2 + 4k^2 + (h^2k + 4k)
For arities 4 to 6 it's in autosum.c
*/
}
if(ARITY(t) == 2 && FUNCTOR(ARG(0,t)) == '*' && FUNCTOR(ARG(1,t)) == '*')
/* a(b-c) + d(c-b) = a(b-c) - d(b-c) */
{ o[i] = pullminusout2; ++i;
}
o[i] = contentfactor; ++i;
/* try it before regroupterms.
Example: 2a(a+3)-(3+a)
But for arity 3 it should come AFTER factorbygrouping.
Example: k^2(h^2 + 4) + h^2k + 4k,
we don't want to factor k out of the whole
sum but rather factor k out of the second two terms.
*/
}
if(!polyvalflag)
/* so polyvalop hasn't been thrown in when pathlength == 0 */
{ /* regroupterms will do the work of pushminusin,
which we want to prevent when regroupterms is only
LEARNING. We have regroupterms in pre_ops because
it must come before additivecancel, which also
will regroup terms (looking strange); so now we
must check that there are no negative sums
within the terms to be regrouped. */
if(status(regroupterms) > LEARNING || !block_regroupterms(t))
{ /* no negative sums, go ahead and regroup */
o[i] = regroupterms; ++i;
o[i] = additivecancel; ++i;
/* additivecancel should precede factoring, so as not to
factor x^2-x^2 for example */
}
}
/* factoring by pattern-matching comes in pre_ops, so that we
don't miss obvious cases by doing simple things first,
as in 2^3 - (3b)^3 for example. The factoring operations
are listed in factorops in factor.c and the list there must
match the total of operations given in pre_ops and autosum.
*/
if(!constant(t))
{ tflag = hypertflag = 0;
if(pathlength <= 2 && path[0] == '=')
{ set_trigflag(history(currentline),&tflag);
/* this takes both sides of the equation into account,
which is necessary to prevent one side of an
identity being written as a polynomial in sin x
while the other side gets written as a polynomial
in cos x, so the identity can't be verified.
It's probably a good thing when solving equations, too.
*/
set_hypertrigflag(history(currentline),&hypertflag);
}
else
{ set_trigflag(t,&tflag);
set_hypertrigflag(t,&hypertflag);
}
trigcount = tflag ? nbits(tflag) : 0;
if(INDICATES(tflag,SEC) && INDICATES(tflag,TAN))
{ /* example, sec^2 x - tan^2 x = 1. If we don't change the
left side to 1 in pre_ops, it will get factored. */
o[i] = secsqminustansq; ++i;
o[i] = tansquare2; ++i;
o[i] = secsqtotansq; ++i;
}
if(INDICATES(tflag,SIN) && INDICATES(tflag,COS))
{ o[i] = sinsquare1; ++i; /* sin^2 + cos^2 = 1 */
/* sinsquare1 must be applied in preops because in case
sin^2 2x + cos^2 2x + sin x
it will start using double angle formulas before postops gets the sum.
Also in integral(sin^2x + cos^2 x,x): if sinsquare1 is not
in preops, half-angle formulas will be used instead.
*/
/* Next catch expanded powers of sin^2 + cos^2, because
it looks silly to fail to factor such a thing and
make it 1, e.g. if such a thing is the integrand we
then do a long useless trig computation!
*/
if(ARITY(t) == 2)
{ /* check for sin (x-a) cos(x+a) + cos(x-a) sin(x+a) etc. */
int k = trigproducts(t);
switch(k)
{ case 0:
break;
case 1:
o[i] = sinsumrev; ++i;
break;
case 2:
o[i] = sindifrev; ++i;
break;
case 3:
o[i] = cossumrev; ++i;
break;
case 4:
o[i] = cosdifrev; ++i;
break;
}
}
if(ARITY(t) == 3 && squareofone(t))
{ o[i] = factorsquareofsum; ++i;
}
if(ARITY(t) > 3 && powerofone(t))
{ switch(ARITY(t))
{ case 4:
o[i] = factorcubeofsum; ++i;
break;
case 5:
o[i] = factor4thofsum; ++i;
break;
default:
o[i] = factornthofsum; ++i;
}
}
}
if(hypertflag &&
currenttopic != _hyperfunctions &&
hyperindicates(hypertflag,COSH) &&
hyperindicates(hypertflag,SINH)
)
{ o[i] = coshsqminussinhsq; ++i;
if(get_hflag() || get_whichpass() > 1)
/* hflag nonzero means the problem already contains
exponentials. These operations introduce exponentials
and so can screw up hyper-trig identities that otherwise
can be proved without introducing exponentials.
*/
{ o[i] = coshplussinh; ++i;
o[i] = coshminussinh; ++i;
}
}
if(hypertflag &&
currenttopic != _hyperfunctions &&
hyperindicates(hypertflag,COSH)
)
{ o[i] = coshsqminus1; ++i;
}
if(hypertflag &&
currenttopic != _hyperfunctions &&
hyperindicates(hypertflag,SINH)
)
{ o[i] = sinhsqplus1; ++i;
}
if(hypertflag &&
currenttopic != _hyperfunctions &&
hyperindicates(hypertflag,TANH)
)
{ o[i] = oneminustanhsq; ++i;
}
if(hypertflag &&
currenttopic != _hyperfunctions &&
hyperindicates(hypertflag,SECH)
)
{ o[i] = oneminussechsq; ++i;
}
}
if(pathlength >= 2 && path[pathlength-2] == '^' &&
n == 2 && iscomplex(ARG(1,t)) &&
contains(ARG(0,t),COS) && contains(ARG(1,t),SIN)
)
{ o[i] = rectangulartopolar; ++i;
/* example: cos(pi/4) + i sin(pi/4); this must be
in pre_ops or else cos(pi/4) gets evaluated
before we convert to polar form, resulting in
an ugly arctan computation when we do get to
polar form. */
}
if(n == 2 &&
(SIGNEDFRACTION(ARG(0,t)) || SIGNEDFRACTION(ARG(1,t))) &&
complexnumerical(t) &&
(contains(t,SQRT) || contains(t,ROOT)) &&
currenttopic != _add_numerical_fractions &&
!contains(t,'^') && /* don't work on 2^(1/2)/3^(1/2) + sqrt 4 to
simplify the sqrt(4) */
!iscomplex(t)
)
{ o[i] = polyvalop; ++i;
/* simplify 1/2 + sqrt(5)/2 before commondenom gets it.
This will get surdsimp used. */
}
if(inhibited(alltoleft) /* set by completethesquare */
||
(
(FACTORFLAG ||
(problemtype == TRIG_IDENTITY && pathlength == 2 &&
common_variables(ARG(0,t),ARG(1,t))
/* This clause should not cause factoring of
sin^t x - sin^2 y, or even x^2 - y^2; this clause
is needed for more complicated situations */
)
) &&
/* allow factoring at toplevel when solving identities. */
!constant(t) &&
(!get_infractionflag() || !contains_fract(t)) &&
/* don't factor (1- y^2/x^2) in the num or denom of a fraction,
instead wait for it to be put over a common denom */
!suppress_factoring(problemtype,0) &&
!(SOLVETYPE(problemtype) && count_abs(t) >= 2) &&
!(trigcount && trig_suppress_factoring(trigcount,t))
/* trig functions with different non-constant args?
or more than one trig function but all to even powers?
if so don't factor. */
)
)
/* don't !irreducible(t) as it probably takes
longer than trying these simple operators */
{ if(n==2 && !insqrtflag && !inrootflag && !infractexpflag)
/* don't factor in �(x^2-a^2), because it
blocks finding substitutions you need
to solve some equations */
{ int j,flag=0,signflag=0,oneflag=0;
term trigarg;
unsigned short g;
/* 1-sin^2 = cos^2 must be tried before factoring */
/* and also two similar csc identities */
for(j=0;j<n;j++)
{ u = ARG(j,t);
if(NEGATIVE(u))
{ ++signflag;
u = ARG(0,u);
}
if(ONE(u))
{ ++oneflag;
continue;
}
if(FUNCTOR(u) == '^' && equals(ARG(1,u),two))
{ g = FUNCTOR(ARG(0,u));
if(g==SIN || g==COS || g==TAN || g==COT || g==CSC || g==SEC)
{ if(!flag)
{ ++flag;
trigarg = ARG(0,ARG(0,u));
}
else if(flag && equals(ARG(0,ARG(0,u)),trigarg))
++flag;
}
}
}
if(flag==1 && oneflag==1)
{ if(g==SIN)
{ if(get_trigpolyflag() != COS)
{ o[i] = sinsquare2; ++i;
}
}
else if(g==COS)
{ if(get_trigpolyflag() != SIN)
{ o[i] = sinsquare3; ++i;
}
}
else if(g == CSC)
{ if(get_trigpolyflag() != COT)
{ o[i] = cotsquare2; ++i;
}
}
}
if(flag == 2 && signflag == 1 &&
(g == CSC || g == COT)
)
{ o[i] = cscsqminuscotsq; ++i;
}
if(flag == 2 && signflag == 1 &&
( g == SIN || g == COS) &&
get_trigpolyflag() != COS
)
{ o[i] = sinsqtocossq; ++i;
}
if(!stop_differenceofsquares(t))
{ o[i] = differenceofsquares; ++i;
}
if(get_complex() && SOLVETYPE(problemtype))
/* don't use this in num and denom of fractions
when simplifying */
{ o[i] = sumofsquares; ++i;
}
if(!flag) /* no exponents of two */
{ term a = ARG(0,t);
term b = ARG(1,t);
if(NEGATIVE(a))
a = ARG(0,a);
if(NEGATIVE(b))
b = ARG(0,b);
/* We block factoring on the very special case
(p+q)^3 + (p-q)^3, which is better handled by
direct binomial expansion.
*/
if( ! (
FUNCTOR(a) == '^' && FUNCTOR(ARG(0,a)) == '+' && equals(ARG(1,a),three) &&
FUNCTOR(b) == '^' && FUNCTOR(ARG(0,b)) == '+' && equals(ARG(1,b),three) &&
ARITY(ARG(0,a)) == 2 && ARITY(ARG(0,b)) == 2 &&
equals(ARG(0,ARG(0,a)),ARG(0,ARG(0,b))) &&
(
( NEGATIVE(ARG(1,ARG(0,b))) && equals(ARG(1,ARG(0,a)),ARG(0,ARG(1,ARG(0,b))))) ||
( NEGATIVE(ARG(1,ARG(0,a))) && equals(ARG(1,ARG(0,b)),ARG(0,ARG(1,ARG(0,a)))))
)
)
)
{ o[i] = differenceofcubes; ++i;
o[i] = sumofcubes; ++i;
o[i] = sumoffourthpowers; ++i;
}
}
}
else if(n==3 ||
(n==6 && bivariate(t)) ||
/* n==6 because of x^2+2xy+y^2+3x+3y+2 */
/* ok to use these under sqrt */
(n==4 && bivariate(t))
/*n==4 because of x^2+2x+1 - y^2 */
)
{
o[i] = factorsquareofsum; ++i;
o[i] = factorsquareofdif; ++i;
}
/* You can't restrict the arity on factorquadratic to 3
because the constant term could be a sum; but
you shouldn't use it on arity 2, although it
gives correct output, doing so makes Mathpert
suggest its use as a hint instead of
contentfactor */
if(n > 2 &&
currenttopic != _quadratic_formula &&
currenttopic != _complete_the_square &&
( (!intflag && !indexflag && !seriesflag) || (!inrootflag && !insqrtflag && !infractexpflag))
)
/* Don't factor quadratics under a root or sqrt or fractexp
inside an integral or indexed sum, because (in integrals) it
blocks completethesquare, which is necessary, and (in sums)
it's useless anyway. */
{ o[i] = factorquadratic; ++i;
/* factorquartic is in postops so it won't be
used on x^4 - 2x^2y^2 + y^4 */
}
switch(n)
{ case 2:
case 3:
break;
case 4:
o[i] = factorcubeofsum; ++i;
break;
case 5:
o[i] = factor4thofsum; ++i;
break;
default:
o[i] = factornthofsum; ++i;
break;
}
if(n==4 &&
SOLVETYPE(problemtype) &&
stricttrigpoly(t,get_eigenvariable())
)
/* example: 2 sin x tan x + tan x - 2 sin x -1 = 0 */
{ o[i] = factorbygrouping; ++i;
}
if(n==4 && get_nvariables() > 1)
{ o[i] = factorhelper; ++i;
}
}
if(!toplevel_arithmetic && !contains_big_exponents(t))
/* so arithmetic hasn't been thrown in above */
{ if(arithflag.negexp)
{ o[i] = arithmetic; ++i;
}
else /* don't do too much arithmetic! */
/* we hit this under topic _radicals */
/* for example don't evaluate powers in a sum */
/* but DO work on 1 + 2 + �3 */
{ numberflag = 0;
for(j=0;j<n;j++)
{ u = ARG(j,t);
if(NUMBER(u))
++numberflag;
}
if(numberflag > 1)
{ o[i] = arithmetic; ++i;
}
}
}
if(arithflag.complex)
{ o[i] = weakcomplexarithmetic; ++i;
}
/* use cos^2-1 => sin^2 and csc^2-1 => cot^2 and
secsqtotansq when not
in fractions, when these expressions stand alone;
otherwise, we'll get sin^2 = 1-cos^2 => sin^2 = 1-(1-sin^2)
when post_ops hits the cos^2. But don't do this in a
fraction as you may need to factor and cancel. And
don't do it if the whole currentline contains only
one trig function. */
if(!get_infractionflag() && ARITY(t)==2 && ONE(ARG(0,t)) &&
NEGATIVE(ARG(1,t)) &&
FUNCTOR(ARG(0,ARG(1,t))) == '^' &&
equals(ARG(1,ARG(0,ARG(1,t))),two)
)
{ unsigned short h = FUNCTOR(ARG(0,ARG(0,ARG(1,t))));
switch (h)
{ case COS:
if(
!((trigexpandflag & (1<<12)) && !INDICATES(tflag,SIN))
)
/* Not the case that (only sin and cos occur and sin
does not occur), meaning it's not the case that
everything is already expressed in terms of cos */
{ o[i] = sinsquare3; ++i;
}
break;
case CSC:
o[i] = cotsquare2; ++i;
break;
case SEC:
o[i] = tansquare2; ++i;
break;
}
}
if(!get_infractionflag() && ARITY(t)==2 && equals(ARG(1,t),minusone) &&
FUNCTOR(ARG(0,t)) == '^' && equals(ARG(1,ARG(0,t)),two)
)
{ unsigned short h = FUNCTOR(ARG(0,ARG(0,t)));
switch (h)
{ case COS:
o[i] = sinsquare3; ++i;
break;
case CSC:
o[i] = cotsquare2; ++i;
break;
case SEC:
o[i] = tansquare2; ++i;
break;
}
}
/* The following operators make e.g. sin 3x + sin 5x into
a product of trig functions. When should they be used?
The conditions are complicated and part of the problem
is to decide, in the case of a sum of arity 3, which
two args it should be applied to. This decision must be
made by the operators themselves. Function trigfactor()
in this file checks some necessary conditions.
The operators themselves fail in auto mode if they would
create fractional args, unless we're solving equations or
inequalities. For example, sin(2x) - sin x would create
a term in sin(3x/2), which we don't want when solving
identities, but DO want when solving equations, e.g.
sin(3x)-sin(2x)=0 goes much easier if we use difofsin.
*/
if( (trigexpandflag & 1) && /* not all trig functions have same arg */
!limitflag && !intflag && !indexflag && !seriesflag && !difflag &&
(ARITY(t) == 2 || ARITY(t) == 3) &&
currenttopic != _trig_product
/* on this topic, the point is to create sums of trig functions,
and using sumofsin etc. creates loops. */
)
{ j = trigfactor(t);
if( currenttopic == _trig_factor ||
/* sin alpha + sin beta and so on occur in the
problem sets under that topic and trigfactor
won't suggest using sumofsin, but on this topic
it should always be used. */
(j > 0 && ARITY(t) == 3)
)
{ o[i] = sumofsin; ++i;
o[i] = difofsin; ++i;
o[i] = sumofcos; ++i;
o[i] = difofcos; ++i;
}
else if(j && ARITY(t) == 2)
{ switch(j)
{ case 1:
o[i] = sumofsin; ++i;
break;
case 2:
o[i] = difofsin; ++i;
break;
case 3:
o[i] = sumofcos; ++i;
break;
case 4:
o[i] = difofcos; ++i;
break;
}
}
}
if(INDICATES(tflag,COS) &&
!indexflag &&
!seriesflag &&
!intflag && /* don't change cos 2t to anything involving sin^2 or cos^2
inside an integral. In fact we do the opposite to solve
trig power integrals, so this would cause a loop. */
((tflag & 0x1000) || currenttopic == _double_angle) &&
(trigexpandflag & 1) && /* not all trig functions have same arg */
currenttopic != _trig_product && /* leave cos(2t) alone then */
!trignegative(t) /* don't touch cos(-2x) - cos(2x) for example
because the cos(-2x) will be worked on soon */
)
{ /* Be careful about which double cosine rule to apply. Is there
a sin term already in this sum? */
long oneflag=0, cosflag=0;
if(INDICATES(tflag,SIN) &&
(problemtype != TRIG_IDENTITY || get_trigpolyflag() != SIN)
)
{ o[i] = doublecos6; ++i; /* cos 2� - 1 = - 2 sin^2 � */
}
/* Is there a 1 in this sum with the same sign as the cosine term ? */
for(j=0;j<ARITY(t);j++)
{ u = ARG(j,t);
if(ISINTEGER(u))
oneflag = INTDATA(u);
if(NEGATIVE(u) && ONE(ARG(0,u)))
oneflag = -INTDATA(u);
if(FUNCTOR(u) == COS && FUNCTOR(ARG(0,u)) =='*' && iseven(ARG(0,u)))
cosflag = 1;
if(NEGATIVE(u) && FUNCTOR(ARG(0,u)) == COS &&
FUNCTOR(ARG(0,ARG(0,u))) == '*' && iseven(ARG(0,ARG(0,u)))
)
cosflag = -1;
/* what about 2 - 2 cos 2x */
if(FUNCTOR(u) == '*' && ISINTEGER(ARG(0,u)) &&
FUNCTOR(ARG(1,u)) == COS &&
FUNCTOR(ARG(0,ARG(1,u))) == '*' &&
iseven(ARG(0,ARG(1,u)))
)
cosflag = INTDATA(ARG(0,u));
if(NEGATIVE(u) && FUNCTOR(ARG(0,u)) == '*' &&
FUNCTOR(ARG(1,ARG(0,u))) == COS &&
FUNCTOR(ARG(0,ARG(1,ARG(0,u)))) == '*' &&
iseven(ARG(0,ARG(1,ARG(0,u))))
)
cosflag = -INTDATA(ARG(0,ARG(0,u)));
}
if(cosflag * oneflag > 0)
{ o[i] = doublecos5; ++i; /* cos 2� + 1 = 2cos^2 � */
}
else if(cosflag * oneflag < 0 &&
( problemtype != TRIG_IDENTITY || get_trigpolyflag() != SIN)
)
{ o[i] = doublecos6; ++i; /* cos 2� - 1 = - 2 sin^2 � */
}
else if(problemtype!= TRIG_IDENTITY || !get_trigpolyflag())
{ o[i] = doublecos4; ++i; /* cos 2� = cos^2 � - sin^2 � (must be tried last in auto mode)*/
}
/* These have to be done in pre-ops or they'll get
preempted when postops hits the cosine term. */
}
for(k=0;k<n;k++)
{ u = ARG(k,t);
if(NEGATIVE(u))
u = ARG(0,u);
if(!FRACTION(u))
break;
}
if(k==n &&
(!limitflag || (pathlength >= 2 && path[pathlength-2]!= LIMIT)) &&
/* don't add fractions when limsum could be used. If the
individual limits are indeterminate then eventually
commondenom will be used to add them. */
(
!iscomplex(t) ||
n > 2 ||
complex_addfractions_conditions(t)
)
)
{ /* all fractions */
o[i] = addfractions; ++i;
/* e.g. in common denom problems, if the denoms are the
same or ESPECIALLY if they differ only by a sign
as in 1-x^2 and x^2-1, add the fractions before
factoring the denoms and looking for a common denom. */
}
if(problemtype == INTEGRATION && INDICATES(tflag,ATAN) && INDICATES(tflag,TAN))
{ o[i] = atantan2; ++i; /* atan (tan x) = x+c1 */
}
/* Are there two logarithms which ought to be collected before
something happens to block the collection?
Examples: log(6x) - log x
log((x+a)^2) - log(x^2-a^2)
*/
if( (g = twologs(t)) != 0 && forced_logcollect(g,t))
{ if(g == LN)
{ o[i] = collectlns2; ++i;
o[i] = collectlns; ++i;
o[i] = attractlns; ++i;
}
else if (g == LOG)
{ o[i] = collectlogs2; ++i;
o[i] = collectlogs; ++i;
o[i] = attractlogs; ++i;
}
else if(g == LOGB)
{ o[i] = collectlogb2; ++i;
o[i] = collectlogb; ++i;
o[i] = attractlogb2; ++i;
}
}
*nops = i;
return;
case '*' :
pre_product(t,o+i,&k);
*nops = i+k;
return;
case '^' :
u = ARG(0,t);
v = ARG(1,t);
if(ISINTEGER(u) && FUNCTOR(v) == '*' && ISINTEGER(ARG(0,v)))
/* example: 2^(2n) becomes (2^2)^n which will become 4^n;
powertopower will not be used in preops on (2^2)^n so
no loop will arise.
*/
{ o[i] = reversepowertopower1; ++i;
}
if(FRACTION(u) && ZERO(ARG(1,u)) && iseven(v))
/* (1/0)^2 becomes 1/0^2 which becomes infinity, not undefined */
{ o[i] = quotienttopower; ++i;
}
if(ZERO(v))
{ o[i] = zeroexponent; ++i;
*nops = i;
return;
}
else if(ONE(v))
{ o[i] = unitexponent; ++i;
*nops = i;
return;
}
if(ZERO(u))
{ o[i] = zerobase; ++i;
*nops = i;
return;
}
if(ONE(u))
{ o[i] = unitbase; ++i;
*nops = i;
return;
}
if(INTEGERP(u) && ONEHALF(v) && radicalflag >= 0)
{ /* change 2^(1/2) to sqrt(2) */
o[i] = exponenttosqrt; ++i;
}
if(equals(u,complexi))
{ o[i] = defnofi; ++i; /* i^2 = -1 */
o[i] = powersofi0; ++i; /* i^(4n) = 1 */
o[i] = powersofi1; ++i; /* i^(4n+1) = i */
o[i] = powersofi2; ++i; /* i^(4n+2) = -1 */
o[i] = powersofi3; ++i; /* i^(4n+3) = -i */
}
if(INTEGERP(u) && contains(v,LOGB))
{ o[i] = writeintegeraspower; ++i;
}
if(equals(u,eulere))
{ if(currenttopic != _polar_form)
{ o[i] = etotheipi; ++i;
o[i] = etotheminusipi; ++i;
}
o[i] = etothei2npi; ++i;
o[i] = etothecoterminal; ++i;
if(constant(v) && contains(v,LN))
/* example, e^(2 ln 2) should get simplified to 2^2;
but we don't want to loop with introducelninexponent,
which makes x^y into e(y ln x). */
{ if(FUNCTOR(v) == LN)
{ o[i] = lninexponent; ++i;
}
else if(FUNCTOR(v) == '*')
{ o[i] = lninexponent2; ++i;
}
}
}
/* catch e^(- ln x) */
if(equals(u,eulere) && NEGATIVE(ARG(1,t)) &&
FUNCTOR(ARG(0,v)) == LN
)
{ o[i] = eliminatenegexp; ++i;
*nops = i;
return;
}
/* catch 10^(- log x) */
if(equals(u,ten) && NEGATIVE(v) &&
FUNCTOR(ARG(0,v)) == LOG
)
{ o[i] = eliminatenegexp; ++i;
*nops = i;
return;
}
/* catch b^(- log(b,x) */
if(ATOMIC(u) && NEGATIVE(v) &&
FUNCTOR(ARG(0,v)) == LOGB
&& equals(ARG(0,ARG(0,v)),u)
)
{ o[i] = eliminatenegexp; ++i;
*nops = i;
return;
}
if(NEGATIVE(v) && RATIONALP(ARG(0,v)) &&
FUNCTOR(u) == '+' &&
pathlength >= 2 &&
path[pathlength-2] == INTEGRAL &&
path[pathlength-1] == 1
)
{ o[i] = eliminatenegexp; ++i;
/* example, integral((1+e^(-x))^(-1/2),x). This
should become integral(1/(1+e^(-x))^(1/2),x), or
else it won't complete in auto mode. This opens the
way for a trig substitution. */
}
/* catch x^(p/log(b,x)) */
if(FRACTION(v) && FUNCTOR(ARG(1,v)) == LOGB && equals(ARG(1,ARG(1,v)),u))
{ o[i] = introducelogbinexponent; ++i;
}
if(FRACTION(v) && FUNCTOR(ARG(1,v)) == LOG && equals(ARG(0,ARG(1,v)),u))
{ o[i] = introduceloginexponent; ++i;
}
if(FRACTION(v) && FUNCTOR(ARG(1,v)) == LN && equals(ARG(0,ARG(1,v)),u))
{ o[i] = introducelninexponent; ++i;
}
if(FRACTION(v) && FUNCTOR(ARG(1,v)) == '*')
{ for(j=0;j<ARITY(ARG(1,v));j++)
{ if(FUNCTOR(ARG(j,ARG(1,v))) == LOGB && equals(ARG(1,ARG(j,ARG(1,v))),u))
{ o[i] = introducelogbinexponent; ++i;
break;
}
if(FUNCTOR(ARG(j,ARG(1,v))) == LN && equals(ARG(0,ARG(j,ARG(1,v))),u))
{ o[i] = introducelninexponent; ++i;
}
if(FUNCTOR(ARG(j,ARG(1,v))) == LOG && equals(ARG(0,ARG(j,ARG(1,v))),u))
{ o[i] = introduceloginexponent; ++i;
}
}
}
if(NEGATIVE(u) && ONE(ARG(0,u))) /* (-1)^n */
{ if(NEGATIVE(v) && status(powerofminusone)<=LEARNING)
{ o[i] = eliminateconstnegexp; ++i;
}
o[i] = intpowerofminusone; ++i;
o[i] = powerofminusone; ++i;
}
if(ISINTEGER(u) && FRACTION(v) &&
ISINTEGER(ARG(1,v)) && ISINTEGER(ARG(0,v)) &&
/* don't fool around with bignums here */
!ZERO(ARG(1,v)) /* zero denom can arise after evaluating
a limit. Example: 3^(1/0) */
)
{ if(equals(ARG(1,v),two))
{ if(perfect_square(INTDATA(u)) &&
radicalflag < 0 /* sqrts will be converted to fractexps
so go ahead and factor under exponents */
)
{ o[i] = factorundersqrt; ++i;
*nops = i;
return;
}
}
if(perfect_power(INTDATA(u),INTDATA(ARG(1,v))) &&
radicalflag <= 0 /* 0 is allowed here and not just above
because in the above case, backtosqrts
will be called, but not here. */
)
{ o[i] = factorunderroot; ++i;
*nops = i;
return;
}
}
if(INTEGERP(u) && INTEGERP(v) && !toplevel_arithmetic &&
(currenttopic == _numerical_exponents || !contains_big_exponents(t))
)
{ o[i] = arithmetic; ++i;
/* otherwise we never even evaluate 2^3 in topic numerical exponents */
}
g = FUNCTOR(u);
switch(g)
{ case SQRT:
o[i] = powerofsqrt; ++i;
if(radicalflag == -1)
{ o[i] = powersqrtexp; ++i;
}
break;
case ROOT:
o[i] = powerofroot; ++i; /* (��x)� = x */
o[i] = powerofroot4; ++i; /* root(mn,x)^n = root(m,x) */
/* this will also produce sqrt, imitating powerofroot4,
so there's no need to call that separately */
/* powerofroot2 and powerofroot3 are in postops.c */
if(radicalflag == -1)
{ o[i] = powerrootexp; ++i;
}
break;
case '^':
if(!ISINTEGER(ARG(0,u)) || !ISINTEGER(ARG(1,u)))
/* but leave (2^2)^n alone so it can become 4^n */
{ o[i] = powertopower; ++i;
}
if(
(ISINTEGER(ARG(1,u)) && RATIONALP(v)) ||
(ISINTEGER(v) && RATIONALP(ARG(1,u))) ||
(RATIONALP(ARG(1,u)) && RATIONALP(v))
)
{ /* example: (2^3)^(1/3) */
term temp;
value(product(ARG(1,u),v),&temp);
if(ISINTEGER(temp))
{ o[i] = powertopower; ++i;
}
}
break;
case COS:
if((trigexpandflag & 1) && FRACTION(ARG(0,u)))
{ o[i] = cossqhalf; ++i;
}
if(NEGATIVE(ARG(0,u)))
{ o[i] = cossqeven; ++i;
}
break;
case SIN:
if((trigexpandflag & 1) && FRACTION(ARG(0,u)))
{ o[i] = sinsqhalf; ++i;
}
if(NEGATIVE(ARG(0,u)))
{ o[i] = sinsqeven; ++i;
}
break;
case TAN:
if(NEGATIVE(ARG(0,u)))
{ o[i] = tansqeven; ++i;
}
break;
case SEC:
if(NEGATIVE(ARG(0,u)))
{ o[i] = secsqeven; ++i;
}
break;
case CSC:
if(NEGATIVE(ARG(0,u)))
{ o[i] = cscsqeven; ++i;
}
break;
case '+': /* catch (sin x + cos x)^2 */
if(ARITY(u) == 2 &&
(
(FUNCTOR(ARG(0,u)) == SIN && FUNCTOR(ARG(1,u)) == COS) ||
(FUNCTOR(ARG(0,u)) == COS && FUNCTOR(ARG(1,u)) == SIN)
) &&
equals(ARG(0,ARG(0,u)),ARG(0,ARG(1,u)))
)
{ o[i] = squareofsum; ++i;
}
break;
}
if(!ATOMIC(v))
{ h = FUNCTOR(v);
if(!ATOMIC(u) &&
FUNCTOR(u) == '/' &&
FUNCTOR(v) == '-'
)
{ o[i] = negexpofquotient; ++i;
*nops = i;
return;
}
if(
(get_polyvalnegexpflag() == -1 ||
(!equals(u,eulere) && !get_infractionflag() && get_polyvalnegexpflag() <=0) ||
/* because it looks silly to leave x^(-3) + 1/x^2 alone */
/* but don't eliminate negative exponents of e because they
don't look silly. */
(get_polyvalnegexpflag() == 2 && !iscomplex(t)) ||
( limfractflag == -1 /* in a fraction in a limit */
&& ( (pathlength >= 2 && path[pathlength-2] == '+') ||
(pathlength >= 4 && path[pathlength-4] == '+' && path[pathlength-2] == '-')
)
/* e.g. lim(h->0,((3+h)^-1 - 3^-1)/h) */
)
)
&& !difflag && !intflag && !indexflag && !seriesflag
)
{ if(NEGATIVE(u))
{ o[i] = minustopower; ++i;
} /* example: (-(1/2))^(-3) */
else
{ o[i] = eliminateconstnegexp; ++i;
}
}
if(!SOLVETYPE(problemtype))
{ switch(h)
{ case LOG:
o[i] = loginexponent; ++i;
break;
case LN :
o[i] = lninexponent; ++i;
break;
case LOGB:
o[i] = logbinexponent; ++i;
break;
case '*' :
if(!difflag && !intflag && !limitflag &&
contains_at_toplevel(v,LN)
)
/* leave the base constant when differentiating
or integrating */
{ o[i] = lninexponent2; ++i;
}
if(!difflag && contains_at_toplevel(v,LOG))
{ o[i] = loginexponent2; ++i;
}
if(contains_at_toplevel(v,LOGB))
{ o[i] = logbinexponent2; ++i;
}
break;
}
}
}
if( !(pathlength >=2 &&
(path[pathlength-2] == LOGB ||
path[pathlength-2] == LOG ||
path[pathlength-2] == LN
)
) &&
(
(INTEGERP(u) && INTEGERP(v) && !contains_big_exponents(t))
|| (arithflag.negexp && !INTEGERP(v))
/* arithflag.negexp is set only for advanced problem
types; example: 4^(5-3) will be done in one
step when this is set, instead of two */
|| equals(u,minusone) /* otherwise (-1)^2 won't be done */
)
)
{ o[i] = arithmetic; ++i;
}
if(arithflag.complex &&
(
(equals(u,complexi) && get_complex() && INTEGERP(v))
|| arithflag.negexp
)
)
{ o[i] = weakcomplexarithmetic; ++i;
}
break;
case '/' :
num = ARG(0,t);
denom = ARG(1,t);
g = FUNCTOR(num);
h = FUNCTOR(denom);
if(FUNCTOR(denom) == '^' && ZERO(ARG(0,denom)))
{ if(equals(ARG(1,denom),two))
{ if(equals(num,infinity))
{ o[i] = infinityoverzerosq; ++i;
}
else if(!NOTDEFINED(num))
{ o[i] = zerosqdenom; ++i;
o[i] = zerosqdenom2; ++i;
}
}
else if(iseven(ARG(1,denom)))
{ if(equals(num,infinity))
{ o[i] = infinityoverzero2n; ++i;
}
else if(!NOTDEFINED(num))
{ o[i] = zero2ndenom; ++i;
o[i] = zero2ndenom2; ++i;
}
}
}
if(ZERO(denom))
{ if(equals(num,infinity))
{ o[i] = infinityoverzero;++i;
o[i] = infinityoverzero2;++i;
o[i] = infinityoverzero3;++i;
}
else if(!contains_zero_denom(num) &&
!contains(num,UNDEFINED) &&
!contains(num,INFINITY)
)
{ o[i] = zerodenom; ++i;
o[i] = zerodenom2; ++i;
o[i] = zerodenom3; ++i;
}
if(g=='-')
{ o[i] = minusoutfromnum; ++i;
}
if(g == '+')
{ o[i] = minusoutfromnum2; ++i;
}
*nops = i;
return;
}
if(ISZERO(num))
{ o[i] = zeronum; ++i;
*nops = i;
return;
}
if(ISONE(denom))
{ o[i] = unitdenom; ++i;
*nops = i;
return;
}
if(ISONE(num) && FRACTION(denom))
{ o[i] = invertandmultiply2; ++i;
*nops = i;
return;
}
if(get_complex() &&
(complexexpression(num) &&
(!complexexpression(denom) || !contains(denom,'+'))
)
)
{ o[i] = complexform; ++i;
/* example: (2+i)/2i -> 1/2 -i */
/* but we don't want to use it on )1+i)/(1-i) etc.,
where we need to show more steps */
}
if(get_complex() &&
(
(h == '^' && equals(ARG(0,denom),eulere) && iscomplex(ARG(1,denom))) ||
(h == '*' && contains(denom,'e') && iscomplex(denom))
)
)
{ o[i] = complexexptonum; ++i;
}
if(contains_fract(denom) && contains_fract(num))
{ o[i] = differenceofsquares; ++i;
}
x = get_eigenvariable();
/* when integrating 1/quadratic, it's better to complete the
square rather than factor by quadratic formula and then
use partial fractions. After completing the square and
substituting you get something that integrates to a ratio of
logs. However, for other rational functions you go ahead and
factor and use partial fractions, if a substitution doesn't
work first.
*/
if(intflag && path[pathlength-2]==INTEGRAL &&
FUNCTOR(num) == TAN && FUNCTOR(denom) == SEC
&& equals(ARG(0,num),ARG(0,denom))
)
{ /* rewrite tan x/sec x as cos x tan x, so it will
then go to sin x, when it's the integrand.
Otherwise this integral will be done by u = sec x,
which is unecessarily fancy. */
o[i] = secrecip; ++i;
}
if(intflag && ONE(num) &&
ispolyin(denom,x) &&
path[pathlength-2]==INTEGRAL
)
{ makepoly(denom,x,&v);
if(DEGREE(v) == 2)
{ o[i] = completethesquare1; ++i;
*nops = i;
return;
}
}
if(g == LIMIT && INTEGERP(denom))
{ o[i] = pulloutrational; ++i;
*nops = i;
return;
}
if(g == '*' && h != '/' &&
!(h == '*' && contains_at_toplevel(denom,'/'))
)
{ for(j=0;j<ARITY(num);j++)
{ if(FRACTION(ARG(j,num)))
{ o[i] = compoundfractions4; ++i;
*nops = i;
return;
}
}
}
if(g ==h)
switch(g)
{ case '-' :
o[i] = cancelminusinquotient; ++i;
break;
case '+' : /* (x-y)/(x-�y) should get factored */
if(!(trigexpandflag & 8))
/* don't factor if all trig functions occur
to even powers */
{ o[i] = differenceofsquares; ++i;
}
if(ARITY(num) == 2 && ARITY(denom) == 2 &&
(trigexpandflag & 1) /* not all trig functions have same arg */
)
{ o[i] = trigsuminfraction; ++i;
/* Example: (cos 2x - cos 6x)/(sin 6x - sin 2x),
we need to apply difofcos in the num and difofsin
in the denom, but if we don't do it here, then
difofsin is applied in the denom and the resulting
cos 2x is rewritten sin^2 - cos^2, ruining the
solution. */
}
break;
case '^':
if(intflag && path[pathlength-2]==INTEGRAL &&
FUNCTOR(ARG(0,num))==SIN &&
FUNCTOR(ARG(0,denom)) == COS &&
equals(ARG(1,num),ARG(1,denom)) &&
ISATOM(ARG(0,ARG(0,num))) &&
equals(ARG(0,ARG(0,num)),ARG(0,ARG(0,denom)))
)
{ /* integrating sin� / cos� */
o[i] = tanrule2; ++i;
}
if(intflag && path[pathlength-2]==INTEGRAL &&
FUNCTOR(ARG(0,num))== COS &&
FUNCTOR(ARG(0,denom)) == SIN &&
equals(ARG(1,num),ARG(1,denom)) &&
ISATOM(ARG(0,ARG(0,num))) &&
equals(ARG(0,ARG(0,num)),ARG(0,ARG(0,denom)))
)
{ /* integrating cos�/sin� */
o[i] = cotrule2; ++i;
}
if(RATIONALP(ARG(1,num)) &&
equals(ARG(1,num),ARG(1,denom)) &&
get_polyvalnegexpflag() != 1 &&
FUNCTOR(ARG(0,num)) != '^' &&
FUNCTOR(ARG(0,denom)) != '^'
/* example, if denom is (2^3)^1/3 we don't
want to bring the 1/3 outside the fraction,
rather we want to wait till (2^3)^(1/3) becomes 2
*/
)
{ o[i] = poweroutoffraction; ++i;
}
break;
case SQRT:
if(INTEGERP(ARG(0,num))
&& INTEGERP(ARG(0,denom))
&& topflag
&& nsquarefree(ARG(0,num))
&& nsquarefree(ARG(0,denom))
)
{ o[i] = quotientofsqrts; ++i;
}
break;
/* Example: �(2/3) is left alone but �(2/3) + 5 is
rewritten �2/�3 + 5 = (�2 + 5�3)/�3 = (�6 + 15) /3 */
case ROOT:
if(equals(ARG(0,num),ARG(0,denom))
&& INTEGERP(ARG(1,num))
&& INTEGERP(ARG(1,denom))
&& topflag
&& rootfree(ARG(1,num),(unsigned) INTDATA(ARG(0,num)))
&& rootfree(ARG(1,denom),(unsigned) INTDATA(ARG(0,denom)))
)
/* chkinput ensures that indices of roots are
at most 0xffff, so the cast to unsigned is
harmless, as no operator creates roots with
large indices. */
{ o[i] = quotientofroots; ++i;
}
break;
}
if(g == SIN && h == '+' && !cancel(ARG(0,num),two,&w,&q) && (trigexpandflag & 1))
{ o[i] = tanhalf1rev; ++i; /* sin(2u)/(1+cos(2u)) = tan u */
o[i] = cothalf2rev; ++i; /* sin(2u)/(1-cos(2u)) = cot u */
}
if(h == SIN && g == '+' && !cancel(ARG(0,denom),two,&w,&q) && (trigexpandflag & 1))
{ o[i] = tanhalf2rev; ++i; /* (1-cos 2u)/sin(2u) = tan u */
o[i] = cothalf1rev; ++i; /* (1+cos 2u)/sin(2u) = cot u */
}
if(g == '/' || h == '/')
{ o[i] = compoundfractions1; ++i; /*(a/c)/(b/c) = a/b */
}
if(g == '+' && !iscomplex(num))
/* (-a-b)/c = -(a+b)/c, but not (-a-bi)/c = -(a+bi)/c,
because the latter must become -a/c - (b/c) i
*/
{ o[i] = minusoutfromnum2; ++i;
}
if(h == '+' && contains(denom, SQRT) && numerical(t))
/* bring algebraic numbers to standard form */
{ o[i] = minusoutfromdenom2; ++i;
}
if(h == '*')
/* get rid of compound fractions in the denominator
before using cancelgcd */
{ int kk;
unsigned short mm = ARITY(denom);
for(kk=0;kk<mm;++kk)
{ if(FRACTION(ARG(kk,denom)))
break;
}
if(kk < mm)
{ o[i] = invertandmultiply; ++i;
}
}
if(status(eliminatenegexp) > LEARNING && !stopcancel(t))
{ o[i] = cancelop; ++i;
statusflag = 0;
if(contains_sqrt(num) &&
!(numerical(t)) && /* leave sqrt(2)/2 alone */
( pathlength <= 1 || problemtype >= LIMITS)
/* You need cancelsqrt2 desparately in L'Hopital's rule problems */
)
{ o[i] = cancelsqrt2; ++i;
o[i] = cancelroot2; ++i;;
}
if(contains_sqrt(denom))
{ o[i] = cancelsqrt; ++i;
o[i] = cancelroot; ++i;
}
}
else
statusflag = 1;
if(arithflag.negexp || !contains(t,'^'))
{ o[i] = arithmetic; ++i;
}
if(arithflag.complex)
{ o[i] = weakcomplexarithmetic; ++i;
}
if(
(get_polyvalnegexpflag() <= 0 && !difflag && !intflag) ||
(
get_polyvalnegexpflag() == 2 &&
!difflag && !intflag &&
!iscomplex(ARG(0,t))
)
)
/* by default eliminate neg exponents
in numerators of fractions, except
if those fractions are inside derivatives
or integrals; but if polyvalnegexpflag is 2, as it
is for _de_moivre, don't eliminate negative exponents
if either the base or the power is complex.
*/
{ o[i] = eliminateconstnegexpnum; ++i;
}
o[i] = eliminatenegexpdenom; ++i;
if(statusflag && !stopcancel(t)) /* see a few lines up */
{ o[i] = cancelop; ++i; /* AFTER eliminatenegexpnum etc */
if(contains_sqrt(num) || contains_sqrt(denom))
{ o[i] = cancelsqrt; ++i;
o[i] = cancelroot; ++i;
}
}
if((g == '+' || g == '*' || g == '^') &&
(h == '+' || h == '*' || h == '^') &&
!stop_cancelgcd(num) &&
!stop_cancelgcd(denom)
/* first multiply out products of sums in the
num and denom; otherwise cancelgcd produces
correct but very mysterious black-box results */
)
{ /* cancelgcd must go in pre_ops. Example:
(cos x - 1)/ (cos^2 x -1) should be cancelled,
but if we wait till post_ops, the denominator
will be sin^2 x instead.
On the other hand, we don't want it to work
on (-a^2 + c^2 + a^2 -2b^2+c^2)/((a-b)(a-c)(b-c)),
because the cancellations and collections should
be done first.
*/
o[i] = cancelgcd; ++i;
if(intflag && path[pathlength-2] == INTEGRAL &&
FUNCTOR(num) == '+' && FUNCTOR(denom) == '+' /* don't use it if the denom is a power of x */
)
{ o[i] = polydivop; ++i;
/* this has to be here to pre-empt factoring in
the denom, which will take place when autosimp
calls pre_ops on the denominator. */
}
}
if(contains(t,SIN) && contains(t,COS))
/* example: (1-sin^2 x)/cos^2 x => (cos^2 x)/cos^2 x */
/* instead of factoring the numerator, which will happen
when pre_ops hits the numerator. */
{ o[i] = sinsquare2; ++i;
o[i] = sinsquare3; ++i;
}
if(contains(t,CSC) && contains(t,COT))
/* example: (csc^2x-1)/cot^2 x => (cot^2 x)/cot^2 x */
/* instead of factoring the numerator, which will happen
when pre_ops hits the numerator. */
{ o[i] = cotsquare2; ++i;
}
if(contains(t,SEC) && contains(t,TAN))
/* example: (sec^2x-1)/tan^2 x => (tan^2 x)/cot^2 x */
/* instead of factoring the numerator, which will happen
when pre_ops hits the numerator. */
{ o[i] = tansquare2; ++i;
}
break;
case SQRT:
u = ARG(0,t);
if(radicalflag == -1 && /* in calculus past limits,
radicalflag == -2, not -1 */
pathlength >= 2 && path[pathlength-2] != LIMIT &&
!(pathlength >= 4 && path[pathlength-2] == '/' && path[pathlength-4] == LIMIT) &&
/* don't rewrite sqrt into fractional exponents
when it's just inside a limit, or when it's the numerator
or denom of a fraction just inside a limit. This is only
needed when the sqrts are deeper inside. */
!algebraic_number(u) /* leave sqrt( 3/2 + 1/2 sqrt 5) alone */
)
{ if(INTEGERP(u) && currenttopic != _numerical_radicals)
{ o[i] = knownroot; ++i;
/* it looks silly to write sqrt 4 as 4^(1/2) */
}
o[i] = sqrtexp; ++i;
*nops = i;
return;
}
/* all these next few ops need to come BEFORE arithmetic,
e.g. to handle sqrt(2^100) without multiplying it out */
if(INTEGERP(u) && currenttopic != _numerical_radicals)
/* On that one topic, go ahead and factor 16 in sqrt(16) */
{ o[i] = knownroot; ++i;
}
if(OBJECT(u) || FUNCTOR(u) == '*')
{ o[i] = factorundersqrt; ++i;
}
if(OBJECT(u))
{ o[i] = evaltorational; ++i;
}
g = FUNCTOR(u);
if(get_complex && (g == '-' || obviously_negative(u)))
{ o[i] = sqrtofminus1;++i;
o[i] = sqrtofneg; ++i;
}
if( g=='*' || g == '^')
{ if( !get_complex() || seminumerical(t))
{ o[i] = sqrttoabs; ++i;
}
if(get_complex()== 0 && !sqrtexp_conditions(ARG(0,u)))
{ o[i] = sqrtofpower; ++i;
o[i] = sqrtsimp; ++ i; /* sqrt(x^2y) = x sqrt y */
}
}
if(g == '/' && ONE(ARG(0,u)))
{ o[i] = sqrtofquotient; ++i; /* sqrt(1/x) => 1/sqrt x */
/* Must happen before squareeqn, otherwise we miss a
substitution in sqrt x = 2 sqrt 2 - sqrt(1/x), and
unnecessarily square, getting a 4th degree equation
instead of a quadratic */
}
if(INTEGERP(u))
{ o[i] = arithmetic; ++i;
}
if(arithflag.complex)
{ o[i] = weakcomplexarithmetic; ++i;
}
break;
case ROOT:
u = ARG(1,t);
if(get_complex() && !iscomplex(u))
{ o[i] = complexrootminus; ++i;
}
if((radicalflag < 0 && !algebraic_number(u))|| intflag)
{ if(INTEGERP(u) && currenttopic != _numerical_radicals)
{ o[i] = knownroot; ++i;
}
if(currenttopic != _cubic_one_root &&
currenttopic != _complex_cubics
)
{ o[i] = rootexp; ++i;
}
}
if(equals(ARG(0,t),two))
{ o[i] = roottosqrt; ++i;
*nops=i;
return;
}
if(ISINTEGER(ARG(0,t)) && ISINTEGER(u) && currenttopic != _numerical_radicals)
{ o[i] = knownroot; ++i;
}
if(OBJECT(u) || FUNCTOR(u) == '*')
{ o[i] = factorunderroot; ++i;
}
g = FUNCTOR(ARG(1,t));
if(g=='*' && radicalflag != -1)
{ o[i] = rootsimp; ++i; /* ��(x�y) = x ��y */
}
if(g=='^' && radicalflag != -1 && !is_complex(ARG(1,t)))
/* don't use these when what's under the root is complex */
{ o[i] = rootofpower; ++i; /* root(n,x^n) = x if x>=0 or n odd */
o[i] = rootofpower2; ++i; /* root(2n,x^n) = sqrt(x^n) */
o[i] = rootofpower4; ++i; /* root(mn,a^n) = root(m,a) */
o[i] = rootofpower3; ++i; /* root(n,x^(nm)) = x^m if x>=0 or n odd */
o[i] = rootsimp; ++i; /* ��(x�y) = x ��y, works also
with x^k instead of x�y */
if(SOLVETYPE(problemtype))
{ o[i] = rootofpower5; ++i;
/* root(3,x^2) = root(3,x)^2 for example */
}
}
if(INTEGERP(u))
{ o[i] = arithmetic; ++i;
}
if(arithflag.complex)
{ o[i] = weakcomplexarithmetic; ++i;
}
break;
case COS:
u = ARG(0,t);
if(NEGATIVE(u))
{ o[i] = coseven; ++i;
}
if(OBJECT(u) && TYPE(u) == DOUBLE)
{ o[i] = devalop; ++i;
}
if(seminumerical(u) || (FUNCTOR(u) == DEG && numerical(ARG(0,u))))
{ select_trigeval_op(1,t,o+i,&k);
i += k;
}
break;
case SIN:
u = ARG(0,t);
if(NEGATIVE(u))
{ o[i] = sinodd; ++i;
}
if(OBJECT(u) && TYPE(u) == DOUBLE)
{ o[i] = devalop; ++i;
}
if(seminumerical(u) || (FUNCTOR(u) == DEG && numerical(ARG(0,u))))
{ select_trigeval_op(1,t,o+i,&k);
i += k;
}
break;
case TAN:
u = ARG(0,t);
if(NEGATIVE(u))
{ o[i] = tanodd; ++i;
}
if(OBJECT(u) && TYPE(u) == DOUBLE)
{ o[i] = devalop; ++i;
}
if(seminumerical(u) || (FUNCTOR(u) == DEG && numerical(ARG(0,u))))
{ select_trigeval_op(1,t,o+i,&k);
i += k;
}
break;
case COT:
u = ARG(0,t);
if(ZERO(u))
{ o[i] = cottotan; ++i;
}
if(NEGATIVE(u))
{ o[i] = cotodd; ++i;
}
if(seminumerical(u) || (FUNCTOR(u) == DEG && numerical(ARG(0,u))))
{ select_trigeval_op(1,t,o+i,&k);
i += k;
}
break;
case SEC:
u = ARG(0,t);
if(ZERO(u))
{ o[i] = secrule; ++i;
}
if(NEGATIVE(u))
{ o[i] = seceven; ++i;
}
if(problemtype == DIFFERENTIATE_FROM_DEFN
&& limitflag && !difflag
)
{ o[i] = secrule; ++i;
}
if(seminumerical(u) || (FUNCTOR(u) == DEG && numerical(ARG(0,u))))
{ select_trigeval_op(1,t,o+i,&k);
i += k;
}
break;
case CSC:
u = ARG(0,t);
if(ZERO(u))
{ o[i] = cscrule; ++i;
}
if(NEGATIVE(u))
{ o[i] = cscodd; ++i;
}
if(problemtype == DIFFERENTIATE_FROM_DEFN
&& !difflag
)
{ o[i] = cscrule; ++i;
}
if(seminumerical(u) || (FUNCTOR(u) == DEG && numerical(ARG(0,u))))
{ select_trigeval_op(1,t,o+i,&k);
i += k;
}
break;
case ASIN:
if(FUNCTOR(ARG(0,t)) == SIN)
{ o[i] = asinsin; ++i;
/* example, arcsin(sin(pi/12)). If we don't do this in
preops, we lose our chance as sin(pi/12) gets transformed. */
}
break;
case ACOS:
if(FUNCTOR(ARG(0,t)) == COS)
{ o[i] = acoscos; ++i;
/* example, arccos(cos(pi/12)). If we don't do this in
preops, we lose our chance as sin(pi/12) gets transformed. */
}
break;
case ATAN:
if(FUNCTOR(ARG(0,t)) == TAN)
{ o[i] = atantan; ++i;
/* In pre_ops to beat tan x = sin x / cos x to the punch;
also to beat tan(x/2)... inside the atan */
}
break;
case LN:
u = ARG(0,t);
if(
(FRACTION(u) && ONE(ARG(0,u))) ||
(FUNCTOR(u) == ABS && FRACTION(ARG(0,u)) && ONE(ARG(0,ARG(0,u))))
)
{ o[i] = lnofreciprocal; ++i;
}
if(ISATOM(u))
{ if(equals(u,complexi))
{ o[i] = lnofi; ++i;
}
}
switch(FUNCTOR(u))
{ case SQRT:
if(!logcollectflag)
{ o[i] = lnsqrt; ++i;
}
break;
case ROOT:
if(!logcollectflag)
{ o[i] = lnroot; ++i;
}
break;
case '^':
if(equals(eulere,ARG(0,u)))
{ o[i] = lnofpowerofe; ++i;
}
if(!logcollectflag && !limitflag)
{ o[i] = lnofpower; ++i;
}
else if(contains_log(ARG(1,u)) &&
!stoplogofpower(u)
)
{ o[i] = lnofpower; ++i;
}
else if(limitflag &&
!(FUNCTOR(ARG(0,u)) == '+' && ARITY(ARG(0,u)) == 2 &&
ONE(ARG(0,ARG(0,u))) &&
equals(ARG(1,ARG(0,u)), reciprocal(ARG(1,u)))
/* lim ln (1 + h/x)^(x/h) arises in differentiating
ln x from definition and the derivation
is ruined if we use lnofpower on it. */
)
)
{ /* use lnofpower on limits of ln(t^2) or t ln(t^2)
or ln(t^2)/t or t/ln(t^2). But, in a sum,
we use attractlns instead. So check that only
/ and * are on the path between here and the LIMIT
*/
for(j = pathlength-2; j>= 0; j--)
{ if(path[j] == LIMIT)
{ o[i] = lnofpower; ++i;
break;
}
if(path[j] != '*' && path[j] != '/')
break;
}
}
break;
case '-':
if(ONE(ARG(0,u)))
{ o[i] = lnofminusone; ++i;
}
break;
}
break;
case LOGB:
u = ARG(1,t);
if(FUNCTOR(u) == '^' && equals(ARG(0,t),ARG(0,u)))
{ o[i] = logbofpowerofb; ++i;
}
if(FUNCTOR(u) == '^' && contains_log(ARG(1,u)) && !stoplogofpower(u))
{ o[i] = logbofpower; ++i;
/* Example: log(3,x^log(3,x)) should go to log(3,x) log(3,x)
before the base of logs in the exponents gets changed.
But, log(2,a^log(a,4)) should NOT be touched, because
the inner term will be simplfied.
*/
}
if(FUNCTOR(u) == ROOT && !logcollectflag)
{ o[i] = logroot; ++i;
}
break;
case LOG:
u = ARG(0,t);
switch(FUNCTOR(u))
{ case SQRT:
if(!logcollectflag)
{ o[i] = logsqrt; ++i;
}
break;
case ROOT:
if(!logcollectflag)
{ o[i] = logroot; ++i;
}
break;
case '^':
if(equals(ten,ARG(0,u)))
{ o[i] = logofpowerof10; ++i;
}
if(!logcollectflag && !limitflag)
{ o[i] = logofpower; ++i;
}
else if(contains_log(ARG(1,u)) && !stoplogofpower(u))
{ o[i] = logofpower; ++i;
}
break;
}
break;
case SUM:
if( ISINFINITE(ARG(3,t)) &&
( problemtype == ADDSERIES ||
(problemtype == TESTCONVERGENCE && currenttopic == _comparison_test)
)
)
/* an infinite sum. These operators try to bring a series
to geometric or telescoping form and sum the series
explicitly. It's necessary to use these in comparison_test
too so that the new series can eventually be added up.
*/
{ series_preops(t,o+i,&k);
i += k;
}
break;
case OR:
if(problemtype == RELATED_RATES && pathlength <= 1)
{ *nops = 0;
return;
}
if(problemtype == MINMAX)
{ o[i] = addcriticalpoints; ++i;
o[i] = addendpoints; ++i;
o[i] = addundefinedpoints; ++i;
/* rejectpoint and tabulate are in postops */
}
if(SOLVETYPE(problemtype))
{ o[i] = collectmultiplesolns; ++i;
if(!get_complex())
{ o[i] = introduceabs; ++i;
}
}
if(problemtype == MINMAX)
{ o[i] = rejectpoint; ++i;
}
o[i] = lessthantole; ++i;
o[i] = greaterthantoge; ++i;
break;
case AND:
if(problemtype == LINEAR_EQUATIONS)
{ o[i] = dropeqn; ++i;
if(currenttopic != _eqns_by_substitution)
{ o[i] = varsleft; ++i;
}
o[i] = evaluatedeterminant; i++;
/* although pure automode needs this only under
topic _cramersrule, a user might invoke Cramer's Rule
in menu mode and then press Auto Finish. So we
need it under other topics too. */
}
if(interval_as_and(t))
{ if(NEGATIVE(ARG(1,ARG(0,t))))
{ o[i] = FUNCTOR(ARG(0,t)) == '<' ? changesigns1 : changesigns2; ++i;
}
o[i] = transfer; ++i;
}
break;
case SG:
o[i] = sgpos; ++i;
o[i] = sgneg; ++i;
if(FUNCTOR(ARG(0,t)) == '*')
{ o[i] = sgprod1; ++i;
}
else if(FRACTION(ARG(0,t)))
{ if(ONE(ARG(0,ARG(0,t))))
{ o[i] = sgrecip2; ++i;
}
else
{ o[i] = sgrecip3;++i;
o[i] = sgfract1; ++i;
o[i] = sgfract2; ++i;
}
}
else if(FUNCTOR(ARG(0,t)) == '^')
{ o[i] = sgpower; ++i;
}
break;
case INTEGRAL:
if(CANTFACTOR(t)) /* this integral has already been tried */
{ *nops = i;
return;
}
if(pathlength == 4 && path[0] == '-' && path[2] == '-' &&
equals(t,history(0))
)
{ *nops = i;
return; // give up
}
u = ARG(0,t);
x = ARG(1,t);
if(
(FUNCTOR(u) == '*' || FRACTION(u)) &&
contains(ARG(0,u),SG) &&
!contains(ARG(1,u),SG)
)
{ o[i] = sgint; ++i;
}
if(FUNCTOR(u) == DIFF || FUNCTOR(u) == PR)
{ o[i] = fundamentaltheorem; ++i;
}
if(squareofone(u)) /* don't touch sin^4 x + 2cos^2 x sin^2 x + cos^4 x */
{ *nops = i;
return;
}
if(ARITY(t) == 4)
{ term lo = ARG(2,t);
term hi = ARG(3,t);
if( (NEGATIVE(lo) && equals(ARG(0,lo),hi)) ||
(NEGATIVE(hi) && equals(ARG(0,hi),lo))
)
{ o[i] = oddintegrand; ++i;
o[i] = evenintegrand; ++i;
}
o[i] = switchlimits; ++i; /* make lower limit less than upper */
}
if(ARITY(t) == 4 &&
(IMPROPER(t) || equals(ARG(2,t),minusinfinity) ||
equals(ARG(3,t),infinity)
) &&
status(integraltolimit) <= LEARNING
)
{ if(FUNCTOR(ARG(0,t)) != ABS)
/* if the functor is ABS, breakabsint
should be used to eliminate ABS. */
{ if(equals(ARG(3,t),infinity))
{ o[i] = insertpoint; ++i;
o[i] = intdivtest1; ++i;
o[i] = integraltolimit; ++i;
}
else if(equals(ARG(2,t),minusinfinity))
{ o[i] = insertpoint; ++i;
o[i] = intdivtest2; ++i;
o[i] = integraltolimit2; ++i;
}
else if(IMPROPER(t))
{ o[i] = insertpoint; ++i;
o[i] = integraltolimit3; ++i;
o[i] = integraltolimit4; ++i;
}
}
}
if(suppress_trig_expansion(ARG(0,t),x))
{ o[i] = intcossq; ++i;
o[i] = intsinsq; ++i;
}
if(get_nextdefn())
{ o[i] = trysubstitution; ++i;
}
o[i] = changeintegrationvariable; ++i;
g = FUNCTOR(u);
switch(g)
{ case '+' :
if(status(intpoly) >= KNOWN && ispolyin(u,x))
{ o[i] = intpoly; ++i;
*nops = i;
return; /* intpoly is certain to work */
}
if(intsub_on_sum(u,x))
{ o[i] = intsub; ++i; /* before breaking up the sum */
}
if(!blockintlinearity(u,x))
{ o[i] = intlinearity; ++i; /* instead of just intsum */
}
break;
case '-':
if(!contains(u,FUNCTOR(x)))
{ o[i] = intconst; ++i;
}
o[i] = intminus; ++i;
break;
case '^' : /* catch f� x where f is SEC, TAN, COT, CSC */
v = ARG(0,u);
h = FUNCTOR(v);
if(h == TAN &&
equals(ARG(0,v),x) &&
isinteger(ARG(1,u))
)
{ o[i] = intsubtan; ++i;
}
if(h == COT &&
equals(ARG(0,v),x) &&
isinteger(ARG(1,u))
)
{ o[i] = intsubcot; ++i;
}
if(h == SEC &&
equals(ARG(0,v),x) &&
isinteger(ARG(1,u))
)
{ if(INTEGERP(ARG(1,u)))
{ if(equals(ARG(1,u),two))
{ o[i] = intsecsq; ++i;
}
else if(ISEVEN(ARG(1,u)))
{ o[i] = intsubtan; ++i;
}
else
{ o[i] = intsecpower; ++i;
}
}
if(!infer(even(ARG(1,u))))
{ o[i] = intsubtan; ++i;
}
else
{ o[i] = intsecpower; ++i;
}
}
if(h == CSC &&
equals(ARG(0,ARG(0,u)),x) &&
isinteger(ARG(1,u))
)
{ if(INTEGERP(ARG(1,u)))
{ if(equals(ARG(1,u),two))
{ o[i] = intcscsq; ++i;
}
else if(ISEVEN(ARG(1,u)))
{ o[i] = intsubcot; ++i;
}
else
{ o[i] = intcscpower; ++i;
}
}
if(!infer(even(ARG(1,u))))
{ o[i] = intsubcot; ++i;
}
else
{ o[i] = intcscpower; ++i;
}
}
break;
case SIN:
case COS:
case TAN:
if(FUNCTOR(ARG(0,u))==SQRT &&
equals(ARG(0,ARG(0,u)),x)
)
{ o[i] = intsub; ++i;
}
break;
case '*' :
if(
FUNCTOR(u) == '*' &&
!contains_at_toplevel(u,'/') &&
!RATIONALP(ARG(0,u))
/* multiply the fractions together before bringing
out a constant unless it's a rational number
as the first factor, because there might be
some cancellations in the integrand, e.g.
a sqrt(10) in the denom somewhere to cancel
a sqrt(10) as a toplevel factor. */
)
{ o[i] = intlinear; ++i;
}
o[i] = inttosec; ++i;
o[i] = inttocsc; ++i;
if(!PRIME(t) && intsub_in_preops(u,ARG(1,t)) && !get_pending())
/* PRIME means integration by substitution has
been tried on it already. The call to
get_pending prevents using intsub again while
you still are supposed to be simplifying du/dx */
/* example: (x+3)(x^2+6x)^2 */
{ if(status(intsub) >= KNOWN)
{ o[i] = intsub; ++i;
}
else if(status(intsub) >= LEARNING)
/* it won't be used in topic _simple_int */
{ o[i] = choosesubstitution; ++i;
}
}
if(!iscomplex(t))
{ o[i] = polyvalop; ++i;
/* It was not thrown in above to avoid preempting intsub */
}
break;
case '/' : /* pull out a constant */
num = ARG(0,u);
denom = ARG(1,u);
o[i] = intconst; ++i;
o[i] = intlinear; ++i;
if(equals(denom,x))
{ o[i] = intrecip; ++i; /* before intsub is used */
}
else if(status(intsub) > LEARNING &&
(ARITY(t) == 2 ||
(!IMPROPER(t) &&
!equals(ARG(3,t),infinity) &&
!equals(ARG(2,t),minusinfinity) &&
!equals(ARG(3,t),minusinfinity) &&
!equals(ARG(2,t),infinity)
)
||
status(integraltolimit) > LEARNING
)
)
{ if(FUNCTOR(denom) == '+' &&
(equals(ARG(0,denom),x) || equals(ARG(ARITY(denom)-1,denom),x))
)
{ o[i] = intrecip2; ++i;
}
else
{ o[i] = intrecip3; ++i;
}
}
/* The following operators go in pre_ops so they get tried
before the denominator is factored, e.g.
1/�(x^2-1) => 1/�((x-1)(x+1))
before it can get integrated.
*/
if(ARITY(t) == 2 || !equals(ARG(3,t),infinity))
{ o[i] = inttoatan; ++i;
o[i] = inttolnratio1; ++i;
o[i] = inttolnratio2; ++i;
}
if(ONE(num) &&
(FUNCTOR(denom) == COS ||
(FUNCTOR(denom) == '^' && FUNCTOR(ARG(0,denom)) == COS)
)
)
{ o[i] = secrule2; ++i; /* 1/cos� = sec� */
}
if(
(ARITY(t) == 2 || !equals(ARG(3,t),infinity)) &&
contains(ARG(1,ARG(0,t)),SQRT)
)
{ o[i] = inttoasin; ++i;
o[i] = inttolnratio3; ++i;
o[i] = inttoacos; ++i;
}
if(!IMPROPER(t) && intsub_in_preops(u,x))
/* example: (x+3)/(x^2+6x)^2 */
{ if(status(intsub) >= KNOWN)
{ o[i] = intsub; ++i;
}
else if(status(intsub) >= LEARNING)
{ o[i] = choosesubstitution; ++i;
}
}
if(!iscomplex(t))
{ o[i] = polyvalop; ++i;
/* It was not thrown in above to avoid preempting intsub */
}
break;
case DIFF:
o[i] = fundamentaltheorem; ++i;
break;
}
break;
case EVAL:
if(FUNCTOR(ARG(0,t)) == LN && status(evalbar) > LEARNING)
{ o[i] = evalbarln; ++i;
}
o[i] = evalbar; ++i;
break;
case '=':
x = get_eigenvariable();
if(problemtype == MINMAX)
{ if(pathlength == 0)
{ o[i] = addcriticalpoints; ++i;
o[i] = addendpoints; ++i; /* could need this if there was
just one critical point */
o[i] = addundefinedpoints; ++i;
}
o[i] = rejectpoint; ++i;
o[i] = eliminateparameter; ++i;
}
if((problemtype == TRIG_IDENTITY || SOLVETYPE(problemtype)))
/* sincosflag is used in autotrig to control the decision
which of the three doublecos operators to use. */
{ if(contains_odd_power(t,SIN))
set_sincosflag(SIN);
else if(contains_odd_power(t,COS))
set_sincosflag(COS);
else
set_sincosflag(0);
if((contains(t,COSH) || contains(t,SINH)) && contains(t,'e'))
set_hflag(1);
else if(currenttopic == _hyperfunctions)
set_hflag(1); /* under this topic you are supposed to
express hyperbolic functions as exponentials. */
else
set_hflag(0);
/* hflag helps control coshdef and sinhdef; for more info
see the comments at the definition of hflag in postops.c */
}
if((problemtype == TRIG_IDENTITY || SOLVETYPE(problemtype)) &&
stricttrigpoly(ARG(0,t),x) &&
stricttrigpoly(ARG(1,t),x)
)
{ unsigned short localtrigflag = 0;
localtrigflag = 0;
set_trigflag(t,&localtrigflag);
if(trigexpandflag && algebraic_identity(t))
trigpolyflag = -1;
else if(trigexpandflag & (1 << 12))
{ /* only sin and cos occur, so we need to write
the problem in the form a sin t + b where
a and b are polys in cos t, or else in the
form a cos t + b where a and b are polys in
sin t, using sinoddpower or cosoddpower to
eliminate the odd powers. What we have to
do now is determine which of these mutually
exclusive forms to use.
If one side of the identity is an even power
of COS and the other side only contains SIN,
then eliminate COS, and vice-versa. Otherwise
choose SIN if COS appears to an odd power, or
COS if SIN appears to an odd power. Otherwise,
arbitrarily choose to eliminate SIN.
But we shouldn't start eliminating sin in
favor of cos too soon. Consider this example:
4 sin x cos x (cos^2 x - sin^2 x) =
4 sin x cos^3 x - 4 sin^3 x cos x,
which can be solved either by multiplying out on
the left or factoring on the right. We don't want
to rewrite sin^2 x as 1-cos^2 on the left. So,
we don't want to set trigpolyflag in such a case.
Indeed, if the two sides can be simplified to the
same thing without using trig identities, then
that's how it should be done.
*/
trigpolyflag = tpf_aux(t,SIN,COS);
}
else if(trigexpandflag & (1 << 4))
{ trigpolyflag = tpf_aux(t,SEC,TAN);
}
else if(trigexpandflag & (1 << 8))
{ trigpolyflag = tpf_aux(t,COT,CSC);
}
else
trigpolyflag = 0;
}
if(problemtype == TRIG_IDENTITY)
/* don't miss a one-step solution just because the terms aren't
in the proper order. If this happens with a lot of sin 3x
terms which will soon get expanded, you miss your chance if
you don't do it on the '='. */
{ if(FRACTION(ARG(0,t)) && FRACTION(ARG(1,t)))
{ term u,v;
u = ARG(0,t);
v = ARG(1,t);
if(FUNCTOR(ARG(0,u)) == '+' && ARITY(ARG(0,u)) == 2 && NEGATIVE(ARG(1,ARG(0,u))) &&
FUNCTOR(ARG(1,u)) == '+' && ARITY(ARG(1,u)) == 2 && NEGATIVE(ARG(1,ARG(1,u))) &&
FUNCTOR(ARG(0,v)) == '+' && ARITY(ARG(0,v)) == 2 && NEGATIVE(ARG(1,ARG(0,v))) &&
FUNCTOR(ARG(1,v)) == '+' && ARITY(ARG(1,v)) == 2 && NEGATIVE(ARG(1,ARG(1,v)))
)
{ o[i] = negatenumdenom; ++i;
/* (a-b)/(c-d) = (b-a)/(d-c) */
}
}
if(
(FUNCTOR(ARG(0,t)) == '*' && !stop_orderfactors(ARG(0,t))) ||
(FUNCTOR(ARG(1,t)) == '*' && !stop_orderfactors(ARG(1,t)))
)
{ o[i] = orderfactors; ++i;
}
if(!stop_orderterms(t))
{ o[i] = orderterms; ++i;
}
/* Also don't miss an identity that, after one side is
content-factored, will be an identity or will need only
one term-ordering step to be an identity. */
u = ARG(0,t);
v = ARG(1,t);
if(FUNCTOR(v) == '*' && FUNCTOR(u) == '+' &&
!content_factor(u,&a,&b) &&
equal_mod_order(product(a,b),v)
)
{ o[i] = contentfactor; ++i;
}
if(FUNCTOR(u) == '*' && FUNCTOR(v) == '*' &&
!content_factor(v,&a,&b) &&
equal_mod_order(u,product(a,b))
)
{ o[i] = contentfactor; ++i;
}
/* is there a common factor on the two sides which we could possibly cancel? */
if(contains_trig(u) && contains_trig(v) && common_factor(u,v))
/* the contains_trig call keeps us from using cancelling when
verifying algebraic identities. */
{ o[i] = diveqn; ++i;
}
/* is there a pair of factors on the two sides which are really identical
but are not yet literally identical? */
o[i] = preparetocancel; ++i;
}
if(currenttopic == _logarithmic_differentiation)
{ if(!contains(t,DIFF))
{ o[i] = difeqn; ++i;
break;
}
if(FUNCTOR(t) == '=' &&
!contains(ARG(1,t),DIFF) &&
FRACTION(ARG(0,t)) &&
( FUNCTOR(ARG(0,ARG(0,t))) == DIFF || FUNCTOR(ARG(0,ARG(0,t))) == PR)
)
{ o[i] = muleqn; ++i;
}
break;
}
if(!SOLVETYPE(problemtype) &&
!(problemtype == LINEAR_EQUATIONS && pathlength == 0)
)
{ u = ARG(0,t);
v = ARG(1,t);
if(FUNCTOR(u) == FUNCTOR(v))
{ g = FUNCTOR(u);
if(g == '^' && equals(ARG(0,u),ARG(0,v)))
{ o[i] = logbeqn; ++i;
*nops = i;
return;
}
if(g == LN || g == LOG) /* if we have ln u = ln v, make it u=v */
{ o[i] = powereqn2; ++i;
*nops = i;
return;
}
}
if(FUNCTOR(u) == '^' && INTEGERP(v))
{ /* example, 5^(...) = 25 */
o[i] = logbeqn; ++i;
}
if(
(FUNCTOR(u) == '*' || FUNCTOR(u) == '+') &&
(FUNCTOR(v) == '*' || FUNCTOR(v) == '+') &&
status(polyvalop) > LEARNING &&
/* don't use "common factor" in elementary algebra */
get_whichpass() > 0
/* and don't use it at all except as a last resort */
)
{ o[i] = factorandcancel; ++i; /* factor out gcd of u and v */
}
}
else
{ pre_equation(t,o+i,&j);
i += j;
}
if(problemtype == INTEGRATION)
{ o[i] = difsubstitution; ++i;
}
else if(problemtype == INDUCTION && !equals(ARG(0,t),ARG(1,t)))
/* the second part prevents arithmetic from rewriting
a = a as true */
{ o[i] = arithmetic; ++i;
o[i] = selectinductionvariable; ++i;
o[i] = useinductionhyp; ++i;
}
break;
case '>':
o[i] = arithmetic; ++i; /* reduce e.g. 0�1 to false */
x = get_eigenvariable();
if(contains(ARG(1,t),FUNCTOR(x)) && !contains(ARG(0,t),FUNCTOR(x)))
{ o[i] = reversegreaterthan; ++i;
}
pre_ineq(t,o+i,&j);
i += j;
break;
case GE :
o[i] = arithmetic; ++i; /* reduce e.g. 0�1 to false */
x = get_eigenvariable();
if(contains(ARG(1,t),FUNCTOR(x)) && !contains(ARG(0,t),FUNCTOR(x)))
{ o[i] = reversege; ++i;
}
pre_ineq(t,o+i,&j);
i += j;
break;
case '<':
pre_ineq(t,o+i,&j);
i += j;
break;
case LE :
pre_ineq(t,o+i,&j);
i += j;
break;
case LIMIT:
u = LIMITAND(t);
x = ARG(0,ARG(0,t));
a = ARG(1,ARG(0,t));
if(!contains(u,FUNCTOR(x)) || contains_deriv(u,x))
/* Don't work on a limit till you've evaluated all
derivatives in the limitand */
{ o[i] = limconst; ++i;
*nops = i;
return;
}
SaveShowStepState();
if(!maximal_sub(u,&temp) &&
(
algpoly(temp) ||
(ARITY(temp) == 1 && TRIGFUNCTOR(FUNCTOR(temp)) && equals(ARG(0,temp),x))
)
)
/* example, (cos(x^2)-1)/x^2 which otherwise
might be transformed to sin^2(x/2) /(x/2),
except that now we have code to get L'Hopital used on it.
*/
{ o[i] = changelimitvariable; ++i;
changelimitvarflag = 1;
}
RestoreShowStepState(); /* maximal_sub can call SetShowStepArg */
if(!contains_big_exponents(t) && !iscomplex(t))
{ o[i] = polyvalop; ++i;
}
if(equals(u,x))
{ o[i] = limident; ++i;
}
if(FUNCTOR(u) == '^' &&
FUNCTOR(ARG(0,u))=='+' && ARITY(ARG(0,u)) == 2 &&
(ONE(ARG(0,ARG(0,u))) || ONE(ARG(1,ARG(0,u))))
)
{ if(FRACTION(ARG(1,u)))
{ o[i] = defnofe; ++i;
}
if(!changelimitvarflag && ISINFINITE(a) && equals(ARG(1,u),x))
{ o[i] = changelimitvariable; ++i;
}
}
if(status(limpoly) >= KNOWN)
{ if(indenomflag && FUNCTOR(u) == '^' && iseven(ARG(1,u)))
{ o[i] = limpower; ++i;
/* If you use limpoly on 1/x^2 you get 1/0 instead
of 1/0^2, so you can't get 'infinity' but only
undefined. Therefore use limpower first. */
}
else if(!FRACTION(u))
/* don't use limpoly on x/2 even though it will work;
and for non-constant denom it can't work. */
{ o[i] = limpoly; ++i;
}
}
if(NEGATIVE(u))
{ o[i] = limlinear; ++i; /* lim(x->0, -2 sin x) for example
should go direct to -2 lim(x->0, sin x) */
o[i] = limminus; ++i;
}
if(FUNCTOR(u) == '/' && OBJECT(ARG(0,u)) &&
!NOTDEFINED(ARG(1,ARG(0,t))) &&
!(
FUNCTOR(ARG(1,u)) == SEC ||
(FUNCTOR(ARG(1,u)) == '*' && contains(ARG(1,u),SEC)) ||
FUNCTOR(ARG(1,u)) == CSC ||
(FUNCTOR(ARG(1,u)) == '*' && contains(ARG(1,u),CSC))
) /* in the latter cases secrecip or cscrecip will be used */
/* some case may be blocked here where SEC is buried deeper
in the term, but then it still should be simplified
before using limrecip. contains_at_toplevel isn't enough
here because the denom may contain sec^2.
*/
)
/* example: u = 2/x . Here limrecip will work
directly:
lim 2/x => 2/lim x rather than
lim 2/x => 2 lim 1/x => 2(1/lim x) = > 2/ lim x
But !NOTDEFINED(a) so as not to do this on lim(x->0,1/x)
which will be handled directly by another operator.
If this condition changes, change autolimit's conditions
for throwing in limrecip as a last resort on limits at
infinity to match.
*/
{ o[i] = limrecip; ++i;
*nops = i;
return;
}
if(FUNCTOR(u) == '/' &&
!changelimitvarflag &&
!ATOMIC(ARG(0,u)) && ARITY(ARG(0,u)) == 1
&& equals(ARG(0,ARG(0,u)),ARG(1,u)) &&
!equals(ARG(1,u),x)
)
/* e.g. sin(5t)/(5t) */
{ o[i] = changelimitvariable; ++i;
*nops = i;
return;
}
if(FRACTION(u) &&
!changelimitvarflag &&
!ATOMIC(ARG(1,u)) && ARITY(ARG(1,u)) == 1
&& equals(ARG(0,ARG(1,u)),ARG(0,u)) && !equals(ARG(0,u),x)
)
/* e.g. 5t/sin(5t) */
{ o[i] = changelimitvariable; ++i;
*nops = i;
return;
}
if(FRACTION(u) && FUNCTOR(ARG(0,u)) == LN &&
!changelimitvarflag &&
FUNCTOR(ARG(0,ARG(0,u))) == '+' &&
!ATOMIC(ARG(1,u)) &&
(
(ONE(ARG(0,ARG(0,ARG(0,u)))) && equals(ARG(1,u),ARG(1,ARG(0,ARG(0,u))))) ||
(ONE(ARG(1,ARG(0,ARG(0,u)))) && equals(ARG(1,u),ARG(0,ARG(0,ARG(0,u)))))
)
)
/* ln(1+u)/u ; if e.g. u is a fraction don't use limlinear first */
{ o[i] = changelimitvariable; ++i;
}
if(FRACTION(u) && contains(ARG(1,u),'/') &&
is_linear_in(ARG(1,u),x) && contains(ARG(1,u),FUNCTOR(x))
)
/* example, 4x-3/(x-1/2) as x->1/2. If we don't try limquotient
in pre_ops, thrashing results due to pulling out 1/2 and putting
it back in again. */
{ o[i] = limquotient; ++i;
}
if(FUNCTOR(u) == '*' || FUNCTOR(u) == '/')
{ o[i] = limlinear; ++i;
}
if(FUNCTOR(u) == '/' &&
ZERO(a) &&
(trigpolyargs2(ARG(1,u),x)==2 || trigpolyargs2(ARG(0,u),x)==2) &&
/* trigpolyargs2 returns 2 if trig functions are actually present,
1 if its arg is really just a polynomial */
(trigexpandflag & 1) /* not all trig functions have the same arg */
)
/* example: lim(x->0, (tan 2x) / tan 3x)
A trigpoly2 is a polynomial in trig functions whose args are
linear in x. Divnumdenom has to be used in pre_ops because
otherwise, trig identities will be used to reduce everything
to trig functions of x, not 2x, 3x, etc., and
the limitand will get horribly complicated.
*/
{ o[i] = divnumdenom; ++i;
}
break;
case DET:
o[i] = evaluatedeterminant; i++;
break;
} /* close big switch */
*nops = i;
return;
} /* close function */
/*_______________________________________________________*/
int suppress_factoring(int problemtype, int dir)
/* return 1 if we are working on a sum which is monomially contained
in another sum, as in 3x - 2(x^2-1); we don't want to factor the x^2-1;
EXCEPT when factoring that sum is going to make the outer sum factor
too, as in 3(y+1)^2 +11(y^2-1) -4(y-1)^2. That form will be caught
and handled by factorquadratic specially, so
this function just goes ahead and suppresses factoring.
Also return 1 when working on a sum whose parent and grandparent
are '/', i.e. is part of a fraction which is the num or denom of a
compound fraction. In this case factoring should be postponed till
invertand multiply has been used.
If dir == 0, we're going DOWN in autosimp, so the last functor
in that path is NOT the term we're working on. If dir==1, we're
going up, so the last functor IS the term we're working on.
*/
{ int pathlength = get_pathlength();
unsigned short const *path = get_path();
int k = pathlength-1;
if(pathlength == 0)
return 0;
if(dir == 1)
{ assert(path[k] == '+');
k-=2; /* ignore the functor of the current term */
}
else
--k; /* ignore the arg number */
if(k >= 2 && path[k] == '/' && path[k-2] == '/')
return 1;
while(k >= 0 &&
( path[k] == '*' ||
(path[k] == '^' && path[k+1] == 1) ||
path[k] == '-'
)
)
k -= 2;
if(k < 0 || path[k] != '+')
return 0;
return 1;
}
/*_______________________________________________________________________*/
static int contains_sq2(unsigned short f, term t, term x)
/* if t has the form f^m(x) where m >= 2, or is a product,
one of whose factors has that form, then return 1; else return 0.
*/
{ unsigned short i,n;
if(FUNCTOR(t) == '^' && ISINTEGER(ARG(1,t)) && INTDATA(ARG(1,t)) >= 2 &&
FUNCTOR(ARG(0,t)) == f && equals(ARG(0,ARG(0,t)),x)
)
return 1;
if(FUNCTOR(t) != '*')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_sq2(f,ARG(i,t),x))
return 1;
}
return 0;
}
/*____________________________________________________________________*/
int intsub_in_preops(term u, term x)
/* Return 1 if integration by substitution should be tried
on an integrand with functor * or / before simplifying the
integrand. We have to pre-empt multiplyout and apart,
because if these are used we lose our chance at substitution.
Here u is the integrand and x is the variable of integration.
We also must intervene in case the integrand contains
ln(tan x), which may become ln(sin x) - ln(cos x) and the
integral broken up irretrievably before intsub gets tried.
We must also pre-empt rationalizedenom in, for example,
1/(x-sqrt(x+2)), where u = sqrt(x+2) is the thing to do.
After rationalizedenom we do get a solution, but it's long
and complicated.
And, in general we must preempt polyvalop too; see the
example in the code below near 'exponential_factor'.
If the integrand is trigrational and all the trig args
are the same, we don't want to miss that opportunity, because
various trig operations can destroy it.
*/
{ unsigned short f = FUNCTOR(u);
term base,power;
assert(f == '*' || f == '/');
if(status(intsub) == UNKNOWN && get_whichpass() == 0)
return 0;
if(logtrig(u))
return 1;
if(trigalg_same(u,x))
return 1;
if(exponential_factor(u,x) && !contains_trig(u))
return 1; /* e^(-t)ln(1+e^(-t))/(1+e^(-t)) for example */
/* the !contains_trig clause is for sin(t)/e^t which should be done by parts */
/* next trap tan(x)/cos^2 and similar examples before tan = sin /cos is used */
if(f == '/' && contains_sq2(COS,ARG(1,u),x))
{ term a,b,c,cossq;
cossq = make_power(cos1(x),two);
if(!cancel(ARG(1,u),cossq,&a,&b) && equals(a,cossq))
{ subst(var0, tan1(x),b,&c);
if(!contains(c,FUNCTOR(x)))
return 1;
}
}
if(f == '/' && contains_sq2(SIN,ARG(1,u),x))
{ term a,b,c,sinsq;
sinsq = make_power(sin1(x),two);
if(!cancel(ARG(1,u),sinsq,&a,&b) && equals(a,sinsq))
{ subst(var0, cot1(x),b,&c);
if(!contains(c,FUNCTOR(x)))
return 1;
}
}
if(f == '/' && FUNCTOR(ARG(0,u)) == '^' && ISINTEGER(ARG(1,ARG(0,u))) &&
FUNCTOR(ARG(0,ARG(0,u))) == TAN && equals(ARG(0,ARG(0,ARG(0,u))),x) &&
FUNCTOR(ARG(1,u)) == '^' && FUNCTOR(ARG(0,ARG(1,u))) == COS &&
equals(ARG(0,ARG(0,ARG(1,u))),x) && equals(ARG(1,ARG(1,u)),two)
)
return 1;
if(f == '/' && FUNCTOR(ARG(0,u)) == '^' && ISINTEGER(ARG(1,ARG(0,u))) &&
FUNCTOR(ARG(0,ARG(0,u))) == COT && equals(ARG(0,ARG(0,ARG(0,u))),x) &&
FUNCTOR(ARG(1,u)) == '^' && FUNCTOR(ARG(0,ARG(1,u))) == SIN &&
equals(ARG(0,ARG(0,ARG(1,u))),x) && equals(ARG(1,ARG(1,u)),two)
)
return 1;
if(f == '/' && FUNCTOR(ARG(0,u)) == '+' && FUNCTOR(ARG(1,u)) == '+')
return 1; /* preempt 'apart'-- it's possible that the numerator is
(a multiple of) the derivative of the denominator, and
we miss that substitution if we use apart. */
if(!ispolyin(ARG(0,u),x))
return 0; /* Then post_ops is soon enough; we only have to
pre-empt apart and multiplyout and partialfractionsop */
if(grat(u,x))
/* grat tells if u is a rational function in x, but it allows
sums of products of powers of polynomials in num and denom,
not requiring strict polynomial/polynomial form. See polynoms.c.
*/
return 1; /* example, 1/(x(x+1)^4). If we use partial
fractions we wind up with 1/(x+1)^4 which
Mathpert can't do. */
if(FUNCTOR(ARG(1,u)) == '^')
{ base = ARG(0,ARG(1,u));
power = ARG(1,ARG(1,u));
if(constant(power) && !ATOMIC(base) && ispolyin(base,x))
return 1; /* example: (x+3)(x^2+6x)^2 */
if(equals(base,eulere) &&
ispolyin(power,x) &&
contains(power,'^') /* don't use subst on (2x+3)e^x, it will
try u = 2x+3 . In general (poly)e^(linear)
should be multiplied out and split.
*/
)
return 1;
}
if(possible_trigsub(u,x))
return 0; /* example, x^3 sqrt(1-x^2);
if you substitute u = x^2, you are lost.
*/
if(!contains(ARG(0,u),FUNCTOR(x)) &&
FUNCTOR(ARG(1,u)) == SQRT &&
ispolyin(ARG(0,ARG(1,u)),x)
)
return 0; /* integral(1/sqrt(f(x)),x) doesn't get better by substitution */
if(ARITY(ARG(1,u)) == 1 &&
ispolyin(ARG(0,ARG(1,u)),x) &&
contains(ARG(0,ARG(1,u)),'^') /* don't use subst on (x+2)/sqrt x,
it will find u = x+2 and complicate the integral */
)
return 1;
if(!ATOMIC(ARG(1,u)) &&
ispolyin(ARG(1,u),x) &&
contains(ARG(1,u),'^') /* a nonlinear poly */
)
return 1;
if(FRACTION(u) &&
FUNCTOR(ARG(1,u)) == '+' &&
contains_sqrt(ARG(1,u))
)
return 1; /* pre-empt rationalizedenom */
return 0;
}
/*____________________________________________________________________*/
static int stopcancel(term t)
/* t is a fraction. Return 1 if there's a good reason to block
cancellation, because some trivial simplification should be done
first. This WILL allow arithmetic to be done on num and denom,
so e.g. pm/(q(-m)) is not going to cancel m before multiplying,
which looks bad if p,q,m are all large numbers. But on the whole
we gain more by stopping applications of cancel which do too much
too soon.
Specifically, if either num or denom is a product containing
a factor which is a product or a negation, return 1. Else return 0.
*/
{ term u,v;
int i,j;
unsigned short f,n;
if(status(polyvalop)==WELLKNOWN)
return 0; /* in that case don't block cancel, get things
done as fast as you can. */
if(!FRACTION(t))
return 0; /* assert(0) */
if(FRACTION(ARG(0,t)) && FRACTION(ARG(1,t)))
return 1; /* invert and multiply first */
for(i=0;i<2;i++)
{ u = ARG(i,t);
f = FUNCTOR(u);
if(f == '^' && equals(ARG(0,u),ten) && FUNCTOR(ARG(1,u)) == LOG)
return 1;
if(f == '^' && equals(ARG(0,u),eulere) && FUNCTOR(ARG(1,u)) == LN)
return 1;
if(f == '^' && FUNCTOR(ARG(1,u)) == LOGB && equals(ARG(0,u),ARG(0,ARG(1,u))))
return 1;
if(f == '^' && equals(ARG(0,u),ten) && FUNCTOR(ARG(1,u)) == '*' &&
contains_at_toplevel(ARG(1,u),LOG)
)
return 1;
if(f == '^' && equals(ARG(0,u),eulere) && FUNCTOR(ARG(1,u)) == '*' &&
contains_at_toplevel(ARG(1,u),LN)
)
return 1;
if(f == '^' && FUNCTOR(ARG(1,u)) == '*')
{ n = ARITY(ARG(1,u));
for(j=0;j<n;j++)
{ if(FUNCTOR(ARG(j,ARG(1,u))) == LOGB &&
equals(ARG(0,ARG(j,ARG(1,u))), ARG(0,u))
)
return 1;
}
}
if(f != '*')
continue;
n = ARITY(u);
for(j=0;j<n;j++)
{ v = ARG(j,u);
if(NEGATIVE(v))
return 1;
if(FUNCTOR(v) == '*')
return 1;
}
}
return 0;
}
/*___________________________________________________________*/
int contains_bound_vars(term t)
/* return 1 if t contains DIFF,INTEGRAL, LIMIT, SUM, or PRODUCT */
{ unsigned short n;
int i;
if(ATOMIC(t))
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_bound_vars(ARG(i,t)))
return 1;
}
return 0;
}
/*___________________________________________________________*/
static int contains_fract(term u)
/* u is a sum, power, or product.
Return 1 if u contains fractions that will eventually be
put over a common denom. This is used to block the
use of cancelgcd on a fraction with u in the num or denom */
{ unsigned short f = FUNCTOR(u);
unsigned short n = ARITY(u);
int i;
if(ATOMIC(u))
return 0;
if(f == '^')
return contains_fract(ARG(0,u));
if(f == '/')
return 1;
for(i=0;i<n;i++)
{ if(contains_fract(ARG(i,u)))
return 1;
}
return 0;
}
/*___________________________________________________________*/
static int contains_deriv(term u, term x)
/* return 1 if u contains a derivative with respect to x */
{ int i;
unsigned short n;
if(ATOMIC(u))
return 0;
if(FUNCTOR(u) == DIFF && equals(ARG(1,u),x))
return 1;
n = ARITY(u);
for(i=0;i<n;i++)
{ if(contains_deriv(ARG(i,u),x))
return 1;
}
return 0;
}
/*___________________________________________________________*/
static int trigfactor(term t)
/* t is a sum of arity 2 or 3. Unless each summand is a sin or cosine,
the function will fail, returning 0. Success will be indicated by
a nonzero value. Determine whether sumofsin, difofsin, sumofcos, difofcos
should be used. In case of arity 2, return 1,2,3,or 4 to indicate which
of these operators (in the listed order) should be tried. In case
of arity 3, just return 1. All four operators will be tried.
The operator itself will decide which of the three args to
work on.
The following code makes them be tried when the args of the
trig functions are integer multiples of the same symbolic part,
and in case n == 2, both are even or both odd, so fractions won't
be created, and at least one is more than 2, because for sin 2x,
you might as well expand.
These operations have to be in pre_ops, else trig functions
get expanded (in some topics) in post_ops. When n is two
you might as well just expand.
*/
{ term a,b,c,p,q,r;
long ka, kb;
term na,nb,nc,sa,sb,sc;
int sign = 1;
unsigned short fa,fb,fc;
unsigned short n = ARITY(t);
assert(FUNCTOR(t) == '+');
a = ARG(0,t);
b = ARG(1,t);
if(n == 3)
c = ARG(2,t);
if(NEGATIVE(a))
{ sign = -1;
a = ARG(0,a);
}
if(NEGATIVE(b))
{ sign = sign < 0 ? -2: -1;
b = ARG(0,b);
}
fa = FUNCTOR(a);
fb = FUNCTOR(b);
if(fa != SIN && fa != COS)
return 0;
if(fb != SIN && fb != COS)
return 0;
if(n == 2 && fa != fb)
return 0;
p = ARG(0,a);
q = ARG(0,b);
if(get_currenttopic() == _trig_factor)
{ /* only require p and q be nonconstant */
if(constant(p) || constant(q))
return 0;
goto out;
}
if(n == 3)
{ fc = FUNCTOR(c);
if(NEGATIVE(c))
{ c = ARG(0,c);
sign = sign < 0 ? sign-1 : -1;
}
if(fc != SIN && fc != COS)
return 0;
r = ARG(0,c);
}
/* Now, p and q, and r if n==3, must both be integer multiples of the
same thing. */
ratpart2(p,&na,&sa);
if(!INTEGERP(na))
return 0;
ratpart2(q,&nb,&sb);
if(!INTEGERP(nb) || (equals(na,nb) && n == 2))
return 0;
if(!equals(sa,sb))
return 0;
if(n==3)
{ ratpart2(r,&nc,&sc);
if(!INTEGERP(nc))
return 0;
if(equals(na,nb) && equals(na,nc))
return 0; /* all three args equal */
if(!equals(sa,sc))
return 0;
if(equals(na,nc) || equals(nb,nc))
return 0; /* all 2 coefficients must be different */
if(sign == -3)
return 0; /* all 3 terms negative */
return 1;
}
if(n == 2 && !SOLVETYPE(get_problemtype()))
{ /* the coefficients must be both even or both odd, so fractions
won't be created */
ka = INTDATA(na);
kb = INTDATA(nb);
if( (ka + kb) & 1)
return 0;
goto out;
}
out:
if(sign == -2)
return 0; /* both terms negative */
if(fa == SIN && sign > 0)
return 1; /* sumofsin */
if(fa == SIN && sign == -1)
return 2; /* difofsin */
if(fb == COS && sign > 0)
return 3; /* sumofcos */
if(fb == COS && sign == -1)
return 4; /* difofcos */
assert(0);
return 1;
}
/*_______________________________________________________________*/
static int suppress_trig_expansion(term u, term x)
/* u is an integrand, x the variable of integration.
Even though u may contain trig
functions with different arguments, it may be that
cos 2x should not be expanded, because
next sin^2 x will be written in terms of cos 2x and
then the substitution v = 2x will be made. This
function detects the case in which u contains
only cos (2n)x and sin^2 mx, cos^2 mx so that this
can happen, where 2n and m are specific integers.
But, consider integral( 1/(1-sin^2 x),x). Here we
want to wait till 1-sin^2 x is rewritten cos^2 x, we
don't want to write sin^2 x in terms of cos 2x.
Indeed in general we don't want to use these half-angle
formulas in the denominator of an integrand. Therefore
return 0 if u contains a fraction with a non-constant
denominator.
Return 0 to suppress trig expansion, 1 not to
suppress it.
*/
{ unsigned short f = FUNCTOR(u);
unsigned short n;
int i;
term v;
if(ATOMIC(u))
return equals(u,x) ? 0 : 1;
if(f == COS)
{ v = ARG(0,u);
if(FUNCTOR(v) == '*' && ARITY(v) == 2 &&
INTEGERP(ARG(0,v)) && ISEVEN(ARG(0,v)) &&
equals(ARG(1,v),x)
)
return 1;
else
return 0;
}
if(f == '^' && (FUNCTOR(ARG(0,u)) == SIN || FUNCTOR(ARG(0,u)) == COS) &&
INTEGERP(ARG(1,u)) && ISEVEN(ARG(1,u)) /* even exponent */
)
{ v = ARG(0,ARG(0,u));
if(equals(x,v))
return 1;
if(FUNCTOR(v) == '*' && ARITY(v) == 2 &&
INTEGERP(ARG(0,v)) && equals(ARG(1,v),x)
)
return 1;
return 0;
}
if(f == '+' && ARITY(u) == 2 &&
FUNCTOR(ARG(0,u)) == '^' && FUNCTOR(ARG(1,u)) == '^' &&
equals(ARG(1,ARG(0,u)),two) && equals(ARG(1,ARG(1,u)),two)
)
{ term a,b;
a = ARG(0,ARG(0,u));
b = ARG(0,ARG(1,u));
if(equals(ARG(0,a),ARG(0,b)) &&
((FUNCTOR(a)==COS && FUNCTOR(b)==SIN) || (FUNCTOR(b)==COS && FUNCTOR(a)==SIN))
)
return 0; /* integrand is sin^2 + cos^2 , don't expand */
}
if(f == '/' && contains(ARG(1,u),FUNCTOR(x)))
return 0;
n = ARITY(u);
for(i=0;i<n;i++)
{ if(!suppress_trig_expansion(ARG(i,u),x))
return 0;
}
return 1;
}
/*___________________________________________________________*/
static int intsub_on_sum(term t,term x)
/* Return 1 if intsub should be tried on the integrand t,
which is assumed to be a sum. x is the variable of integration.
For example: if t = sin x cos x + sin^2 x cos x, we want
to use u = sin x on the whole integral, rather than splitting
into two integrals and introducing the same substitution twice.
On the other hand, if t = 1-1/x, we do NOT want to use
u = - 1/x, because it leads to (u^2+1)/u and soon loops,
except for the name of the variables. It seems reasonable
that if any summand (after stripping off minus signs and
constant denominators) is not a product or quotient,
or if any summand is a rational function,
then intsub shouldn't be used, at least not before intsum.
Even algebraic function: consider 1/sqrt(x) + 1/sqrt(x-1),
which leads to a mess upon substituting u = sqrt x.
*/
{ unsigned short n = ARITY(t);
int i;
term v;
if(status(intsub) == UNKNOWN && get_whichpass() == 0)
return 0;
for(i=0;i<n;i++)
{ v = ARG(i,t);
if(NEGATIVE(v))
v = ARG(0,v);
if(FRACTION(v) && !contains(ARG(1,v),FUNCTOR(x)))
v = ARG(0,v);
if(ATOMIC(v))
return 0;
if(FUNCTOR(v) != '*' && FUNCTOR(v) != '/')
return 0;
if(algebraic_in2(v,x))
return 0;
}
return 1; /* every summand is a product or quotient, and
none is a rational function */
}
/*______________________________________________________________________*/
static int logtrig(term u)
/* return 1 if u contains a subterm ln(f(x)), where f is
csc, sec, cot, or tan; or with log instead of ln
*/
{ unsigned short f,n;
int i;
if(ATOMIC(u))
return 0;
if(FUNCTOR(u) == LN || FUNCTOR(u) == LOG)
{ f = FUNCTOR(ARG(0,u));
if(f==TAN || f==COT || f==SEC || f==CSC)
return 1;
return 0;
}
n = ARITY(u);
for(i=0;i<n;i++)
{ if(logtrig(ARG(i,u)))
return 1;
}
return 0;
}
/*___________________________________________________________________*/
static int contains_power(term t, term x)
/* return 1 if t contains a power of x */
{ unsigned short n;
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && equals(x,ARG(0,t)))
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_power(ARG(i,t),x))
return 1;
}
return 0;
}
/*____________________________________________________________________*/
static int bivariate(term t)
/* return 1 if t contains powers of two or more different variables */
{ int nvariables = get_nvariables();
term *atomlist;
int i,count;
if(nvariables < 2)
return 0; /* quickly */
nvariables = variablesin(t,&atomlist);
if(nvariables < 2)
return 0;
count = 0;
for(i=0;i<nvariables;i++)
{ if(contains_power(t,atomlist[i]))
++count;
}
if(count < 2)
return 0;
return 1;
}
/*______________________________________________________________________*/
static int blockintlinearity(term u, term x)
/* u is an integrand which is a sum. Return 1 if
there is a good reason not to split the integral into a sum
of integrals. For example, if the integrand
is tan^2 x + 1, sin^2 x + cos^2 x, or sec^2 x - tan^2 x.
This does not need to trap cases where integration by
substitution should be used, as intsub_on_sum does that
and is called first.
*/
{ term a,b,c;
int sign = 1;
if(FUNCTOR(u) != '+')
return 0;
if(ARITY(u) != 2)
return 0;
a = ARG(0,u);
b = ARG(1,u);
if(NEGATIVE(a) && NEGATIVE(b))
{ a = ARG(0,a);
b = ARG(0,b);
}
if(ONE(a))
{ c = a;
a = b;
b = c;
}
if(ONE(b))
{ /* check for tan^2 + 1 */
if(FUNCTOR(a) != '^' || !equals(ARG(1,a),two))
return 0;
if(FUNCTOR(ARG(0,a)) == TAN && equals(ARG(0,ARG(0,a)),x))
return 1;
/* This way we won't transform integral(tan^2 u + 1,x) to
integral(sec^2 u,x), unless u is just x. */
return 0;
}
if(NEGATIVE(a))
{ c = a;
a = b;
b = c;
}
if(NEGATIVE(b))
{ sign = -1;
b = ARG(0,b);
}
if(FUNCTOR(a) != '^' || !equals(ARG(1,a),two))
return 0;
if(FUNCTOR(b) != '^' || !equals(ARG(1,b),two))
return 0;
a = ARG(0,a);
b = ARG(0,b);
if(!equals(ARG(0,a),ARG(0,b)))
return 0;
if(sign > 0 && FUNCTOR(a) == SIN && FUNCTOR(b) == COS)
return 1;
if(sign > 0 && FUNCTOR(b) == SIN && FUNCTOR(a) == COS)
return 1;
if(sign < 0 && FUNCTOR(b) == SEC && FUNCTOR(a) == TAN)
return 1;
if(sign < 0 && FUNCTOR(a) == SEC && FUNCTOR(b) == TAN)
return 1;
return 0;
}
/*________________________________________________________________*/
static int perfect_square(long x)
/* return nonzero if x is a perfect square, 0 if not */
{ double z = sqrt(x);
if(fabs(z * z - x) < 0.2)
return 1;
return 0;
}
/*________________________________________________________________*/
static int perfect_power(long x, long n)
/* return nonzero if x is a perfect n-th power, 0 if not */
{ double z = pow(x,1/(double) n);
if(fabs(pow(z,n) - x ) < 0.2)
return 1;
return 0;
}
/*_________________________________________________________________*/
static int block_regroupterms(term t)
/* t is a sum. Return 1 if pushminusin can work on a subterm of t,
else return 0. */
{ unsigned short n = ARITY(t);
int j;
term u;
assert(FUNCTOR(t) == '+');
for(j=0;j<n;j++)
{ u = ARG(j,t);
if(NEGATIVE(u) && FUNCTOR(ARG(0,u)) == '+')
return 1;
if(FUNCTOR(u) == '+' && block_regroupterms(u))
return 1;
}
return 0;
}
/*______________________________________________________________________*/
static int trignegative(term t)
/* return 1 if t contains a trig function with a negative argument */
{ int i;
unsigned short f,n;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(TRIGFUNCTOR(f) && NEGATIVE(ARG(0,t)))
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(trignegative(ARG(i,t)))
return 1;
}
return 0;
}
/*______________________________________________________________________*/
static int count_abs(term t)
/* Return the number of non-econstant ABS subterms of t.
Nested ABS terms will not be counted.
*/
{ unsigned short n;
int i;
int count = 0;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t)==ABS)
return econstant(t) ? 0 : 1;
n = ARITY(t);
for(i=0;i<n;i++)
count += count_abs(ARG(i,t));
return count;
}
/*__________________________________________________________________*/
static int local_contains_trig(term t)
/* return 1 if t contains a trig functor */
/* local copy of the function in polyval.dll, for speed */
{ unsigned short n,f;
int i;
if(ATOMIC(t))
return 0;
n = ARITY(t);
f = FUNCTOR(t);
if(f == SIN || f == COS || f == TAN || f == COT || f == CSC || f == SEC)
return 1;
for(i=0;i<n;i++)
{ if(local_contains_trig(ARG(i,t)))
return 1;
}
return 0;
}
/*________________________________________________________________________*/
static int permits_collection(term t)
/* return 1 if t is a sum which permits additive cancellation or
collection, or a product one of whose factors permits collection,
or a power with an integer exponent whose base permits collection.
Otherwise return 0.
*/
{ unsigned short f = FUNCTOR(t);
unsigned short n = ARITY(t);
int i;
term u;
if(ATOMIC(t))
return 0;
if(f == '^')
{ if(INTEGERP(ARG(1,t)))
return permits_collection(ARG(0,t));
return 0;
}
if(f == '*')
{ for(i=0;i<n;i++)
{ if(permits_collection(ARG(i,t)))
return 1;
}
return 0;
}
if(f == '+')
return collect(t,&u);
/* collect returns nonzero if something collects or cancels */
return 0;
}
/*________________________________________________________________________*/
static int contains_big_power(term t)
/* return 1 if t contains a power x^n with n an integer > 50 */
{ unsigned i,n;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && ISINTEGER(ARG(1,t)) && INTDATA(ARG(1,t)) > 50)
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_big_power(ARG(i,t)))
return 1;
}
return 0;
}
static int stop_cancelgcd(term t)
/* t is the num or denom of a fraction. If t is a sum that
permits collecting or cancelling of additive terms,
or contains as a summand products of sums that can be multiplied out,
or contains fractions, or contains sums that can be
content_factored, return 1.
Also, if t can be content_factored or contains a factor that
can be content_factored, return 1, because although cancelgcd will
do it, if it factors both num and denom it can't tell ShowStep
what to select.
Also, stop it in case t contains a power x^n with n an integer > 50.
Otherwise return 0.
*/
{ int i;
unsigned short n;
term c,s,u;
if(ATOMIC(t))
return 0;
if(contains_fract(t))
return 1;
if(expandable_sum(t))
return 1;
if(FUNCTOR(t) == '+')
{ if(permits_collection(t))
return 1;
if(!content_factor(t,&c,&s))
return 1;
return 0;
}
if(FUNCTOR(t) == '*')
{ n = ARITY(t);
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(!content_factor(u,&c,&s))
return 1;
}
}
if(contains_big_power(t))
return 1;
return 0;
}
/*______________________________________________________________________*/
static int stop_differenceofsquares(term t)
/* t is an arity 2 sum. Return 1 in order to
prevent t from being factored by differenceofsquares.
Example: don't factor 1-1/x^2 = (1-1/x)(1+1/x), because
this only complicates the problem rather than simplifying it.
Example 2: x^(2n)- x^(2m), which should be content-factored,
provided n-m is an integer.
Return 0 to proceed with factoring.
*/
{ term u,v,p,q,temp;
unsigned short f,g;
if(FUNCTOR(t) != '+' || ARITY(t) != 2)
return 1; /* assert(0) */
u = ARG(0,t);
v = ARG(1,t);
if(NEGATIVE(u))
u = ARG(0,u);
if(NEGATIVE(v))
v = ARG(0,v);
f = FUNCTOR(u);
g = FUNCTOR(v);
if(f == '^' && g == '^' && equals(ARG(0,u),ARG(0,v)))
{ p = ARG(1,u);
q = ARG(1,v);
if(INTEGERP(p) && INTEGERP(q))
return 1;
polyval(sum(p,tnegate(q)),&temp);
if(INTEGERP(temp))
return 1;
}
if(f == '^')
u = ARG(0,u);
if(g == '^')
v = ARG(0,v);
if(FRACTION(v) && !numerical(ARG(1,v)))
return 1;
if(FRACTION(u) && !numerical(ARG(1,u)))
return 1;
return 0;
}
/*______________________________________________________________________*/
static int commondenom_in_preops(term t)
/* return 1 if commondenom needs to be applied to t before
the summands are simplified.
Specifically, if t has the form sqrt(u)/a +b/ sqrt u
or some closely related forms, where a doesn't contain a
square root not a factor of b.
Also, if t is inside a derivative, return 0; never use
common denoms before differentiating.
Return 0 otherwise.
*/
{ term a,b,cancelled,trash;
int err;
if(FUNCTOR(t) != '+')
return 0;
if(ARITY(t) > 2)
return 0; /* too complicated to worry about */
if(difflag)
return 0; /* don't use common denoms inside derivatives. */
a = ARG(0,t);
b = ARG(1,t);
if(NEGATIVE(a))
a = ARG(0,a);
if(NEGATIVE(b))
b = ARG(0,b);
if(FRACTION(a) && FRACTION(b))
{ err = cancel(ARG(0,a),ARG(1,b),&cancelled,&trash);
if(!err && contains(cancelled,SQRT))
return 1;
err = cancel(ARG(0,b),ARG(1,a),&cancelled,&trash);
if(!err && contains(cancelled,SQRT))
return 1;
return 0;
}
if(FRACTION(b))
{ err = cancel(a,ARG(1,b),&cancelled,&trash);
if(!err && contains(cancelled,SQRT))
return 1;
return 0;
}
if(FRACTION(a))
{ err = cancel(b,ARG(1,a),&cancelled,&trash);
if(!err && contains(cancelled,SQRT))
return 1;
return 0;
}
return 0;
}
/*_________________________________________________________________________*/
static int nearly_done(term t)
/* t is an equation. Return 1 if it's an identity within a very few
simple steps of completion. Then it looks silly to substitute.
Return 0 otherwise.
*/
{ term u,v,x,temp,num,denom;
unsigned short f,g,h1,h2;
if(FUNCTOR(t) != '=')
return 0;
u = ARG(0,t);
v = ARG(1,t);
if(NEGATIVE(u) && NEGATIVE(v))
{ u = ARG(0,u);
v = ARG(0,v);
}
f = FUNCTOR(u);
g = FUNCTOR(v);
if(TRIGFUNCTOR(g) && !TRIGFUNCTOR(f)) /* then swap */
{ temp = u;
u = v;
v = temp;
f = FUNCTOR(u);
g = FUNCTOR(v);
}
if(!TRIGFUNCTOR(f))
return 0;
x = ARG(0,u);
if(g == '/')
{ num = ARG(0,v);
denom = ARG(1,v);
h1 = FUNCTOR(num);
h2 = FUNCTOR(denom);
if(!ONE(num) && !TRIGFUNCTOR(h1) || !TRIGFUNCTOR(h2))
return 0;
if(!equals(ARG(0,num),x) || !equals(ARG(0,denom),x))
return 0;
switch(f)
{ case TAN:
if(h1 == SIN && h2 == COS)
return 1;
if(ONE(num) && h2 == COT)
return 1;
return 0;
case COT:
if(h1 == COS && h2 == SIN)
return 1;
if(ONE(num) && h2 == TAN)
return 1;
return 0;
case SEC:
if(ONE(num) && h2 == COS)
return 1;
return 0;
case CSC:
if(ONE(num) && h2 == SIN)
return 1;
return 0;
case SIN:
if(ONE(num) && h2 == CSC)
return 1;
return 0;
case COS:
if(ONE(num) && h2 == SEC)
return 1;
return 0;
}
}
return 0;
}
/*_________________________________________________________________________*/
static int sumoflogs(term t)
/* return 1 if t is a sum of log terms, 0 if not */
{ unsigned short n,g;
term u;
int i,count=0;
if(FUNCTOR(t) != '+')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ u = ARG(i,t);
g = FUNCTOR(u);
if(g == LN || g == LOG || g == LOGB)
++count;
}
if(count == n)
return 1;
return 0;
}
/*_________________________________________________________________________*/
static int stop_maxsub(term u)
/* return 1 to block the use of maximalsub on u*/
{ unsigned short f;
term c,s;
int currenttopic = get_currenttopic();
int problemtype = get_problemtype();
if(!SOLVETYPE(problemtype) && CALCULUS_TOPIC(currenttopic))
return 1; /* e.g. when sec(arctan(sqrt 5 u)) has arisen
as part of an answer to an integral, don't set
v = arctan(sqrt 5 u) while simplifying. */
if(NEGATIVE(u))
u = ARG(0,u);
if(ATOMIC(u))
return 1;
f = FUNCTOR(u);
if (FRACTION(u) && !econstant(denom(u)) && noxious(denom(u)))
return 1;
if(
FUNCTOR(u) == '+' && ARITY(u) == 2 &&
(
(SIGNEDFRACTION(ARG(1,u)) && noxious(denom(ARG(1,u)))) ||
(SIGNEDFRACTION(ARG(0,u)) && noxious(denom(ARG(0,u))))
)
)
return 1;
if(FUNCTOR(u) == '=' && nearly_done(u))
return 1;
if(FUNCTOR(u) == '+' && contains(u,'/') && immediate_comdenom(u))
return 1;
if(different_sqrts(u))
return 1;
if(f == '*' && special_difofsquares(u))
return 1;
if(f == '/' &&
(special_difofsquares(ARG(1,u)) || special_difofsquares(ARG(0,u)))
)
return 1;
if(f == '+' && sumoflogs(u))
/* example, log(sqrt(x) + 1) + log(sqrt(x)-1) */
/* stop it on a sum of logs. This may be overzealous, we'll see. */
return 1;
if(f == '*')
{ ratpart2(u,&c,&s);
if(FUNCTOR(s) == '+' && sumoflogs(s))
return 1;
}
return 0;
}
/*_________________________________________________________________*/
int almost_algebraic(term t)
/* return 1 if t has the form a/c +/- sqrt(b)/d for
integers a,b,c,d. (We want to trap these in preops and
simplify them immediately to algebraic number standard form,
to prevent wasting steps with addfractions and commondenom,
contenfactor, etc.)
Also works on x + a/c +/- sqrt(b)/c
since such expressions are produced by the quadratic formula.
Otherwise return 0.
*/
{ term a,b,c,d,u,v;
if(FUNCTOR(t) != '+')
return 0;
if(ARITY(t) > 3)
return 0;
if(ARITY(t) == 2)
{ u = ARG(0,t);
v = ARG(1,t);
}
else if(ARITY(t) == 3 && ISATOM(ARG(0,t)))
{ u = ARG(1,t);
v = ARG(2,t);
}
else
return 0;
if(NEGATIVE(u))
u = ARG(0,u);
if(NEGATIVE(v))
v = ARG(0,v);
if(!FRACTION(u) || !FRACTION(v))
return 0;
a = ARG(0,u);
c = ARG(1,u);
b = ARG(0,v);
d = ARG(1,v);
if(FUNCTOR(b) != SQRT)
return 0;
if(!INTEGERP(ARG(0,b)))
return 0;
if(!INTEGERP(a) || !INTEGERP(c) || !INTEGERP(d))
return 0;
return 1;
}
/*__________________________________________________________________*/
int sqrtcomdenom(term t)
/* called from stopsub in autoeqn.c to prevent using substitutions on examples
like (3/sqrt(x)- 9sqrt(x))^2 = 6x-2, in which common denoms should be used
instead of substitution. Return 1 if after stripping away minus signs and
integer exponents from t we are left with a sum on which commondenom_in_preops
returns 1. */
{ term u = t;
while(NEGATIVE(u) ||
(FUNCTOR(u) == '^' && isinteger(ARG(1,u)))
)
u = ARG(0,u);
return commondenom_in_preops(u);
}
/*___________________________________________________________________*/
static int trigproducts(term t)
/* t is a sum of arity 2.
Return nonzero if sinsumrev, sindifrev, cossumrev, or cosdifrev
will work leaving a sin or cos of a non-sum.
Example: sin(x-a)cos(x+a) + cos(x-a)sin(x+a) = sin(2x)
Return 1,2,3,or 4 to tell which of these operators (in the listed
order) should be used. Return 0 if none of them will work
leaving a sin or cos of a non-sum.
*/
{ unsigned short n = ARITY(t);
term u,v,a,b,c,d;
unsigned short fa,fb,fc,fd;
int sign = 1;
char buffer[DIMREASONBUFFER];
term w;
if(FUNCTOR(t) != '+' || n != 2)
return 0;
u = ARG(0,t);
v = ARG(1,t);
if(NEGATIVE(u))
{ u = ARG(0,u);
sign *= -1;
}
if(NEGATIVE(v))
{ v = ARG(0,v);
sign *= -1;
}
if(FUNCTOR(u) != '*' || FUNCTOR(v) != '*')
return 0;
if(ARITY(u) != 2 || ARITY(v) != 2)
return 0; /* giving up on 2 sin(x-a) cos(x+a) + 2 cos(x-a) sin(x+a) = 2 sin 2x,
unfortunately, but this will come up very rarely. */
a = ARG(0,u);
b = ARG(1,u);
c = ARG(0,v);
d = ARG(1,v);
fa = FUNCTOR(a);
fb = FUNCTOR(b);
fc = FUNCTOR(c);
fd = FUNCTOR(d);
if(fa != SIN && fa != COS)
return 0;
if(fb != SIN && fb != COS)
return 0;
if(fc != SIN && fc != COS)
return 0;
if(fd != SIN && fd != COS)
return 0;
if(
(fa == SIN && fb == SIN && fc == COS && fd == COS) ||
(fa == COS && fb == COS && fc == SIN && fd == SIN)
)
{ if(sign == 1 && !cosdifrev(t,zero,&w,buffer) && FUNCTOR(ARG(0,w)) != '+')
return 4;
if(sign == -1 && !cossumrev(t,zero,&w,buffer) && FUNCTOR(ARG(0,w)) != '+')
return 3;
}
if(fa == fb || fc == fd)
return 0;
if(sign == 1 && !sinsumrev(t,zero,&w,buffer) && FUNCTOR(ARG(0,w)) != '+')
return 1;
if(sign == -1 && !sindifrev(t,zero,&w,buffer) && FUNCTOR(ARG(0,w)) != '+')
return 2;
return 0;
}
/*__________________________________________________________________*/
static int trigargsgcd(term t, term *gcd)
/* return 0 if there is a non-trivial gcd of the args of all the
trig functions in t and the initial value of *gcd. (So initially,
it will be called with *gcd = zero, and then called recursively with
other arguments.)
For example, if t is a function of sin (8x) and trig functions of 2x,
and *gcd is initially zero, return 0. But DO NOT count fractional gcds,
e.g. on sin^4(x/2) + cos^4(x/2), return 1. Put the gcd of the trig args
and the initial *gcd into *gcd if 0 is returned. If 1 is returned,
*gcd can be garbage. If there are no trig functions in t, return 2.
*/
{ unsigned short n,f;
int i,err,retval;
term s;
if(ATOMIC(t))
return 2;
f = FUNCTOR(t);
if(TRIGFUNCTOR(f))
{ polygcd(ARG(0,t),*gcd,&s);
if(ONE(s))
return 1;
*gcd = s;
return 0;
}
n = ARITY(t);
retval = 2;
for(i=0;i<n;i++)
{ err = trigargsgcd(ARG(i,t),gcd);
if(err == 1 || ONE(*gcd))
return 1;
if(err == 0)
retval = 0;
}
if(ONE(*gcd))
return 1;
return retval;
}
/*__________________________________________________________________*/
static int trigargsgcd1(term t)
/* return 0 if there is a non-trivial gcd of the args of all the
trig functions in t, and this gcd either has no fractional part or
the denominator of the fractional part is not even.
For example, if t is a function of sin (8x) and
trig functions of 2x, return 0. But DO NOT count fractional gcds with
even denominators, e.g. on sin^4(x/2) + cos^4(x/2), return 1.
*/
{ term temp = zero;
term c,s;
int err = trigargsgcd(t,&temp);
if(err)
return 1;
if(ISATOM(temp))
return 0; /* e.g. t may involve cos(x/2) and sin x, so temp is x */
if(FRACTION(temp) && iseven(ARG(1,temp)))
return 1;
if(FUNCTOR(temp) == '*')
{ ratpart2(temp,&c,&s);
if(FRACTION(c) && iseven(ARG(1,c)))
return 1;
}
return 0;
}
/*_________________________________________________________*/
static unsigned short tpf_aux(term t, unsigned short a, unsigned short b)
/* t is an equation. If one side is an even power of a term with functor b,
and the other side doesn't contain b, then return b.
Otherwise, if b appears to an odd power, return a,
and if a appears to an odd power, return b, and if neither appears to an odd
power return a, unless t doesn't contain b, in which case return b.
*/
{ int i,aflag,bflag;
term u;
if(ARITY(t) != 2)
return a;
for(i=0;i<2;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '^' &&
iseven(ARG(1,u)) &&
FUNCTOR(ARG(0,u)) == b &&
!contains(ARG(i ? 0 : 1,t),b)
)
return b;
}
bflag = contains_odd_power(t,b);
aflag = contains_odd_power(t,a);
if(bflag)
return a;
if(aflag)
return b;
if(!contains(t,b))
return b;
return a;
}
/* ___________________________________________________________________*/
int algebraic_identity(term t)
/* Return 1 if t is an equation whose two sides polyval to the
same term. Return 0 otherwise.
*/
{ term u,v;
int saveit = get_polyvalfactorflag();
set_polyvalfactorflag(1);
if(FUNCTOR(t) != '=')
return 0;
polyval(ARG(0,t),&u);
polyval(ARG(1,t),&v);
set_polyvalfactorflag(saveit);
if(equals(u,v))
return 1;
return 0;
}
/*__________________________________________________________________*/
int contains_power_of_sum(term t)
/* return 1 if t contains a low integer power of a sum, 0 if not. */
{ unsigned short n;
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && ISINTEGER(ARG(1,t)) &&
FUNCTOR(ARG(0,t)) == '+' && INTDATA(ARG(1,t)) <= 10
)
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_power_of_sum(ARG(i,t)))
return 1;
}
return 0;
}
/*____________________________________________________________________*/
static unsigned short twologs(term t)
/* return nonzero if t is a sum containing two logs, possibly multiplied
by an integer; 0 if not. The nonzero
return value is LN, LOG, or LOGB, whichever was found.
*/
{ int k;
unsigned short n,f,g;
term u;
if(FUNCTOR(t) != '+')
return 0;
n = ARITY(t);
f = 0;
for(k=0;k<n;k++)
{ u = ARG(k,t);
if(NEGATIVE(u))
u = ARG(0,u);
if(FUNCTOR(u) == '*' && ARITY(u) == 2 && INTEGERP(ARG(0,u)))
u = ARG(1,u);
g = FUNCTOR(u);
if(g == LOG || g == LN)
{ if(f && g == f)
return g;
else
f = g;
}
else if( g == LOGB)
{ if(f && g == f)
return g;
else
f = g;
}
else
continue;
}
return 0;
}
/*_________________________________________________________*/
static int forced_logcollect(unsigned short f, term t)
/* f is LN, LOG, or LOGB. t is a sum containing two or more summands
with functor f, or an integer times a term with functor f.
If two of these summands (possibly after discarding the initial integer)
have the form f(a) + f(b/a)
or the form f(ab)-f(a), including numerical cases like ln 6 - ln 2,
return 1. In general, if the result of collecting logs would permit
a cancel or cancelgcd operation, return 1.
Also, log(sqrt(x)+2) + log(sqrt(x)-2) should be collected. But
log(x-1) + log(x+1) should not be, at least it's not forced by this function.
Otherwise return 0.
*/
{ int i,j,err;
unsigned short n = ARITY(t);
term a,b,u,v,p,q;
int signu, signv;
assert(FUNCTOR(t) == '+');
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(NEGATIVE(u))
{ signu = -1;
u = ARG(0,u);
}
else
signu = 1;
if(FUNCTOR(u) == '*' && ARITY(u) == 2 && INTEGERP(ARG(0,u)))
u = ARG(1,u);
if(FUNCTOR(u) != f)
continue;
for(j=i+1;j<n;j++)
{ v = ARG(j,t);
if(NEGATIVE(v))
{ signv = -1;
v = ARG(0,v);
}
else
signv = 1;
if(FUNCTOR(v) == '*' && ARITY(v) == 2 && INTEGERP(ARG(0,v)))
v = ARG(1,v);
if(FUNCTOR(v) != f)
continue;
if(f == LOGB && !equals(ARG(0,v),ARG(0,u)))
continue;
a = ARG(f == LOGB ? 1: 0,u);
b = ARG(f == LOGB ? 1: 0,v);
if(signu != signv)
{ if(!FRACTION(a) && !FRACTION(b)) /* ln ac - ln c */
{ err = cancel(a,b,&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(a,b,&p,&q);
if(!err)
return 1;
}
else if(FRACTION(a) && !FRACTION(b))
{ /* ln(b/a) - ln b */
err = cancel(ARG(0,a),b,&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(ARG(0,a),b,&p,&q);
if(!err)
return 1;
}
else if(FRACTION(b) && !FRACTION(a))
{ err = cancel(ARG(0,b),a,&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(ARG(0,b),a,&p,&q);
if(!err)
return 1;
}
else /* both fractions */
{ err = cancel(product(ARG(0,a),ARG(0,b)),product(ARG(1,a),ARG(1,b)),&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(ARG(0,a),ARG(0,b),&p,&q);
if(!err)
return 1;
}
}
else /* same sign */
{ if(FRACTION(b) && !FRACTION(a)) /* ln a + ln(c/a) */
{ err = cancel(a,ARG(1,b),&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(a,ARG(1,b),&p,&q);
if(!err)
return 1;
}
else if(FRACTION(a) && !FRACTION(b))
{ err = cancel(b,ARG(1,a),&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(b,ARG(1,a),&p,&q);
if(!err)
return 1;
}
else if(FRACTION(a) && FRACTION(b))
{ err = cancel(ARG(0,a),ARG(1,b),&p,&q);
if(!err)
return 1;
err = cancel(ARG(0,b),ARG(1,a),&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(ARG(0,a),ARG(1,b),&p,&q);
if(!err)
return 1;
err = cancelgcd_aux(ARG(0,b),ARG(1,a),&p,&q);
if(!err)
return 1;
}
else if(FUNCTOR(a) == '+' && FUNCTOR(b) == '+' &&
contains(a,SQRT) && contains(b,SQRT)
)
{ term temp = product(a,b);
char buffer[DIMREASONBUFFER];
term w;
int err = difofsquares(temp,zero,&w,buffer);
RELEASE(temp);
return !err;
}
}
}
}
return 0;
}
/*__________________________________________________________________________*/
static int contains_odd_power(term t, unsigned short f)
/* return 1 if t contains a subterm with functor f, to an odd
power or not to a power (i.e. power 1). Nested occurrences of f
will not be counted, and occurrences inside functors other than
those used in polynomials will not be counted. */
{ int i;
unsigned short n,g;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && FUNCTOR(ARG(0,t)) == f)
{ return isodd(ARG(1,t)) ? 1 : 0;
}
if(FUNCTOR(t) == f)
return 1;
n = ARITY(t);
g = FUNCTOR(t);
if(g != '-' && !INEQUALITY(g) && g != '*' && g != '+' && g != '^')
return 0;
for(i=0;i<n;i++)
{ if(contains_odd_power(ARG(i,t),f))
return 1;
}
return 0;
}
/*__________________________________________________________________________*/
static int contains_even_power(term t, unsigned short f)
/* return 1 if t contains a subterm with functor f, to an even
positive power. Nested occurrences of f
will not be counted, and occurrences inside functors other than
those used in polynomials will not be counted. */
{ int i;
unsigned short n,g;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && FUNCTOR(ARG(0,t)) == f)
{ return iseven(ARG(1,t)) ? 1 : 0;
}
if(FUNCTOR(t) == f)
return 0;
n = ARITY(t);
g = FUNCTOR(t);
if(g != '-' && !INEQUALITY(g) && g != '*' && g != '+' && g != '^')
return 0;
for(i=0;i<n;i++)
{ if(contains_even_power(ARG(i,t),f))
return 1;
}
return 0;
}
/*________________________________________________________________________*/
int stop_orderterms(term t)
/* Return 1 if t, the term at the end of the current path,
is in a product in a sum, so the product will be multiplied out by 'expand'
anyway. Don't waste time ordering the terms.
Also return 1 if t is the arg of a ROOT or SQRT and does not contain
a power. There's no point in changing sqrt(x+h) to sqrt(h+x).
Also return 1 if t is an integrand. Intlinear will be applied,
no point in ordering the sum. Also if the sum contains an integral.
Also return 1 if t is one side of an equation in a set of linear
equations.
Also when solving complex equations, use polynomial order or complexform instead.
*/
{ unsigned short const *path = get_path();
int pathlength = get_pathlength();
int i, sumflag=0, productflag=0;
unsigned short f = FUNCTOR(t);
int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
term u;
if(get_complex() && iscomplex(t) &&
(problemtype == SOLVE_EQUATION ||
currenttopic == _complex_quadratics ||
currenttopic == _complex_cubics
)
)
return 1; /* use writeaspoly instead, so we get ix^2 + 1 instead of 1 + x^2 i */
if(pathlength == 0 && INEQUALITY(f))
{ for(i=0;i<2;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '+' && expandable_sum(u))
return 1;
}
return 0;
}
if(pathlength & 1)
--pathlength; /* in postops, delete the functor of the current sum */
if(pathlength >= 2 &&
(path[pathlength-2] == INTEGRAL || path[pathlength-2] == DIFF)
)
return 1;
if(pathlength >= 2 &&
(path[pathlength-2] == SQRT || path[pathlength-2] == ROOT) &&
!contains(t,'^')
)
return 1;
if(pathlength >= 2 &&
(path[pathlength-2] == INTEGRAL ||
path[pathlength-2] == DIFF ||
path[pathlength-2] == LIMIT
)
)
return 1;
if(contains(t,INTEGRAL) || contains(t,DIFF) || contains(t,LIMIT))
return 1; /* wait till the calculus is done before ordering terms */
if(pathlength >= 4 && path[pathlength-4] == INTEGRAL &&
path[pathlength-3] == 1 &&
path[pathlength-2] == '/' &&
path[pathlength-1] == 1
)
return 1; /* don't reorder terms in the numerator of an integrand */
if(problemtype == LINEAR_EQUATIONS &&
pathlength >= 2 &&
path[pathlength-2] == '='
)
return 1; /* don't reorder terms in linear equations because
lineupvars will be called soon and will take care of it. */
for(i=0;i<pathlength; i += 2)
{ if(path[i] == '+' && !sumflag)
{ sumflag = 1;
continue;
}
if(sumflag &&
(
(path[i] == '^' && path[i+1] == 1) ||
path[i] == '+' ||
path[i] == '-'
)
)
continue;
if(sumflag && path[i] == '*')
{ productflag = 1;
continue;
}
if(productflag == 1 && path[i] == '^' && path[i+1] == 1)
continue;
sumflag = productflag = 0;
}
return productflag;
}
/*________________________________________________________________________*/
int stop_orderfactors(term t)
/* Return 1 if t, the term at the end of the current path,
is a product which contains a sum, and there is a '+' on
the path to t, so eventually it will be multiplied out.
Also return 1 if the parent is DIFF or INTEGRAL or SUM or PRODUCT
Don't waste time reordering the factors.
Also return 1 on ix^2 in problemtype SOLVE_EQUATION
*/
{ unsigned short const *path = get_path();
int pathlength = get_pathlength();
int i;
unsigned short f = FUNCTOR(t);
if(f != '*')
return 0;
if(ORDERED(t))
return 1;
if(get_complex() &&
iscomplex(t) && !constant(t) &&
get_problemtype() == SOLVE_EQUATION
)
return 1;
if(pathlength < 2)
return 0;
if(pathlength >= 2 &&
(path[pathlength-2] == INTEGRAL || path[pathlength-2] == DIFF ||
path[pathlength-2] == SUM || path[pathlength-2] == PRODUCT
)
)
return 1;
if(!expandable(t))
return 0;
if(pathlength & 1)
--pathlength; /* in postops, delete the functor of the current sum */
for(i=0;i<pathlength; i += 2)
{ if(path[i] == '+')
return 1;
}
return 0;
}
/*________________________________________________________________________*/
static int contains_complex_exponentials(term t)
/* return 1 if t contains e^complex, 0 if not */
{ unsigned short n;
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t)== '^' && equals(ARG(0,t),eulere) && iscomplex(ARG(1,t)))
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_complex_exponentials(ARG(i,t)))
return 1;
}
return 0;
}
/*________________________________________________________________________*/
static int stoplogofpower(term u)
/* u is a power. Return 1 if it has one of the forms a^(log(a,b)),
e^ln(b), 10^log(b), or the same with other factors in the exponents */
{ char buffer[DIMREASONBUFFER];
term w;
if(!logbinexponent(u,zero,&w,buffer))
return 1;
if(!logbinexponent2(u,zero,&w,buffer))
return 1;
if(!lninexponent(u,zero,&w,buffer))
return 1;
if(!lninexponent2(u,zero,&w,buffer))
return 1;
if(!loginexponent(u,zero,&w,buffer))
return 1;
if(!loginexponent2(u,zero,&w,buffer))
return 1;
return 0;
}
/*_______________________________________________________________________*/
static int trigalg_same_aux(term u, term x, term *arg)
/* return 1 if u is built up algebraically from trig functions of
*arg, if *arg is not ILLEGAL; if *arg is ILLEGAL, fill it in
as the arg of the first trig function encountered as a subterm of u.
But if that arg is just x itself, fail. Return 0 for failure.
*/
{ unsigned short n;
int i;
unsigned short f = FUNCTOR(u);
if(TRIGFUNCTOR(f) && contains(ARG(0,u),FUNCTOR(x)))
{ if(equals(ARG(0,u),x))
return 0;
if(FUNCTOR(*arg) != ILLEGAL && !equals(ARG(0,u),*arg))
return 0;
if(FUNCTOR(*arg) == ILLEGAL)
*arg = ARG(0,u);
return 1;
}
if(ATOMIC(u))
return 1;
n = ARITY(u);
for(i=0;i<n;i++)
{ if(!trigalg_same_aux(ARG(i,u),x,arg))
return 0;
}
return 1;
}
/*_______________________________________________________________________*/
static int trigalg_same(term u, term x)
/* return 1 if u is built up algebraically from trig functions of
the same argument, not just x itself. Return 0 otherwise.
*/
{ term arg;
int rval;
SETFUNCTOR(arg,ILLEGAL,0);
rval = trigalg_same_aux(u,x,&arg);
if(rval == 0)
return 0;
if(FUNCTOR(arg) == ILLEGAL)
return 0; /* no trig function found */
if(equals(arg,x))
return 0; /* per specs */
return 1;
}
/*___________________________________________________________________*/
static int exponential_factor(term t,term x)
/* return 1 if t is a product containing a factor e^u where u contains x,
but is not linear in x,
or if t is a fraction whose num or denom contains such a factor; or if
the first arg is c^u(x), as in 2^x/(1+2^x)^2 */
{ unsigned short n;
int i;
term u;
if(FRACTION(t))
return exponential_factor(ARG(0,t),x) || exponential_factor(ARG(1,t),x);
if(FUNCTOR(t) == '^' && !contains(ARG(0,t),FUNCTOR(x)) && contains(ARG(1,t),FUNCTOR(x)))
return 1;
if(FUNCTOR(t) != '*')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '^' &&
!contains(ARG(0,u),FUNCTOR(x)) &&
contains(ARG(1,u),FUNCTOR(x)) &&
!is_linear_in(ARG(1,u),x)
)
return 1;
}
return 0;
}
/*___________________________________________________________________*/
static int stop_arith(term t)
/* if t contains more than one term of the forms 0+x, 0*u, 1*u
where u is not numerical, return nonzero. In general, return the
number of such subterms. (This is used to prevent using 'arithmetic'
on such terms--polyvalop will be used instead.)
*/
{ unsigned short n,f;
int i, count;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
n = ARITY(t);
switch(f)
{ case '+':
count = 0;
for(i=0;i<n;i++)
{ if(ZERO(ARG(i,t)))
++count;
count += stop_arith(ARG(i,t));
}
return count;
case '*':
count = 0;
for(i=0;i<n;i++)
{ if(ZERO(ARG(i,t)) || ONE(ARG(i,t)))
++count;
count += stop_arith(ARG(i,t));
}
return count;
}
count = 0;
for(i=0;i<n;i++)
count += stop_arith(ARG(i,t));
return count;
}
/*____________________________________________________________*/
static int obvious_trig(term t)
/* return 1 if t contains a sum of the form cos^2 x + sin^2 x,
0 otherwise
*/
{ unsigned short i,n;
int err;
term lhs,a,temp;
if(ATOMIC(t))
return 0;
n = ARITY(t);
if(FUNCTOR(t) == '+')
{ lhs = sum(make_power(sin1(var0),two),make_power(cos1(var0),two));
err = match(t,lhs,one,&a,&temp); /* instantiate a and temp */
if(!err)
return 1;
}
for(i=0;i<n;i++)
{ if(obvious_trig(ARG(i,t)))
return 1;
}
return 0;
}
/*____________________________________________________________*/
static int complex_addfractions_conditions(term t)
/* t is a sum of 2 fractions, and t contains complexi.
Should we use addfractions?
Return 1 if so, 0 if not
*/
{ term u = ARG(0,t);
term v = ARG(1,t);
term num1,num2, den1, den2;
if(NEGATIVE(u))
u = ARG(0,u);
if(NEGATIVE(v))
v = ARG(0,v);
if(!FRACTION(u) || !FRACTION(v))
return 0; /* assert(0) */
num1 = ARG(0,u);
num2 = ARG(0,v);
den1 = ARG(1,u);
den2 = ARG(1,v);
if(!equals(den1,den2))
return 0; /* addfractions won't work anyway */
if(iscomplex(den1))
{ if(mvpoly(num1) && mvpoly(num2))
return 1;
return 0;
}
if(iscomplex(num1) && iscomplex(num2))
return 1;
return 0;
/* don't add fractions in sqrt(3)/2^(3/4) + i/2^(3/4) */
}
/*___________________________________________________________*/
static int common_factor(term u, term v)
/* return 1 if u and v have a common factor that
might be cancelled out in the identity u = v.
Do not check if the conditions for cancellation
are actually satisfied; the operation will do that.
*/
{ int err;
term a,b;
while(FRACTION(u) || NEGATIVE(u))
u = ARG(0,u);
while(FRACTION(v) || NEGATIVE(v))
v = ARG(0,v);
err = cancel(u,v,&a,&b);
return !err;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists