Sindbad~EG File Manager
/* post_ops: post-associated operators, used together with pre_ops
to describe the automode simplification algorithm of Mathpert.
M. Beeson
Original date 1.7.91
Last modified 3.30.99
1.4.00 added tanhi and cothi;
1.4.00 added code to get sinhdef, tanhdef, cothdef, coshdef used
when the argument is a natural log.
1.5.00 added oksqrt and oksqrt_aux
1.13.00 added code to get sin((arctan(8)-pi_term)/2) simplified by apart
1.13.00 added secrecip and cscrecip
1.15.00 modified conditions for rectangulartopolar and complexexponential
2.27.00 added missing 'break' at line 1429
2.27.00 added statefinalbound1 and statefinalbound2
3.2.00 added call to series_ops
4.2.00 added intervaltoabs1 and intervaltoabs2
12.01.00 modified sqrtexp_conditions
6.17.04 modified post_ops so sqrtsimp isn't used on complex problems
6.18.04 modified conditions for complexexponential in postops so it's not used inside SQR or ROOT.
put in complexsqrt and complexroot
removed the modification made yesterday about sqrtsimp.
6.21.04 modified conditions for rectangulartopolar
Added !is_complex at line 1985
8.20.04 Added pathlength==0 etc. at line 1755 to stop expanding (x^2+y^2)^3 at toplevel
1.23.06 Added sumtodifofsums0
1.25.06 added rejectpoint under MATRIX
1.28.06 don't use introducelninexponent on a power that's a summand, see line 2063.
Modified logbases, so that it won't count terms of the form log(b,b^z).
9.2.07 modified conditions for complexform so that k pi i/3 will be left alone
9.4.07 removed lnofi, and modified conditions for lnofnegative
4.23.13 changed ARG(1,v) to ARG(0,v) around line 1561, two places.
5.1.13 added code to autoseries to get lnofquotient used when problemtype is POWERSERIES
5.6.13 include <stddef.h>
5.8-5.13.13 worked on autoseries and powers_match
5.13.13 modified sqrtexp_conditions and elimfractexp_conditions, and conditions in post_ops for rootexp
and conditions for introducenegexp in autoquo.
5.14.13 corrected condition to use lnseries, and constantintosigma
added sinsqhalf2 and cossqhalf2 to autoseries
added code to autoseries not to expand a series under an exponent
5.15.13 added code to autoseries to not expand inside any function.
5.20.13 added cotseries to autoseries
added code to only apply lnofquotient (in calculus) when it won't reduce the domain, i.e. when num and denom are both positive
[undid this change 5.30.13, as (1) it does not reduce the domain because ABSFUNCTOR is introduced, and (2) we need to use lnofquotient
inside derivatives and integrals.
5.21.13 added apartandcancel where apart was, inside integrals and series
added code to get eliminatenegexp used in expanding ln(sin(x)/x) in a power series
5.24.13 added secseries to autoseries.
5.29.13 commented out two unused variables in postops, and one in autoexp
corrected = to == at line 1768
5.31.13 added trigrat2 condition for introducenegexp in an integrand, to deal with sin^4 x/cos^2 x.
Only introducenegexp in POWERSERIES for fractional exponents.
6.3.13 added binomialseries to autoseries for fractions
Used betterbyparts to help control introducenegexp
6.4.13 put recipofi into autoquo and autoexp
6.5.13 modified conditions for postops to call series_toplevel
6.7.13 added factorialrecursion to autoquo.
6.9.13 replaced call to contains_infinite_series with contains_unconvergent_series
7.13.13 added code to prevent rectangular_to_polar being used on _complex_cubics
12.10.14 added innumflag at line 2021
3.18.23 changed !trigpolyflag == to trigpolyflag != several places.
3.19.23 changed get_nvariables to get_nvariables() line 1724
5.7.24 added the dated line to get autotrig called on sin(acos x) etc.
5.7.24 added is_complex() to get autotrig called on sin(i) etc.
12.7.24 barred checknumerically from being used on complex terms
12.31.24 added code to use certain operators on TESTCONVERGENCE etc.
1.7.25 changed seriesintdif to seriesintdifdef at all occurrences
1.26.25 modified conditions for cottosincos in autoexp
1.26.25 put logbinexponent2 into autoexp
1.29.25 changed conditions for introducelninexponent under DIFFERENTIATE_FROM_DEFN
1.30.25 added dated line to autoquo
1.30.25 added dated line to autoexp
*/
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "mpdoc.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 "probtype.h" /* set_control_flags needs values of problemtype */
#include "prover.h"
#include "polynoms.h"
#include "exec.h"
#include "algaux.h"
#include "order.h"
#include "automode.h"
#include "symbols.h"
#include "pvalaux.h" /* complex_number */
#include "cflags.h" /* get_currenttopic */
#include "cancel.h" /* contains_undefined */
#include "autosimp.h" /* get_whichpass etc */
#include "ssolve.h" /* solved */
#include "maxsub.h" /* locate */
#include "nfactor.h" /* small_prime */
#include "eqn.h" /* econstant */
#include "trigpoly.h" /* stricttrigpoly */
#include "preops.h" /* get_trigpolyflag, algebraic_identity */
#include "islinear.h" /* is_linear */
#include "autosum.h" /* contains_arctrigs */
#include "sqrts.h" /* fractexps */
#include "deval.h" /* deval */
#include "relrates.h" /* used */
#include "surdsimp.h" /* canonical */
#include "binders.h" /* get_binders */
#include "domain.h" /* contains_defined_variables */
#include "scontrol.h" /* series_ops */
#include "intsub.h" /* betterbyparts */
static int fractional_factor(term);
static int sqrt_condition(term,term);
static int contains_integer_sqrt(term);
static int contains_integer_root(term,term);
static int in_sum(void);
static int unsolved_lineqns(term t);
static int elimfractexp_conditions(unsigned pathlength, unsigned short *path);
static int ok_complexexp(unsigned short *path, int pathlength);
static int stop_logofproduct(term t);
static int stop_changebase(void);
static int rationalizable_cubic(term denom);
static int insumfract(void);
static int sg_ok(void);
static int solved_cubic(term t);
static int oksqrt(term t);
static void autoexp(term t,actualop *o, int *nops);
static void autoquo(term t,actualop *o, int *nops);
static void autoseries(term t, actualop *o, int *nops);
static int powers_match(term t);
/*_____________________________________________________________________*/
static int hflag;
/* set in preops under '=' when the identity contains exponentials AND
cosh or sinh, or on topic _hyperfunctions1, where you are supposed to
convert hyperbolic functions to exponentials.
If nonzero, coshdef and sinhdef can be used when whichpass == 0.
If zero, they have to wait until whichpass > 1. Before hflag existed,
they always waited until whichpass > 1, which means common denoms sneak in
first, making ugly solutions.
*/
void set_hflag(int n)
{ hflag = n;
}
int get_hflag(void)
{ return hflag;
}
/*_____________________________________________________________________*/
void post_ops(term t, term *arg, actualop *o, int *nops)
/* fill in operators o[i] that might work on t; use *arg to supply
one argument for such an operator. *arg is used only in automode solving
of equations and linear equations and when it used to return an arg, the
operator in question must certainly work. If it is not used then *arg
will not be changed; so the calling function should set it to ILLEGAL and
check if it changes. */
{ unsigned short f = FUNCTOR(t);
term u;
unsigned short g;
int i=0;
double z;
int whichpass = get_whichpass();
const int intflag = get_intflag();
const int indexflag = get_indexsumflag();
const int seriesflag = get_seriesflag();
const int limitflag = get_limitflag();
const int difflag = get_difflag();
const int inpowerflag = get_inpowerflag();
const int indenomflag = get_indenomflag();
const int innumflag = get_innumflag();
// const int insqrtflag = get_insqrtflag(); // unused
// const int inrootflag = get_inrootflag(); // unused
int currenttopic = get_currenttopic();
int problemtype = get_problemtype();
int pathlength = get_pathlength();
unsigned short *path = get_path();
int trigexpandflag, factorflag, expandflag, logcollectflag,radicalflag;
int trigpolyflag = get_trigpolyflag();
term cancelled, trash,v,w;
if(ISINTEGER(t))
{ if(pathlength >= 2 && path[pathlength-2] == LOGB && path[pathlength-1] == 2)
{ o[i] = writeintegeraspower; ++i; /* log(2,16) => log(2,2^4) */
}
}
if(ATOMIC(t))
{ if(currenttopic == _minmax && equals(t,falseterm))
{ o[i] = addendpoints; ++i;
o[i] = addundefinedpoints; ++i;
*nops = i;
return;
}
else if(problemtype == INEQUALITIES && equals(t, trueterm))
{ o[i] = explicitdomain; ++i;
*nops = i;
return;
}
else if(equals(t,complexi) &&
pathlength <= 5 &&
currenttopic == _polar_form &&
!in_sum() &&
!(pathlength >=2 && path[0] == '^' && path[1] == 2)
/* don't do it on e^( pi i), or e^((pi/2) i), etc. */
)
{ o[i] = rectangulartopolar; ++i;
*nops = i;
return;
}
goto finish;
}
if(is_complex(t) && pathlength >= 3 &&
currenttopic != _complex_cubics && // where it produces too-long complicated calculations
(path[pathlength-3] == SQRT || path[pathlength-3] == ROOT) &&
(path[pathlength-3] == ROOT || complexparts(ARG(0,t),&w,&v) != 0) &&
/* sqrt(a+bi) is NOT computed by polar form, so only proceed if can't compute u = a+bi */
(
(ATOMIC(t) || f == '*' || f == '^' || f == '/' ) ||
(f == '-' &&
( ATOMIC(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '*' ||
FUNCTOR(ARG(0,t)) == '^' || FUNCTOR(ARG(0,t)) == '/'
)
)
)
)
{ o[i] = rectangulartopolar; ++i; /* sqrt(u) with u complex; bring u to polar form */
}
trigexpandflag = get_trigexpandflag();
expandflag = get_expandflag();
factorflag = get_factorflag();
logcollectflag = get_logcollectflag();
radicalflag = get_radicalflag();
if(whichpass == 5 && /* don't do this till the very end */
ARITY(t) == 1 && OBJECT(ARG(0,t)) && TYPE(ARG(0,t)) == DOUBLE &&
f != SQRT && /* computeroot will be used */
f != '-' && f != DEG
)
{ o[i] = computefunction; ++i;
goto finish;
}
if(TRIGFUNCTOR2(f) && /* trig and arctrig functors */
(!(trigexpandflag & 2) /* bit 1 set means only one trig or arctrig function occurs */
|| (trigexpandflag & 1)
/* bit 0 set to zero means all trig or arctrig functions have the same arg */
|| (trigexpandflag & (1<<4)) // bit 4 set means only TAN and SEC appear
|| (trigexpandflag & (1<<8)) // bit 8 set means only COT and CSC appear
|| seminumerical(ARG(0,t)) /* You still need to evaluate trig functions
even if only one occurs */
|| (TRIGFUNCTOR(f) && ARCTRIGFUNCTOR((FUNCTOR(ARG(0,t))))) // 5.7.24
|| (TRIGFUNCTOR(f) && FUNCTOR(ARG(0,t)) == '*' &&
ARCTRIGFUNCTOR(FUNCTOR(ARG(1,ARG(0,t)))) && equals(ARG(0,ARG(0,t)),two) &&
ARITY(ARG(0,t)) == 2 // sin(2 arcos theta) for example
)
|| iscomplex(t) // sin(i) for example
|| get_complex() // 12.8.24
)
)
{ autotrig(t,o,nops);
i = *nops;
goto finish;
}
switch(f)
{ case '-' :
u = ARG(0,t);
if(currenttopic == _polar_form && pathlength <= 1 &&
( equals(u,complexi) || ATOMIC(u) || (FUNCTOR(u) == '*' && iscomplex(u)))
)
{ if(ATOMIC(u))
{ o[i] = minustopolar; ++i;
}
else
{ o[i] = rectangulartopolar; ++i;
}
}
if(currenttopic == _polar_form && pathlength <= 5 &&
!inpowerflag && !get_infractionflag() &&
!in_sum()
)
{ o[i] = minustopolar; ++i;
}
else if(FRACTION(u) &&
FUNCTOR(ARG(0,u)) == '+' &&
NEGATIVE(ARG(1,ARG(0,u)))
)
{ o[i] = minusintonum; ++i;
}
else if(FUNCTOR(u) == '*' &&
FRACTION(ARG(0,u)) &&
FUNCTOR(ARG(0,ARG(0,u))) == '+' &&
NEGATIVE(ARG(1,ARG(0,ARG(0,u))))
)
{ o[i] = minusintonum; ++i;
/* example: -((e^-x - e^x)/2)i */
}
else if(FRACTION(u) && pathlength >= 3 &&
path[pathlength-3] == SQRT && FUNCTOR(ARG(0,u)) == '+'
)
{ o[i] = minusintonum; ++i;
}
if(FRACTION(u) && pathlength >= 3 &&
path[pathlength-3] == SQRT && FUNCTOR(ARG(1,u)) == '+'
)
{ o[i] = minusintodenom; ++i;
}
if(FRACTION(u))
{ o[i] = minusoutfromdenom33; ++i; /* -a/(b-c) = a/(c-b) */
o[i] = minusoutfromdenom22; ++i; /* -a/(-b-c) = a/(b+c) */
o[i] = minusoutfromnum22; ++i; /* -(-a-b)/c = (a+b)/c */
}
break;
case '+' :
autosum(t,o,&i);
break;
case '*' :
autoproduct(t,o,&i);
break;
case '^' :
autoexp(t,o,&i);
break;
case '/' :
autoquo(t,o,&i);
break;
case MATRIX:
if(problemtype == MINMAX)
{ o[i] = rejectpoint; ++i;
o[i] = addlimits; ++i;
o[i] = selectmax; ++i;
o[i] = selectmin; ++i;
break;
}
case MATRIXINVERSE:
if(numerical(t))
{ o[i] = exactmatrixinverse; ++i;
o[i] = decimalmatrixinverse; ++i;
}
break;
case SQRT:
u = ARG(0,t);
if(OBJECT(u) && TYPE(u) == DOUBLE && whichpass == 5)
{ o[i] = computeroot; ++i;
goto finish;
}
if(INTEGERP(u) || FUNCTOR(u) == '*')
{ o[i] = factorundersqrt; ++i;
}
if(OBJECT(u))
{ o[i] = evaltorational; ++i;
}
if(NOTDEFINED(u))
{ o[i] = sqrtinfinity; ++i;
}
if((!get_complex() || seminumerical(u)) && sqrtexp_conditions(u))
{ o[i] = sqrtexp; ++i;
break;
}
if(ISATOM(u))
{ if(equals(u,complexi))
{ o[i] = sqrtofi; ++i;
}
break;
}
g = FUNCTOR(u);
switch(g)
{ case '*' :
o[i] = sqrtsimp; ++ i; /* sqrt (x^2 y) = x sqrt y */
if(is_complex(u))
{ o[i] = sqrtofi; ++i;
o[i] = complexsqrt; ++i; /* sqrt( a e^it) = sqrt(a) e^(it/2) */
}
if(
!factorflag ||
(innumflag && !(factorflag & 101)) ||
(indenomflag && !(factorflag & 0x110))
)
/* sqrt((a-b)(a+b)) = sqrt(a^2-b^2) */
{ o[i] = multiplyoutundersqrt; ++i;
}
break;
case '^' :
o[i] = sqrtsimp; ++ i; /* sqrt(x^2y) = x sqrt y */
if(is_complex(u))
{ o[i] = complexsqrt; ++i; /* sqrt( a e^it) = sqrt(a) e^(it/2) */
}
break;
case '-' :
/* sqrtofminus1 and sqrtofneg are in preops */
if(is_complex(t))
{ o[i] = sqrtofminusi; ++i;
}
break;
case '/' :
if((!get_complex() || seminumerical(t)) &&
sqrt_condition(history(get_currentline()),t) &&
!limitflag
)
{ o[i] = sqrtofquotient2; ++i;
/* This introduces absolute value signs rather than
make assumptions about the signs of the new sqrts */
}
break;
/* Example: sqrt(2/3) is left alone but sqrt(2/3) + 5 is
rewritten sqrt 2/sqrt 3 + 5 = (sqrt 2 + 5 sqrt 3)/sqrt 3 = (sqrt 6 + 15) /3.
Conditions for sqrtofquotient here must mesh with
those for quotientofsqrts in pre_ops to avoid a loop. */
case '+' :
o[i] = factorpolyundersqrt; ++i;
if(intflag) /* but not indexflag or seriesflag */
{ o[i] = completethesquare1; ++i;
}
if(is_complex(t))
{ o[i] = sqrtofaminusbi; ++i;
o[i] = sqrtofaplusbi; ++i;
}
break;
case SQRT:
o[i] = sqrtofsqrt; ++i;
break;
case ROOT:
o[i] = sqrtofroot; ++i;
break;
}
break;
case ROOT:
u = ARG(1,t);
if(OBJECT(u) && TYPE(u) == DOUBLE && whichpass == 5)
{ o[i] = computeroot; ++i;
goto finish;
}
if(INTEGERP(u) || FUNCTOR(u) == '*')
{ o[i] = factorunderroot; ++i;
}
if(NOTDEFINED(u))
{ o[i] = rootinfinity; ++i;
goto finish;
}
if((radicalflag < 0 && !algebraic_number(ARG(1,t))) ||
iscomplex(ARG(1,t)) ||
( get_complex() &&
NEGATIVE(ARG(1,t)) && obviously_nonnegative(ARG(0,ARG(1,t)))
/* whether odd or even, when computing complex roots of
negative numbers use fractional exponents. */
) ||
( get_problemtype() == POWERSERIES && get_seriesflag() == 0)
)
{ if(FUNCTOR(ARG(1,t)) == '^')
{ o[i] = rootpowerexp; ++i;
}
else
{ o[i] = rootexp; ++i;
}
break;
}
if(ISATOM(u))
{ i = 0;
goto finish;
}
if(OBJECT(u))
{ o[i] = evaltorational; ++i;
}
if(equals(ARG(0,t),two))
{ o[i] = roottosqrt; ++i;
break;
}
if(seminumerical(u) && !NEGATIVE(u) && !get_complex())
{ o[i] = rootofminus; ++i;
/* Needed in solving cubic equations, to simplify e.g.
root(3, 1/2 - (1/18)sqrt 93). But when complex
numbers are on, we use complexrootminus instead,
which is in preops to preempt rootexp. */
}
g = FUNCTOR(u);
switch(g)
{ case '-' :
if(!seminumerical(u))
{ o[i] = rootofminus; ++i;
}
else if(!deval(u,&z) && z != BADVAL && z < 0.0)
{ o[i] = rootofminus; ++i;
}
/* but don't use rootofminus on e.g. root(3,-2(1-sqrt(3)));
keep a positive quantity under the root. */
break;
case '/' :
if( !limitflag &&
( !equals(t,history(get_currentline()))
|| (INTEGERP(ARG(0,u)) && !rootfree(ARG(0,u),(unsigned) INTDATA(ARG(0,t))))
|| (INTEGERP(ARG(1,ARG(0,t))) && !rootfree(ARG(1,ARG(0,t)),(unsigned) INTDATA(ARG(0,t)) ))
/* indices of roots never exceed 0xffff */
|| contains(ARG(0,u),'^')
|| contains(ARG(1,u),'^')
)
)
{ o[i] = rootofquotient; ++i;
}
break;
case '*' :
/* rootsimp is in preops. It need not also be here */
if(!factorflag ||
(innumflag && !(factorflag & 1)) ||
(indenomflag && !(factorflag & 0x10))
)
/* root(n,(a-b)(a+b)) = ^na^2-b^2) */
{ o[i] = multiplyoutunderroot; ++i;
}
if(is_complex(u))
{ o[i] = complexroot; ++i;
}
break;
case '^' :
if(radicalflag == -1)
break;
o[i] = rootofpower2; ++i;
o[i] = rootofpower; ++i;
if(is_complex(u))
{ o[i] = complexroot; ++i;
}
break;
case '+' :
if(NEGATIVE(ARG(0,u)))
{ o[i] = rootofminus; ++i;
}
o[i] = factorpolyunderroot; ++i;
break;
case SQRT:
o[i] = rootofsqrt; ++i;
break;
case ROOT:
o[i] = rootofroot; ++i;
break;
}
break;
case LOG:
u = ARG(0,t);
if(FUNCTOR(u) == ABSFUNCTOR)
u = ARG(0,u);
if(NOTDEFINED(u))
{ o[i] = lninfinity; ++i;
break;
}
if(OBJECT(u))
{ o[i] = logofone; ++i;
o[i] = logoften; ++i;
if(!inpowerflag && !logcollectflag)
{ o[i] = factoroutbase10 ; ++i;
/* Note, pathlength is even when OBJECT(u), since objects
are NOT tacked onto the end of the path */
if((pathlength >= 2 && path[pathlength-2] == '+') ||
(pathlength >= 4 && path[pathlength-2] == '-' && path[pathlength-4] == '+') ||
currenttopic == _logarithms
)
/* No point in factoring inside a log
unless the factored and expanded log
has something to combine with. */
{ o[i] = factornumberinlog; ++i;
}
}
}
g = FUNCTOR(u);
if(g == '^' && equals(ARG(0,u),ten))
{ o[i] = logofpowerof10; ++i;
}
if( (inpowerflag || logcollectflag) &&
/* leave LOG alone in exponents or when collecting logs */
(g != '*' || SOLVETYPE(problemtype)) &&
/* except logs of products with only one
nonconstant factor can still be expanded, but not when solving equations. */
problemtype < LIMITS
)
break;
if(contains(history(get_currentline()),LN) ||
problemtype >= LIMITS
)
{ o[i] = logtoln; ++i;
}
switch(g)
{ case '^':
/* logofpower is in preops */
break;
case '*':
if(limitflag)
break;
if(inpowerflag || problemtype >= LIMITS)
break;
if(!logcollectflag && !stop_logofproduct(t))
{ o[i] = logofproduct; ++i;
}
break;
case '/':
if(limitflag)
break;
o[i] = logofreciprocal; ++i;
if(!(pathlength == 1 && !is_linear(ARG(0,u)) &&
!constant(ARG(0,u)) && !is_linear(ARG(1,u))
/* is_linear returns 0 on linear arguments */
)
)
{ o[i] = logofquotient; ++i;
}
break;
}
break;
case LN:
u = ARG(0,t);
if(FUNCTOR(u) == ABSFUNCTOR)
u = ARG(0,u);
if(ZERO(u))
{ o[i] = lnzero; ++i;
break;
}
if(NOTDEFINED(u))
{ o[i] = lninfinity; ++i;
break;
}
if(equals(u,eulere))
{ o[i] = lnofe; ++i;
}
if(OBJECT(u))
{ o[i] = lnofone; ++i;
if(!inpowerflag && !logcollectflag)
{ o[i] = factornumberinlog; ++i;
}
}
if(iscomplex(u))
{ o[i] = complexln; ++i;
o[i] = complexlntopolarform; ++i;
}
g = FUNCTOR(u);
if(g == '-' && !ONE(ARG(0,u))) // we don't want ln(-1) in one step in automode
{ o[i] = lnofnegative; ++i;
}
if(g == '^' && equals(ARG(0,u),eulere))
{ o[i] = lnofpowerofe; ++i;
}
else if(g == '+' &&
limitflag && !inpowerflag &&
ONE(ARG(0,u)) &&
problemtype == DIFFERENTIATE_FROM_DEFN
/* We need this to differentiate ln x from defn,
but all other times it makes trouble */
)
{ /* ln(1+b) = b ln((1+b)^(1/b)),
but only inside a limit and NOT in a power,
because if done in a power it screws up the
results of limexptolog, e.g. on lim(x->0, (1-2x)^x)
and only if b is going to zero.
The operator itself checks the latter condition.
This operator is needed to diff ln x from defn.
*/
o[i] = lnofpowerreverse; ++i;
}
if(inpowerflag || logcollectflag)
/* leave LN alone in exponents or when collecting logs */
/* Exception: ln x = a + ln x^2 for example; this kind of
equation doesn't yield to attract-collect-isolate.
These examples are handled in auto mode by an operator
that works on equations, called unitelogargs */
break;
switch(g)
{ case '^':
/* lnofpower is in preops */
break;
case '*':
if(limitflag)
break;
if(!logcollectflag)
{ o[i] = lnofproduct; ++i;
}
break;
case '/':
if(limitflag || (problemtype >=LIMITS && !intflag && !difflag))
break;
if(!(
pathlength == 1 && !is_linear(ARG(0,u)) &&
!constant(ARG(0,u)) && !is_linear(ARG(1,u))
)
/* is_linear returns 0 on linear arguments */
)
{ o[i] = lnofquotient; ++i;
}
break;
}
break;
case LOGB:
u = ARG(1,t);
if(FUNCTOR(u) == ABSFUNCTOR)
u = ARG(0,u);
if(equals(ARG(0,t),u))
{ o[i] = logbofb; ++i;
}
if(ONE(u))
{ o[i] = logbofone; ++i;
}
if(equals(ARG(0,t),eulere))
{ o[i] = logbtoln; ++i; /* trap case of base e */
}
if(equals(ARG(0,t),ten))
{ o[i] = logbtolog; ++i;
}
if(pathlength >= 3 && path[pathlength-3] == DIFF)
{ o[i] = logbtolns; ++i;
}
g = FUNCTOR(u);
if(currenttopic == _change_log_base)
{ if(g == '^' && equals(ARG(0,u),eulere))
{ o[i] = logbtolns; ++i;
}
else if(g == '^' && equals(ARG(0,u),ten))
{ o[i] = logbtologs; ++i;
}
/* and in other cases, e.g. log(3,7), should we change to log
or to ln? Let's go for ln. */
else
{ o[i] = logbtolns; ++i;
}
}
if(OBJECT(ARG(0,t)) && !stop_changebase())
{ o[i] = factorlogbase; ++i; /* e.g. log(36,6) = log(6^2,6) */
}
if(OBJECT(u))
{ o[i] = logpowerofbofb; ++i;
if(!inpowerflag && !logcollectflag)
{ o[i] = factoroutbase; ++i;
o[i] = factornumberinlog; ++i;
}
}
if(FUNCTOR(ARG(0,t))=='^')
{ o[i] = logpowerofbofb; ++i;
}
if(FRACTION(ARG(0,t)))
{ o[i] = changebase; ++i; /* in log(1/5,x), change base to 5 */
break;
}
if(inpowerflag)
{ term lastbase = get_lastbase();
if(ATOMIC(lastbase) && !equals(lastbase,ARG(0,t)) &&
/* don't changebase in a compound fraction, simplify the
compound fraction first. The following code only catches
the simplest kinds of compound fractions. */
(pathlength < 5 || ! (path[pathlength-5] == '/' && path[pathlength-3] == '/')) &&
(pathlength < 7 || ! (path[pathlength-7] == '/' && path[pathlength-5] == '/'))
)
{ o[i] = changebase; ++i;
}
}
if(logcollectflag &&
get_whichpass() > 0 && /* don't use it on log(8,80)-log(8,5) */
currenttopic != _change_log_base
/* for that topic it's already been thrown in */
)
{ if(!(pathlength >= 3 && INEQUALITY(path[pathlength-3])))
/* log(b,x) = a will go to x = b^a */
{ o[i] = logbtolns; ++i;
}
break;
}
switch(g)
{ case '^':
o[i] = logbofpowerofb; ++i;
o[i] = logbofpower; ++i;
break;
case '*':
if(!logcollectflag)
{ o[i] = logbofproduct; ++i;
}
break;
case '/':
o[i] = logbofreciprocal; ++i;
if(!(pathlength == 1 && !is_linear(ARG(0,u)) &&
!constant(ARG(0,u)) && !is_linear(ARG(1,u))
/* is_linear returns 0 on linear arguments */
)
)
{ o[i] = logbofquotient; ++i;
}
break;
}
break;
case COSH:
u = ARG(0,t);
if(currenttopic == _hyperfunctions || FUNCTOR(u) == LN || problemtype == POWERSERIES)
{ o[i] = coshdef; ++i;
break;
}
if(NOTDEFINED(u))
{ o[i] = coshinfinity; ++i;
}
if(ZERO(u))
{ o[i]= cosh0; ++i;
}
if(NEGATIVE(u))
{ o[i] = cosheven; ++i; /* cosh(-u) = cosh u */
}
if(iscomplex(u) &&
(FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
)
{ o[i] = coshi; ++i;
}
if(!intflag && !indexflag && !seriesflag && !difflag && FUNCTOR(u) == '+' &&
(!CALCULUS_TOPIC(currenttopic) || contains(u,FUNCTOR(get_eigenvariable()))) &&
(problemtype != TRIG_IDENTITY || pathlength <= 3 ||
path[pathlength-3] == '=' || path[pathlength-3] == '/'
)
/* in automode, only apply this law to one side of an equality.
If cosh(u+v) occurs as a subterm, it's almost always better
to expand in exponentials. */
)
{ o[i] = coshsum; ++i;
}
if(!intflag && !indexflag && !seriesflag && !difflag && !limitflag &&
(problemtype != TRIG_IDENTITY || pathlength <= 3 ||
path[pathlength-3] == '=' || path[pathlength-3] == '/'
) &&
!cancel(u,two,&cancelled,&trash)
)
{ o[i] = doublecosh; ++i;
}
if(
(problemtype == TRIG_IDENTITY || limitflag) &&
(hflag || whichpass > 1)
)
{ o[i] = coshdef; ++i;
}
if(problemtype == SIMPLIFY && INTEGERP(ARG(0,t)))
{ o[i] = coshdef; ++i;
}
break;
case SINH:
u = ARG(0,t);
if(
currenttopic == _hyperfunctions ||
FUNCTOR(u) == LN ||
problemtype == POWERSERIES
)
{ o[i] = sinhdef; ++i;
break;
}
if(NOTDEFINED(u))
{ o[i] = sinhinfinity; ++i;
}
if(NEGATIVE(u))
{ o[i] = sinhodd; ++i; /* sinh(-u) = -sinh u */
}
if(iscomplex(u) &&
(FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
)
{ o[i] = sinhi; ++i;
}
if(!intflag && !indexflag && !seriesflag &&
!difflag && FUNCTOR(u) == '+' &&
/* but definitely use this in limits, e.g. to
differentiate sinh from defn; but only when
the sinh term contains the limit variable!
Not only inside limits, but after the limit has been
evaluated, there's still no use expanding cosh(3x-1) for example. */
(!CALCULUS_TOPIC(currenttopic) || contains(u,FUNCTOR(get_eigenvariable()))) &&
(problemtype != TRIG_IDENTITY || pathlength <= 3 ||
path[pathlength-3] == '=' || path[pathlength-3] == '/'
)
/* in automode, only apply this law to one side of an equality.
If sinh(u+v) occurs as a subterm, it's almost always better
to expand in exponentials. */
)
{ o[i] = sinhsum; ++i;
}
if(ZERO(u))
{ o[i] = sinh0; ++i;
}
if(!intflag && !indexflag && !seriesflag && !difflag && !limitflag &&
!cancel(u,two,&cancelled,&trash) &&
(problemtype != TRIG_IDENTITY || pathlength <= 3 ||
path[pathlength-3] == '=' || path[pathlength-3] == '/'
)
)
{ o[i] = doublesinh; ++i;
}
if((problemtype == TRIG_IDENTITY || limitflag) &&
(hflag || whichpass > 1)
)
{ o[i] = sinhdef; ++i;
}
if(problemtype == SIMPLIFY && INTEGERP(ARG(0,t)))
/* simplify sinh(1) but leave sinh(3/2) alone */
{ o[i] = sinhdef; ++i;
}
break;
case SECH:
u = ARG(0,t);
if(seminumerical(u))
{ o[i] = sechdef; ++i;
}
if(
(problemtype == TRIG_IDENTITY || limitflag ) &&
(hflag || whichpass > 1)
)
{ o[i] = sechdef; ++i;
}
break;
case TANH:
u = ARG(0,t);
if(NOTDEFINED(u))
{ o[i] = sinhinfinity; ++i;
}
if(seminumerical(u) || FUNCTOR(u) == LN)
{ o[i] = tanhdef; ++i;
}
if(iscomplex(u) &&
(FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
)
{ o[i] = tanhi; ++i;
}
if(
(problemtype == TRIG_IDENTITY || limitflag) &&
(hflag || whichpass > 1)
)
{ o[i] = tanhdef; ++i;
}
if(currenttopic == _hyperfunctions)
break;
if(FUNCTOR(u) == LN)
{ o[i] = tanhln; ++i;
}
break;
case CSCH:
u = ARG(0,t);
if(seminumerical(u) || FUNCTOR(u) == LN)
{ o[i] = cschdef; ++i;
}
if(
(problemtype == TRIG_IDENTITY || limitflag) &&
(hflag || whichpass > 1)
)
{ o[i] = cschdef; ++i;
}
break;
case COTH:
u = ARG(0,t);
if(seminumerical(u) || FUNCTOR(u) == LN)
{ o[i] = cothdef; ++i;
}
if(iscomplex(u) &&
(FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
)
{ o[i] = cothi; ++i;
}
if(
(problemtype == TRIG_IDENTITY || limitflag) &&
(hflag || whichpass > 1)
)
{ o[i] = cothdef; ++i;
}
break;
case ASINH:
if(!difflag)
{ o[i] = asinhtoln; ++i;
}
break;
case ATANH:
if(!difflag)
{ o[i] = atanhtoln; ++i;
}
break;
case ACOSH:
if(!difflag)
{ o[i] = acoshtoln; ++i;
}
break;
case SUM:
if(!equals(ARG(3,t),infinity) && !equals(ARG(2,t),minusinfinity))
{ /* a finite summation */
g = FUNCTOR(ARG(0,t));
if(OBJECT(ARG(0,t)))
{ o[i] = sigmaconstant; ++i;
goto finish;
}
o[i] = sigmaconstant; ++i;
if(equals(ARG(0,t),ARG(1,t)))
{ if(!ZERO(ARG(2,t)) && !ONE(ARG(2,t)))
{ o[i] = sumtodifofsums0;++i;
}
else
{ o[i] = sumofi; ++i;
}
}
if(g == '^' &&
equals(ARG(0,ARG(0,t)),ARG(1,t)) &&
ISINTEGER(ARG(1,ARG(0,t)))
)
{ long kk = INTDATA(ARG(1,ARG(0,t)));
if(!ZERO(ARG(2,t))&& !ONE(ARG(2,t)))
{ o[i] = sumtodifofsums0;++i;
}
else if(kk == 2)
{ o[i] = sumofisquared; ++i;
}
else if(kk == 3)
{ o[i] = sumoficubed; ++i;
}
else if(kk == 4)
{ o[i] = sumofitothefourth; ++i;
}
}
if(g == '^' && equals(ARG(1,ARG(0,t)),ARG(1,t)))
{ o[i] = sumofallpowers; ++i;
}
if(EXPANDFLAG ||
problemtype == SIGMA_NOTATION ||
problemtype == TESTCONVERGENCE ||
problemtype == ADDSERIES ||
problemtype == POWERSERIES
)
{ if(status(sigmatosum) >= KNOWN)
{ int nfree = numerical_sum(t);
if(nfree == 2)
{ o[i] = evaluatesigmatorational; ++i;
}
else if(nfree == 1)
{ o[i] = evalpuresigmatorational; ++i;
}
}
o[i] = sigmatosum; ++i;
}
if(problemtype == INDUCTION)
{ term hi = ARG(3,t);
o[i] = evaluatesigmatorational; ++i;
if(FUNCTOR(hi) == '+' && ONE(ARG(1,hi))
&& equals(get_eigenvariable(),ARG(0,hi))
)
{ o[i] = splitofflastterm; ++i;
}
}
switch(g)
{ case '-' :
o[i] = minusoutofsigma; ++i;
break;
case '+' :
o[i] = telescopingsum; ++i;
o[i] = sigmasum; ++i;
o[i] = sigmapoly; ++i;
break;
case '*' : /* fall-through */
case '/' :
o[i] = constantoutofsigma; ++i;
o[i] = sigmapoly; ++i;
break;
}
}
else
{ int k;
series_ops(t,o+i,&k);
i += k;
}
break;
case FACTORIAL:
if(expandflag & 0x0100)
{ o[i] = evaluatefactorial; ++i;
}
break;
case BINOMIAL:
if(expandflag & 0x0100)
{ o[i] = evaluatebinomialcoef; ++i;
}
break;
case AND:
if(problemtype == RELATED_RATES)
{ o[i] = eliminatederivative; ++i;
if(used(DIFEQN))
{ o[i] = substforvar; ++i;
}
break;
}
if(problemtype == LINEAR_EQUATIONS &&
unsolved_lineqns(t)
)
{ if(currenttopic == _eqns_by_substitution)
solve_by_substitution(t,arg,i,o,nops);
else
solve_linear_equations(t,arg,i,o,nops);
return;
}
if(interval_as_and(t))
{ unsigned short ff = FUNCTOR(ARG(0,t));
unsigned short gg = FUNCTOR(ARG(1,t));
term a = ARG(0,ARG(0,t));
term b = ARG(1,ARG(1,t));
term mid = ARG(1,ARG(0,t));
if(pathlength <= 3 &&
solved(t,get_eigenvariable()) &&
!contains_defined_variables(t)
)
{ o[i] = explicitdomain; ++i;
}
if(obviously_nonnegative(mid))
{ if(ff == '<' &&
obviously_positive(strongnegate(a))
)
{ o[i] = gg == '<' ? sqrtinterval3 : sqrtinterval5; ++i;
}
else if(ff == LE &&
obviously_nonnegative(strongnegate(a))
)
{ o[i] = gg == '<' ? sqrtinterval4 : sqrtinterval6; ++i;
}
}
if(FUNCTOR(mid) == '^')
{ if(equals(ARG(1,mid),two))
{ if(FUNCTOR(ARG(0,t)) == '<' && FUNCTOR(ARG(1,t)) == '<')
{ o[i] = sqrtinterval1; ++i;
}
else
{ o[i] = sqrtinterval2; ++i;
}
}
else if(iseven(ARG(1,mid)))
{ if(FUNCTOR(ARG(0,t)) == '<' && FUNCTOR(ARG(1,t)) == '<')
{ o[i] = rootinterval1; ++i;
}
else
{ o[i] = rootinterval2; ++i;
}
}
}
if(FRACTION(mid) &&
econstant(a) && econstant(b) &&
econstant(ARG(0,mid)) &&
!econstant(ARG(1,mid))
)
{ /* reciprocal interval operators */
int signa, signb;
if(obviously_positive(a))
signa = 1;
else if(obviously_negative(a))
signa = -1;
else signa = 0;
if(obviously_positive(b))
signb = 1;
else if(obviously_negative(b))
signb = -1;
else signb = 0;
if(signa == 1 && signb == 1)
{ o[i] = ff == '<' ?
(gg == '<' ? recipinterval11 : recipinterval21):
(gg == '<' ? recipinterval12 : recipinterval22);
++i;
}
if(signa == -1 && signb == -1)
{ o[i] = ff == '<' ?
(gg == '<' ? recipinterval31 : recipinterval41):
(gg == '<' ? recipinterval31 : recipinterval42);
++i;
}
if(signa == -1 && signb == 1)
{ o[i] = ff == '<' ?
(gg == '<' ? recipinterval51 : recipinterval61):
(gg == '<' ? recipinterval52 : recipinterval62);
++i;
}
}
if(SERIES_TOPIC(currenttopic))
{ o[i] = intervaltoabs1; ++i;
o[i] = intervaltoabs2; ++i;
}
}
break;
case OR:
if(problemtype == RELATED_RATES && pathlength <= 1)
{ *nops = 0;
return;
}
o[i] = combineintervals; ++i;
if(get_checksolutionsflag())
{ o[i] = checkroot; ++i;
if(contains(t,PI_ATOM))
{ o[i] = periodicform; ++i;
}
}
if(problemtype == MINMAX)
{ /* addcriticalpoints, addendpoints, addundefinedpoints
are in preops */
o[i] = rejectpoint; ++i;
o[i] = tabulate; ++i;
}
if(currenttopic == _complex_cubics &&
contains_existentials(t) &&
ARITY(t) == 2 && solved_cubic(ARG(0,t)) &&
solved_cubic(ARG(1,t))
)
{ o[i] = translatevar; ++i;
}
if(get_complex() && pathlength == 1 &&
get_whichpass() > 0 &&
contains_existentials(t) &&
!contains_defined_variables(t)
)
{ o[i] = explicitparams; ++i;
}
break;
case SG:
if(ZERO(ARG(0,t)))
{ o[i] = sgzero; ++i;
*nops = i;
return;
}
o[i] = sgpos; ++i;
o[i] = sgneg; ++i;
if(NEGATIVE(ARG(0,t)))
{ o[i] = sgodd; ++i;
}
break;
case ABSFUNCTOR:
o[i] = abspos; ++i;
o[i] = absneg; ++i;
/* don't use absdef in auto mode */
if(intflag && !get_binders() && sg_ok() && !contains(t,SG))
/* !get_binders so we don't introduce SG in a definite integral */
{ o[i] = abssg; ++i; /* abs(x) = x sg(x) */
}
g = FUNCTOR(ARG(0,t));
switch(g)
{ case '^' :
o[i] = abspower; ++i;
break;
case '*' :
o[i] = abslinear; ++i;
break;
case '/' :
o[i] = abslinearquo; ++i;
if(!contains_defined_variables(ARG(0,t)))
/* don't break up abs(a/b) until after
defined variables have been unwound,
e.g. in the answer to an integral,
because a/b may permit cancellations
after the variables are unwound
*/
{ o[i] = absoffraction; ++i;
}
break;
case ROOT:
o[i] = absroot; ++i;
break;
case SQRT:
o[i] = abssqrt; ++i;
break;
}
break;
case ATAN:
o[i] = evalarctan; ++i;
break;
case ACOS:
o[i] = evalarccos; ++i;
break;
case ASIN:
o[i] = evalarcsin; ++i;
break;
case ASEC:
if(seminumerical(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '/')
{ o[i] = evalarcsec; ++i;
}
break;
case ACSC:
if(seminumerical(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '/')
{ o[i] = evalarccsc; ++i;
}
break;
case ACOT:
if(seminumerical(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '/')
{ o[i] = evalarccot; ++i;
}
break;
case '=':
if(problemtype==RELATED_RATES && !contains(t,DIFF) && !used(DIFEQN))
/* don't differentiate more than once */
{ o[i] = difeqn; ++i;
}
if(problemtype==IMPLICIT_DIFF && !contains(t,DIFF) && !used(DIFEQN))
/* don't differentiate more than once */
{ o[i] = difeqn; ++i;
}
if(problemtype == TRIG_IDENTITY && !algebraic_identity(t))
{ unsigned short localtrigflag=0,bf;
if(! is_complex(t))
// don't use checknumerically if complex definitions of trig functions have been used
{ o[i] = checknumerically; ++i;
}
if(whichpass >= 4 && (SIGNEDFRACTION(ARG(0,t)) || SIGNEDFRACTION(ARG(1,t))))
{ o[i] = crossmultiply; ++i;
}
if(FUNCTOR(ARG(0,t)) == '^' && iseven(ARG(1,ARG(0,t))) &&
TRIGFUNCTOR(FUNCTOR(ARG(0,ARG(0,t))))
)
{ set_trigflag(ARG(1,t),&localtrigflag);
bf = FUNCTOR(ARG(0,ARG(0,t))); /* base functor */
if(localtrigflag == (1 << TRIGINDEX(SIN)) && bf == COS)
{ o[i] = cossqtosinsq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(COS)) && bf == SIN)
{ o[i] = sinsqtocossq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(TAN)) && bf == SEC)
{ o[i] = secsqtotansq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(SEC)) && bf == TAN)
{ o[i] = tansqtosecsq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(COT)) && bf == CSC)
{ o[i] = cscsqtocotsq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(CSC)) && bf == COT)
{ o[i] = cotsqtocscsq; ++i;
}
}
if(FUNCTOR(ARG(1,t)) == '^' && iseven(ARG(1,ARG(1,t))) &&
TRIGFUNCTOR(FUNCTOR(ARG(0,ARG(1,t))))
)
{ unsigned short localtrigflag = 0,bf;
set_trigflag(ARG(0,t),&localtrigflag);
bf = FUNCTOR(ARG(0,ARG(1,t))); /* base functor */
if(localtrigflag == (1 << TRIGINDEX(SIN)) && bf == COS &&
trigpolyflag != SIN
)
{ o[i] = cossqtosinsq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(COS)) && bf == SIN &&
trigpolyflag != COS
)
{ o[i] = sinsqtocossq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(TAN)) && bf == SEC &&
trigpolyflag != TAN
)
{ o[i] = secsqtotansq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(SEC)) && bf == TAN &&
trigpolyflag != SEC
)
{ o[i] = tansqtosecsq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(COT)) && bf == CSC &&
trigpolyflag != COT
)
{ o[i] = cscsqtocotsq; ++i;
}
if(localtrigflag == (1 << TRIGINDEX(CSC)) && bf == COT &&
trigpolyflag != CSC
)
{ o[i] = cotsqtocscsq; ++i;
}
}
g = contains_sqrt(t);
if(g == ABSFUNCTOR || g == SQRT)
{ o[i] = squareeqn; ++i;
}
if (g == ROOT)
{ o[i] = powereqn; ++i;
}
if(
(FUNCTOR(ARG(1,t)) == '^' && RATIONALP(ARG(1,ARG(1,t))) && equals(ARG(1,ARG(1,ARG(1,t))),two)) ||
(FUNCTOR(ARG(0,t)) == '^' && RATIONALP(ARG(1,ARG(0,t))) && equals(ARG(1,ARG(1,ARG(0,t))),two))
)
{ o[i] = squareeqn; ++i;
/* squareeqn will only work if the other side can
be checked nonnegative. */
}
else if(
(FUNCTOR(ARG(1,t)) == '^' && RATIONALP(ARG(1,ARG(1,t)))) ||
(FUNCTOR(ARG(0,t)) == '^' && RATIONALP(ARG(1,ARG(0,t))))
)
{ o[i] = powereqn; ++i;
}
}
if(problemtype == LINEAR_EQUATIONS && pathlength < 2 &&
!contains(t,MATRIX) && !contains(t,VECTOR)
)
/* a system of linear equations has degenerated to a
single equation, e.g. because one reduced to an identity */
{ o[i] = regardvarasconst; ++i;
if(FUNCTOR(ARG(0,t)) == '+')
{ o[i] = transfereqn; ++i;
}
if(FUNCTOR(ARG(0,t)) == '*')
{ o[i] = diveqn; ++i;
}
}
if(currenttopic == _higher_order_diff
&& !contains(ARG(1,t),DIFF)
&& (
( FUNCTOR(ARG(0,t)) == DIFF && ARITY(ARG(0,t)) == 2)
|| (FUNCTOR(ARG(0,t)) == PR && ONE(ARG(1,ARG(0,t))))
/* so far we've taken only one derivative */
)
)
{ o[i] = difeqn; ++i;
}
if(SOLVETYPE(problemtype) || /* which includes MINMAX */
(problemtype == LINEAR_EQUATIONS && pathlength<=2 && !contains(t,MATRIX) && !contains(t,VECTOR))
)
{ if(problemtype == IMPLICIT_DIFF)
{ o[i] = transfereqn; ++i;
}
/* for other solvetypes, transfereqn is pre-associated;
it's not needed for RELATED_RATES as ssolve is used.
*/
if(problemtype != RELATED_RATES)
{ if(status(ssolveop) >= KNOWN)
{ o[i] = ssolveop; ++i;
}
else
{ solve_equation(t,i,o,nops);
if(problemtype != MINMAX && currenttopic != _related_rates)
return;
else
i = *nops;
}
}
}
if(problemtype==MINMAX)
{ 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] = tabulate; ++i; /* could need this if one endpoint
and no critical points */
*nops = i;
return;
}
if(problemtype==INDUCTION)
{ /* selectinductionvariable and useinductionhyp are in pre_ops */
o[i] = basiscase; ++i;
o[i] = inductionstep; ++i;
o[i] = thereforeasdesired; ++i;
*nops = i;
return;
}
if(currenttopic == _related_rates)
{ // o[i] = usenumbers; ++i;
// o[i] = usederivs; ++i;
/* these operators change the problemtype to IMPLICIT_DIFF,
but the topic remains _related_rates */
if(whichpass > 0)
{ o[i] = ssolveop; ++i;
}
/* solve for the derivative anyway, even if you
can't use or don't have specific information */
}
if(FUNCTOR(ARG(0,t)) == VECTOR && FUNCTOR(ARG(1,t)) == VECTOR)
{ o[i] = impossibleeqns; ++i;
o[i] = dropzerorow; ++i;
o[i] = convertmatrixeqn; ++i;
*nops = i;
return;
}
if(FUNCTOR(ARG(0,t))== '*' &&
ARITY(ARG(0,t)) == 2 &&
FUNCTOR(ARG(0,ARG(0,t))) == MATRIX
/* The right side can be a vector, a scalar, or a matrix
product. We don't check its form here. */
)
{ solve_matrix_equation(t,arg,i,o,nops);
return;
}
if(FUNCTOR(ARG(0,t)) == VECTOR &&
FUNCTOR(ARG(1,t)) == '*' &&
FUNCTOR(ARG(0,ARG(1,t))) == MATRIX &&
FUNCTOR(ARG(1,ARG(1,t))) == VECTOR
)
{ o[i] = multiplymatrices; ++i;
/* This arises in _gauss_jordan when in term selection mode
the user has multiplied out on the left to get { x,y }
and on the right we still have a matrix product. In
auto mode the two matrix multiplications are done at once.
*/
}
if(FUNCTOR(ARG(0,t))==INTEGRAL)
{ o[i] = transferintegral; ++i;
break;
}
if(contains(ARG(0,t),INTEGRAL) && FUNCTOR(ARG(0,t)) == '*')
{ o[i] = muleqn; ++i; /* needed if coef. is a fraction */
o[i] = diveqn; ++i;
break;
}
if(get_pending() != NULL)
{ o[i] = showcallingproblem; ++i;
break;
}
break;
case '>' : /* fall through */
case GE : /* fall through */
case '<' : /* fall through */
case NE :
case LE :
if(problemtype==INDUCTION)
{ /* selectinductionvariable and useinductionhyp are in pre_ops */
o[i] = basiscase; ++i;
o[i] = inductionstep; ++i;
o[i] = thereforeasdesired; ++i;
*nops = i;
}
else
solve_ineq(t,i,o,nops);
if(problemtype != TESTCONVERGENCE)
return;
else
i = *nops;
break; /* and continue to throw in finishcomparisontest1 etc. */
case MULTIPLICITY:
o[i] = collectmultiplesolns; ++i;
o[i] = rejecteqn; ++i;
break;
case PR:
if(PREDEFINED_FUNCTOR(FUNCTOR(ARG(0,t))))
{ o[i] = primerule; ++i;
}
/* but leave f'(x), f''(x) etc. alone when f is user-defined */
break;
case DIFF:
if(status(derivop) > LEARNING &&
problemtype != DIFFERENTIATE_FROM_DEFN &&
problemtype != DIFFERENTIATE
)
{ o[i] = derivop; ++i;
*nops = i;
return;
}
autodif(t,i,o,nops);
return;
case INTEGRAL:
if(CANTFACTOR(t)) /* this integral has already been tried */
{ *nops =i;
return;
}
if(pathlength == 5 && path[0] == '-' && path[2] == '-' &&
equals(t,history(0))
)
{ *nops = i;
return; // give up
}
autoint(t,arg,i,o,nops);
return;
case LIMIT:
autolimit(t,i,o,nops);
return;
} /* close big switch */
if(pathlength == 1 && /* toplevel */
contains_fractional_exponents(t) &&
(contains(t,SQRT) || contains(t,ROOT))
)
{ /* Don't leave expressions with both fractional exponents and roots. */
term fractexp[3];
int nfracts;
if(radicalflag != -1)
{ nfracts = fractexps(t,fractexp);
if(nfracts == 1 && ONEHALF(fractexp[0]))
{ o[i] = backtosqrts; ++i;
}
else if(nfracts < 3)
{ o[i] = backtoroots; ++i;
/* if only one or two distinct root indices are involved */
}
}
if(contains(t,ROOT) || !oksqrt(t))
/* if backtosqrts and backtoroots fail, then change all roots
to fractional exponents. */
{ o[i] = eliminatesqrts; ++i;
}
}
else if(pathlength == 1 &&
(contains(t,SQRT) || contains(t,ROOT)) &&
radicalflag == -1
)
{ /* t has no fractional exponents, but does have roots.
In some cases we still want to go to fractional exponents
*/
o[i] = eliminatesqrts; ++i;
}
if(currenttopic == _polar_form && pathlength == 1 &&
seminumerical(t) /* example, 1-sqrt(3) */
)
{ o[i] = minustopolar; ++i;
}
finish:
{ char buff[100];
if(problemtype == POWERSERIES && !inpowerflag && !indenomflag)
autoseries(t,o,&i);
if(problemtype == POWERSERIES && !inpowerflag && indenomflag && FUNCTOR(t)== '*'
&& !writeaspoly(t,get_eigenvariable(),&v,buff) && FUNCTOR(v) == '+' && ARITY(v) == 2
)
{ o[i] = writeaspoly; ++i;
}
}
if(problemtype == TESTCONVERGENCE && pathlength <= 1)
{ unsigned short h = FUNCTOR(t);
if(
!contains_calc(t)
&& ( !contains_unconvergent_series(t) // it is allowed to contain finite indexed sums or convergent series
|| (ARITY(t) == 2 && FUNCTOR(ARG(0,t)) == SUM && !contains_unconvergent_series(ARG(1,t)))
)
&& (
h = TRUEFUNCTOR || h == FALSEFUNCTOR ||
(
(h == '=' || h == '<' || h == LE)
&& !contains(ARG(1,t),g) // for unknown reasons, putting FUNCTOR(n) in place of g produces an incorrect
// "unreferenced local variable n" message in Microsoft's VS10 compiler
)
)
&& get_whichpass() > 0
)
{ int k;
series_toplevel(t,o+i,&k);
i+= k;
}
}
*nops = i; /* functor isn't trapped above */
return;
} /* close function */
/*__________________________________________________________________*/
static int splitseriestest(term u, term v)
/* u is a sum. If it contains two summands with different powers of v, return 1. Else return 0.
*/
{ int n = ARITY(u);
term a,c,w;
int i;
int flag = 0;
for(i=0;i<n;i++)
{ twoparts(ARG(i,u),v,&c,&w);
if(FUNCTOR(w) == '^' && equals(ARG(0,w),v))
{ if(!flag)
{ a = ARG(1,w);
flag = 1;
}
else if(!equals(ARG(0,w),a))
return 1;
}
}
return 0;
}
/*__________________________________________________________________*/
static void autoseries(term t, actualop *o, int *nops)
/* do the work of postops for expanding t in a power series */
/* Only called when t isn't in a denominator or an exponent
and problemtype is POWERSERIES.
*/
{ unsigned f = FUNCTOR(t);
term u,v,a,b;
int i = *nops; // maybe some operations have already been found
int pathlength = get_pathlength();
unsigned short *path = get_path();
int seriesflag = get_seriesflag();
int j;
term *varlist = get_varlist();
if(ATOMIC(t))
return;
u = ARG(0,t);
if(ARITY(t) >= 2)
v = ARG(1,t);
if(f==SUM)
{ /* if the summand contains (-1)^k \pm 1 then split into even and odd terms */
o[i] = telescopingseries; ++i;
if(FUNCTOR(u) == '+' && splitseriestest(u,v))
{ // o[i] = splitseries; ++i;
}
*nops = i;
return;
}
if(ARITY(t) > 2 && FUNCTOR(t) != '*' && FUNCTOR(t) != '+')
return; // but we need to use writeaspoly on products of arity 3 sometimes, and addseries on sums of any arity
if(seriesflag)
{ if(pathlength >= 3 && path[pathlength-3] == SUM)
{ o[i] = writeaspoly; ++i;
}
}
if(f == '/')
{ if(
ONE(u) && FUNCTOR(v) == '^' && FUNCTOR(ARG(0,v)) == '+' && ARITY(ARG(0,v)) == 2 &&
ONE(ARG(0,ARG(0,v))) && numerical(ARG(1,v))
)
{ o[i] = binomialseries; ++i;
}
if(ONE(u) && FUNCTOR(v) == '+' && ARITY(v) == 2)
{ a = ARG(0,v);
b = ARG(1,v);
if(ONE(b))
{ term temp = a;
a = b;
b = temp;
}
if(ONE(a) && NEGATIVE(b))
{ if(FUNCTOR(ARG(0,b)) == '^' && INTEGERP(ARG(1,ARG(0,b))) && status(oneoveroneminusxkseries) > LEARNING)
{ o[i] = oneoveroneminusxkseries; ++i;}
else
{ o[i] = oneminusxseries; ++i;}
}
else if(ONE(a))
{ if(FUNCTOR(b) == '^' && INTEGERP(ARG(1,b)) && status(oneoveroneplusxkseries) > LEARNING)
{ o[i] = oneoveroneplusxkseries; ++i;}
else
{ o[i] = oneplusxseries; ++i;}
}
}
if(FUNCTOR(v) == '+' && ARITY(v) == 2 && equals(u,ARG(0,v)) && status(xoveroneplusxseries) > LEARNING)
{ o[i] = xoveroneplusxseries; ++i; }
else if(FUNCTOR(v) == '+' && ARITY(v) == 2)
{ a = ARG(0,v);
b = ARG(1,v);
if(ONE(b))
{ term temp = a;
a = b;
b = temp;
}
if( status(xoveroneplusxseries) > LEARNING)
{ if(NEGATIVE(b))
{ o[i] = xoveroneminusxseries; ++i;
o[i] = xmoveroneminusxkseries;++i;
o[i] = geometricseriesfromk; ++i;
}
else
{ o[i] = xoveroneplusxseries;++i;
o[i] = xmoveroneplusxkseries; ++i;
o[i] = geometricseriesminusfromk; ++i;
}
}
if(FUNCTOR(a) == '^' && equals(ARG(0,a),eulere) && equals(b,minusone))
{ o[i] = bernoulliseries; ++i; // must try this before minusoutfromdenom screws up the chance
}
if(equals(b,minusone))
{ o[i] = minusoutfromdenom3; ++i; /* convert 1/(2x-1) to -(1/(1-2x)) for example */
}
if(!seriesflag && !contains(a,FUNCTOR(get_eigenvariable())))
{ if(mvpoly(a))
{ o[i] = divnumdenom2; ++i;
}
else
{ o[i] = multnumanddenom; ++i;
}
}
if(ARITY(v) == 2 && FUNCTOR(ARG(0,v)) == '^' && equals(ARG(0,ARG(0,v)),eulere) && equals(ARG(1,v),minusone))
{ o[i] = bernoulliseries; ++i;
}
}
}
if(f == '+' && contains(t,CONSTANTOFINTEGRATION))
{ o[i] = eliminatecofi; ++i;
}
if(f == '+' && seriesflag &&
get_nvariables() > 0 &&
!contains(t,FUNCTOR(varlist[0]))
)
// make ((-1)^k - (-1)^(k+1)) get factored inside a series
{ o[i] = contentfactor; ++i;
}
if(seriesflag)
{ // we're inside a series already. Everything below here is for creating series, and automode never creates nested series
// Example: ln(cos x). We don't want to expand cos x in a power series
*nops = i;
return;
}
if(f == '/' && pathlength <= 5)
{ o[i] = partialfractionsop; ++i;
o[i] = pulloutrational; ++i; // example, 1/(3(1-x)) = (1/3) (1/(1-x))
o[i] = multnumdenom; ++i; // example, x/(x^2+x+1), multiply num and denom by 1-x
}
for(j=0;j<pathlength-1;j+=2) // Don't expand inside any function, nor even in a fraction or power. But inside an integral is OK!
{ unsigned short g = path[j];
if(g != '-' && g != '*' && g != '+' && g != INTEGRAL)
{ *nops = i;
return;
}
}
switch(f)
{ case '*':
if(contains(t,SUM) && // when almost finished
( pathlength <= 1 || (pathlength == 3 && path[0] == '-'))
)
{ o[i] = constantintosigma; ++i;
}
if(contains(t,CONSTANTOFINTEGRATION))
{ o[i] = eliminatecofi; ++i;
}
break;
case SEC:
o[i] = secseries; ++i;
break;
case LN:
if(FUNCTOR(u) == '+')
{ if(status(lnseries) > LEARNING)
{ o[i] = lnseries; ++i;
o[i] = lnseriesminus; ++i;
}
else
{ o[i] = seriesintdifdef; ++i; // example, ln(1+x) = integral(diff(ln(1+x),x),x)
}
}
if(FUNCTOR(u) == SIN || FUNCTOR(u) == COS)
{ o[i] = seriesintdifdef; ++i;
}
if(FUNCTOR(u) == '/')
{ term a = ARG(0,u);
term b = ARG(1,u);
if(FUNCTOR(a) == SIN && equals(b,ARG(0,a)) && equals(b,get_eigenvariable()))
{ o[i] = seriesintdifdef; ++i;
}
o[i] = lnofquotient; ++i;
}
break;
case '^':
if(equals(u,eulere))
{ o[i] = negexpseries; ++i;
o[i] = expseries; ++i;
}
if(FUNCTOR(u) == '+' && ARITY(u) == 2 && (ONE(ARG(0,u)) || ONE(ARG(1,u))))
{ o[i] = binomialseries; ++i;
}
if(FUNCTOR(u) == SIN && equals(v,two))
{ o[i] = sinsqhalf2; ++i;
}
if(FUNCTOR(u) == COS && equals(v,two))
{ o[i] = cossqhalf2; ++i; // cos^2 u = (1+cos 2u)/2
}
break;
case SIN:
o[i] = sinseries; ++i;
break;
case COS:
o[i] = cosseries;++i;
break;
case TAN:
if(status(tanseries) > LEARNING)
{ o[i] = tanseries; ++i;
}
case COT:
if(status(tanseries) > LEARNING)
{ o[i] = cotseries; ++i;
}
break;
case ACOS: // fall through
case ASIN: // fall through
case ATAN:
if(status(atanseries) > LEARNING)
{ o[i] = atanseries;++i;
}
else
{ o[i] = seriesintdifdef; ++i;
}
break;
case '+':
if(powers_match(t))
{ o[i] = subseries; ++i;
o[i] = addseries; ++i;
}
o[i] = seriessubindex; ++i;
o[i] = seriesaddindex; ++i;
o[i] = seriesmoreterms; ++i;
break;
}
*nops = i;
return;
}
/*__________________________________________________________________*/
static void autoexp(term t,actualop *o, int *nops)
/* do the work of post_ops when t is a power */
{ unsigned short g;
int i=0;
const int intflag = get_intflag();
const int indexflag = get_indexsumflag();
const int seriesflag = get_seriesflag();
const int limitflag = get_limitflag();
const int difflag = get_difflag();
const int indenomflag = get_indenomflag();
const int innumflag = get_innumflag();
const int limfractflag = get_limfractflag();
const int inpowerflag = get_inpowerflag();
// const int inlogflag = get_inlogflag(); // unused
int expandflag = get_expandflag();
int radicalflag = get_radicalflag();
int trigexpandflag = get_trigexpandflag();
int pathlength = get_pathlength();
int trigpolyflag = get_trigpolyflag();
unsigned short *path = get_path();
term u = ARG(0,t);
term v = ARG(1,t);
term cancelled, trash;
int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
int whichpass = get_whichpass();
term xx = get_eigenvariable();
if(equals(u,complexi) && equals(v,minusone))
{ o[i] = recipofi; ++i;
}
if(ZERO(v))
{ o[i] = zeroexponent; ++i;
}
if(INTEGERP(u) && RATIONALP(v) &&
!nsquarefree(u) &&
!(FRACTION(v) && ONE(ARG(0,v)) && equals(ARG(1,v),two))
/* 16^(1/2) = 2^2 looks too mysterious */
)
/* examples: 36^(1/5) becomes 6^2/5; 4^(1/4) becomes 2^(1/2) */
{ o[i] = powertopower; ++i;
}
if(
(problemtype == TESTCONVERGENCE || problemtype == ADDSERIES)
&& seriesflag
&& FRACTION(v)
&& !contains(u,FUNCTOR(xx))
&& contains(ARG(0,v),FUNCTOR(xx))
)
{ if(equals(ARG(1,v),two))
{ o[i] = exponenttosqrtpower; ++i;
}
else
{ o[i] = exponenttorootpower; ++i;
}
}
if(ATOMIC(u))
{ if(equals(u,infinity))
{ o[i] = powerofinfinity; ++i;
*nops = i;
return;
}
if(INTEGERP(u) && NEGATIVE(v) && INTEGERP(ARG(0,v)))
{ if(ONE(ARG(0,v)))
{ o[i] = eliminatenegexp1; ++i;
}
else
{ o[i] = eliminatenegexp; ++i;
}
*nops = i;
return;
}
if(problemtype == POWERSERIES && intflag && !seriesflag && equals(u, get_eigenvariable()))
{ o[i] = eliminatenegexp; ++i; // needed to expand ln(sin x) in a power series
}
if(get_complex() && equals(u,complexi))
/* without 'complex' here this will apply to an index
variable in a sum. The variable i really means complexi
only when the 'complex' variable is nonzero */
{ /* defnofi, powersofi0 etc are in preops */
if(iscomplex(v))
{ o[i] = introducelninexponent; ++i;
/* We need this to do i^i, but the clause iscomplex(v)
prevents it being used on i^(3/2), which should go
to (e^(pi/2))^(3/2) and hence to e^(3pi i/4)
*/
}
else
{ o[i] = rectangulartopolar; ++i;
}
*nops = i;
return;
}
else if(equals(u,eulere))
{ if(FUNCTOR(v) == LN)
{ o[i] = lninexponent; ++i;
}
if(FUNCTOR(v) == '*')
{ term c,s;
ratpart2(v,&c,&s);
if(FUNCTOR(s) == LN || /* example, e^(2 ln x) */
(FUNCTOR(c) == LN && !contains(s,LN)) /* example, e^(1/x) ln 2 */
)
{ o[i] = lninexponent; ++i;
}
}
if(NEGATIVE(v) &&
!difflag &&
!intflag &&
!limitflag &&
!indexflag &&
!seriesflag &&
(FUNCTOR(ARG(0,v)) == LN ||
(FUNCTOR(ARG(0,v)) == '*' && contains_at_toplevel(ARG(0,v),LN))
)
)
{ o[i] = eliminatenegexp; ++i;
}
if(
iscomplex(v) &&
(FUNCTOR(v) == '+' ||
complex_number(v) ||
(FUNCTOR(v) == '*' && ARITY(v) == 2 && equals(ARG(1,v),complexi) && FUNCTOR(ARG(0,v)) == '+')
) &&
/* must work on e^(x+iy) but NOT on e^iz where z is a complex variable */
!locate(path,path+pathlength-1,'^') && /* not inside another power */
!locate(path,path+pathlength-1, LN) && /* not inside a logarithm,
cf ln(xi) = ln(xe^(i pi/2)) which should NOT
go to ln(x(cos(pi/2) + i sin(pi/2))) */
!locate(path,path+pathlength-1,SQRT) &&
!locate(path,path+pathlength-1,ROOT) &&
get_currenttopic() != _polar_form &&
!(indenomflag && NEGATIVE(v))&& /* wait for introducenegexp */
(pathlength < 3 || path[pathlength-3] != '*' || ok_complexexp(path,pathlength))
)
{ o[i] = complexexponential; ++i;
o[i] = complexexponential2; ++i;
}
if(FUNCTOR(v) == '+' && contains(v,LN))
/* e^(a + ln b) => e^a e^ln b */
/* reversecollectpowers will inhibit(collectpowers) if
one of the powers created contains_monomially a LN term;
it will be released by lninexponent */
{ o[i] = reversecollectpowers2; ++i;
o[i] = reversecollectpowers; ++i;
}
}
else if(equals(u,ten) && FUNCTOR(v) == LOG)
{ o[i] = loginexponent; ++i;
}
else if(equals(u,ten) && FUNCTOR(v) == '+' && contains(v,LOG))
/* 10^(a + log b) => 10^a 10^log b */
{ o[i] = reversecollectpowers; ++i;
}
else if(equals(u,eulere) && FUNCTOR(v) == '+' && contains(v,LN))
{ o[i] = reversecollectpowers; ++i;
}
else if(FUNCTOR(v) == '+' && contains(v,LOGB) && ATOMIC(u))
{ o[i] = reversecollectpowers; ++i;
}
else if(FUNCTOR(v) == LOGB && equals(ARG(0,v),u))
{ o[i] = logbinexponent; ++i;
}
else if(FUNCTOR(v) == '*' && contains(v,LOGB))
{ o[i] = logbinexponent2; ++i;
}
else if(NEGATIVE(v) && FUNCTOR(ARG(0,v)) == LOGB &&
( equals(ARG(0,ARG(0,v)),u) || !cancel(u,ARG(0,ARG(0,v)),&cancelled,&trash))
/* 9^(-log(3,x)) for example of the need to cancel */
)
{ o[i] = eliminatenegexp; ++i;
}
else if(NEGATIVE(v) &&
(intflag || seriesflag || indexflag) && (innumflag || indenomflag) &&
!get_indexsumflag()
)
{ if(ONE(ARG(0,v)))
{ o[i] = eliminatenegexp1; ++i;
}
else
{ o[i] = eliminatenegexp; ++i;
}
/* example: integral(1/(u(1+2u -u^-1)),u);
we don't use the distributive law in the denom, but
if we eliminate the negative exponent, common denom
and cancellation will follow. */
}
else if(INTEGERP(u) && !limitflag && !difflag
&& (problemtype < LIMITS || NUMBER(v)) &&
FUNCTOR(v) != '+' /* Don't use it on 10^(log a - log b) */
)
/* don't factor 10 when differentiating 10^x */
{ /* on known roots like 8^(1/3), don't use factorbase */
if(FRACTION(v) && ONE(ARG(0,v)) &&
ISINTEGER(ARG(1,v)) && INTDATA(ARG(1,v)) < 4 &&
ISINTEGER(u)
)
{ if(INTDATA(ARG(1,v)) == 2)
{ long k = INTDATA(u);
if(k==4 || k == 9 || k ==16 || k == 25 || k == 36
|| k ==49 || k==64 || k==81 || k == 100 || k == 121
|| k == 144 || k == 169
)
{ o[i] = exponenttosqrt; ++i;
*nops = i;
return;
}
}
else if(INTDATA(ARG(1,v)) == 3)
{ long k = INTDATA(u);
if(k==8 || k == 27 || k == 64 || k == 125)
{ o[i] = exponenttoroot; ++i;
*nops = i;
return;
}
}
}
o[i] = factorbase; ++i;
}
}
if(pathlength >= 3 && path[pathlength-3] == DIFF &&
currenttopic == _diff_polynomial && ISINTEGER(v) &&
FUNCTOR(u) == '+' && ARITY(u) == 2
)
{ if(equals(v,two))
{ o[i] = NEGATIVE(ARG(1,u)) ? squareofdif : squareofsum; ++i;
}
else if(equals(v,three))
{ o[i] = NEGATIVE(ARG(1,u)) ? cubeofdif : cubeofsum; ++i;
}
}
if(NOTDEFINED(v))
{ o[i] = toinfinity1; ++i;
o[i] = toinfinity0; ++i;
o[i] = tominusinfinity1; ++i;
o[i] = tominusinfinity0; ++i;
*nops = i;
return;
}
if(!ATOMIC(u))
{ g = FUNCTOR(u);
switch(g)
{ case '+':
if(intflag)
{ if(FRACTION(v) && equals(ARG(1,v),two))
{ term x = get_eigenvariable();
if(ispolyin(u,x) && contains(u,'^'))
{ o[i] = completethesquare1; ++i;
}
}
break; /* don't expand the integrand;
expandintegrand will do it later. If we
do it now, we screw up some integrals that
should be done by substitution, such as
(1+ sqrt x)^9/ sqrt x for example */
}
if(pathlength >= 3 && path[pathlength-3] == SUM)
{ if(equals(v,two))
{ if(ARITY(u) == 2 &&
(
(NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
(NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
)
)
{ o[i] = squareofdif; ++i;
}
else
{ o[i] = squareofsum; ++i;
}
}
else if(equals(v,three) && ARITY(u) == 2 && NEGATIVE(ARG(1,u)))
{ o[i] = cubeofdif; ++i;
}
else if(equals(v,three))
{ o[i] = cubeofsum; ++i;
}
else if(ISINTEGER(v) && INTDATA(v) <= 4)
{ o[i] = binomialtheorem; ++i;
}
}
if( (EXPANDFLAG && !in_sum() &&
!(pathlength==1 && problemtype == SIMPLIFY && get_currentline() > 0)
) ||
currenttopic == _binomial_theorem ||
(get_complex() && iscomplex(u) && ispolyin(u,complexi))
// 1.30.25 added iscomplex, as ispolyin(u,complexi)
// succeeds when u doesn't even contain complexi
)
{ if(equals(v,two))
{ if(ARITY(u) == 2 &&
(
(NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
(NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
)
)
{ o[i] = squareofdif; ++i;
}
else
{ o[i] = squareofsum; ++i;
}
}
else if(equals(v,three) && ARITY(u) == 2 && NEGATIVE(ARG(1,u)))
{ o[i] = cubeofdif; ++i;
}
else if(equals(v,three))
{ o[i] = cubeofsum; ++i;
}
else
{ o[i] = binomialtheorem; ++i;
}
}
else if(!in_sum())
{ if(equals(v,two) && contains_sqrt(u) == SQRT)
{ if(ARITY(u) == 2 &&
(
(NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
(NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
)
)
{ o[i] = squareofdif; ++i;
}
else
{ o[i] = squareofsum; ++i;
}
}
if(equals(v,three) && contains_sqrt(u) == ROOT)
{ if(ARITY(u) == 2 && NEGATIVE(ARG(1,u)))
{ o[i] = cubeofdif; ++i;
}
else
{ o[i] = cubeofsum; ++i;
}
}
}
break;
case '-':
o[i] = minustopower; ++i;
break;
case '/':
if(!difflag && /* better to use power rule first when differentiating */
!contains(v, LIMIT) &&
!contains(v, SUM) &&
!contains(v, DIFF) &&
!contains(v, INTEGRAL) &&
(!get_polyvaldomainflag() ||
(!contains_undefined(v) && !check1(defined(v)))
/* don't do this: (a/b)^infinity = a^infinity/b^infinity.
You don't have to check this if polyvaldomainflag is 0,
and even if it is, first make a fast check for
a NOTDEFINED atom in the exponent, before calling
on the prover to check the definedness of the exponent
*/
)
)
{ if(!limitflag || whichpass > 0 || limfractflag == -1)
/* Don't use (u/v)^n = u^n/v^n in a limitand unless
it is in a compound fraction (limfractflag == -1)
or you failed to solve it completely without
this law (whichpass > 0). For example,
to simplify a difference of two powers of
fractions, where each term individually goes
to infinity, such as
lim(x->0, ((x+1)/x^2)^2 - ((x-1)/x^2)^2)) =
lim(x->0, ((x+1)^2-(x-1)^2)/x^4) =
lim(x->0, 4x/x^4) = infinity
*/
{ o[i] = quotienttopower; ++i;
}
}
break;
case '*':
if(radicalflag == 1 && FRACTION(v))
break; /* (5 \cdot 7)^(1/3), don't make it 5^(1/3)7^(1/3)
if you're going to convert to radicals anyway */
/* don't convert (ab)^-n to a^-n b^-n unless the directions are
to express in negative exponents, or it's in a sum in a
fraction. */
if(!NEGATIVE(v) ||
get_polyvalnegexpflag() == 1 ||
!get_infractionflag() ||
insumfract()
)
{ o[i] = producttopower; ++i;
}
break;
case SQRT:
if(problemtype < LIMITS)
/* In calculus, use fractional exponents instead */
{ o[i] = powerofsqrt2; ++i; /* (sqrt(x))^(2n+1) = x^n sqrt x */
/* powerofsqrt is in preops */
}
break;
case ROOT:
if((radicalflag < 0 && !algebraic_number(ARG(1,u))) ||
iscomplex(ARG(1,u)) ||
( get_complex() &&
NEGATIVE(ARG(1,u)) && obviously_nonnegative(ARG(0,ARG(1,u)))
/* whether odd or even, when computing complex roots of
negative numbers use fractional exponents. */
)
)
{ o[i] = rootpowerexp; ++i;
}
if(!SOLVETYPE(problemtype))
{ o[i] = powerofroot2; ++i; /* (root(n,a))^m = ^na^m)) */
o[i] = powerofroot3; ++i; /* (root(n,a))^(qn+r) = a^q root(n,a^r) */
}
break;
case SG:
o[i] = sgoddpower; ++i;
o[i] = sgevenpower; ++i;
break;
case ABSFUNCTOR:
if(get_complex() && contains(u,'i'))
{ o[i] = squareofabs; ++i;
}
o[i] = absevenpower; ++i;
break;
case SIN:
if((trigexpandflag & 1) || contains_arctrigs(u))
{ o[i] = sinsqhalf; ++i;
}
if(!ATOMIC(ARG(0,u)))
{ o[i] = sinsqperiodic; ++i;
}
if(trigpolyflag == SIN)
{ if(iseven(v))
{ o[i] = sinsqtocossq; ++i;
}
else
{ o[i] = sinoddpower; ++i;
}
}
break;
case COS:
if((trigexpandflag & 1) || contains_arctrigs(u))
{ o[i] = cossqhalf; ++i;
}
if(!ATOMIC(ARG(0,u)))
{ o[i] = cossqperiodic; ++i;
}
if(trigpolyflag == COS)
{ if(iseven(v))
{ o[i] = cossqtosinsq; ++i;
}
else
{ o[i] = cosoddpower; ++i;
}
}
break;
case COT:
if((trigexpandflag & 0x0004) && /* only cot and tan */
!difflag && !indenomflag
)
{ o[i] = cottotan; ++i;
break;
}
if(!(trigexpandflag & 0x100) &&
limfractflag != -1 &&
!difflag &&
!(trigexpandflag & (1 << 4))
)
{ o[i] = cottosincos; ++i;
break;
}
if(trigpolyflag == COT)
{ if(iseven(v))
{ o[i] = cotsqtocscsq; ++i;
}
else
{ o[i] = cotoddpower; ++i;
}
}
case SEC:
if(!(trigexpandflag & 0x10) && limfractflag != -1 &&
(pathlength < 3 || !TRIGFUNCTOR(path[pathlength-3]))
)
{ o[i] = secrule; ++i; /* may be inhibited if inside a sum
containing only sec and tan */
}
/* see comments below near cscrule */
if(!ATOMIC(ARG(0,u)))
{ o[i] = secsqperiodic; ++i;
}
if(trigpolyflag == SEC)
{ if(iseven(v))
{ o[i] = secsqtotansq; ++i;
}
else
{ o[i] = secoddpower; ++i;
}
}
break;
case TAN:
if(trigpolyflag == TAN)
{ if(iseven(v))
{ o[i] = tansqtosecsq; ++i;
}
else
{ o[i] = tanoddpower; ++i;
}
}
break;
case CSC:
if(!(trigexpandflag & 0x100) &&
!(trigexpandflag & (1 << 4)) &&
limfractflag != -1 &&
(pathlength < 3 || !TRIGFUNCTOR(path[pathlength-3]))
)
{ o[i] = cscrule; ++i;
/* this is here instead of in autotrig so that we will
get csc^2 => 1/sin^2 instead of (1/sin)^2.
'path' is used to suppress it in autotrig. */
}
if(!ATOMIC(ARG(0,u)))
{ o[i] = cscsqperiodic; ++i;
}
if(trigpolyflag == CSC)
{ if(iseven(v))
{ o[i] = cscsqtocotsq; ++i;
}
else
{ o[i] = cscoddpower; ++i;
}
}
break;
}
}
/* Don't leave a negative exponent under a root or sqrt,
as in root(2,y^-2) or sqrt(xy^-2). The following
code takes care of this. But do leave it inside an integral.
*/
if(NEGATIVE(v) && !is_complex(v) && !intflag && // added !intflag 1.30.25
(
(pathlength >= 3 &&
(path[pathlength-3] == ROOT || path[pathlength-3] == SQRT)
) ||
(pathlength >= 5 &&
path[pathlength-3] == '*' &&
(path[pathlength-5] == ROOT || path[pathlength-5] == SQRT)
)
)
)
{ if(ONE(ARG(0,v)))
{ o[i] = eliminatenegexp1; ++i;
}
else
{ o[i] = eliminatenegexp; ++i;
}
}
if(radicalflag == 1 && FRACTION(v) && elimfractexp_conditions(pathlength,path))
{ if(FUNCTOR(ARG(0,v)) == '*')
{ /* (ab^n)^(1/2) = a^(1/2)b^(n/2) before introducing sqrt */
unsigned short m = ARITY(ARG(0,v));
int j;
for(j=0;j<m;j++)
{ if(FUNCTOR(ARG(j,ARG(0,v)))== '^')
break;
}
if(j<m)
{ o[i] = producttopower; ++i;
}
}
if(equals(ARG(1,v),two))
{ o[i] = exponenttosqrt; ++i;
}
else
{ o[i] = exponenttoroot; ++i;
}
}
if( SOLVETYPE(problemtype) && problemtype != TESTCONVERGENCE)
{ if( !inpowerflag &&
contains(v,FUNCTOR(get_eigenvariable())) &&
pathlength >=3 &&
path[pathlength-3] != LN &&
path[pathlength-3] != LOG &&
path[pathlength-3] != LOGB &&
path[pathlength-3] != '+' &&
!equals(u,ten) &&
!equals(u,eulere)
)
/* don't rewrite ln(a^b) => ln(e^b ln a) as
this leads to an infinite regress
=> ln(e^ln(a^b))
=> ln(e^ln(e^(b ln a))) etc.
And don't use this in a power to stop the
second step in this and similar regresses.
And don't use it inside a sum, as it won't do any good there
and can block further progress.
*/
{ o[i] = introduceloginexponent; ++i; /* x^n = 10^(n log x) */
/* but in automode it requires log to appear in n */
o[i] = introducelninexponent; ++i; /* x^n = e^(n ln x) */
/* so if log doesn't already appear in n, ln will be used */
}
}
if(problemtype == DIFFERENTIATE_FROM_DEFN &&
limitflag && !difflag && /* definition of derivative has been used already */
( ISATOM(v) || FUNCTOR(v) == '+') /* whether x or x+h in 2^(x+h)-2^x */
)
{ /* you need introducelninexponent to handle f(x) = a^x */
o[i] = introducelninexponent; ++i;
}
if(
(intflag || limitflag) &&
ATOMIC(u) && !equals(u,eulere) &&
!NOTDEFINED(u) && !constant(v) && constant(u) &&
!ATOMIC(v)
)
/* convert 2^f(x) to e^((ln 2) f(x)) in an integral or limit */
/* also pi^f(x) or c^f(x) but not x^2 and not 2^x, since we
have integration and differentiation formulas for 2^x directly.
*/
{ o[i] = introducelninexponent; ++i;
}
/* and don't use introducelninexponent at all unless
we're solving equations or inequalities, or differentiating
from the definition of derivative, or in the case of atom^v in an
integral or limit.
Just leave a^c alone, e.g. in a^c/b^c.
*/
if(whichpass == 5 && /* don't do this till the very end */
(
(OBJECT(u) && TYPE(u) == DOUBLE && NUMBER(v)) ||
(NUMBER(u) && OBJECT(v) && TYPE(v) == DOUBLE) ||
(NUMBER(u) && NEGATIVE(v) && OBJECT(ARG(0,v)) && TYPE(ARG(0,v))==DOUBLE)
)
)
{ o[i] = computepower; ++i;
}
*nops = i;
}
/*__________________________________________________________________*/
static void autoquo(term t,actualop *o, int *nops)
/* do the work of post_ops when t is an quotient */
{ term num = ARG(0,t);
term denom = ARG(1,t);
term x;
const int intflag = get_intflag();
const int limitflag = get_limitflag();
const int difflag = get_difflag();
const int indexflag = get_indexsumflag();
const int seriesflag = get_seriesflag();
const int inpowerflag = get_inpowerflag();
const int inlogflag = get_inlogflag();
const int insqrtflag = get_insqrtflag();
const int inrootflag = get_inrootflag();
const int infractexpflag = get_infractexpflag();
unsigned short g = FUNCTOR(num);
unsigned short h = FUNCTOR(denom);
unsigned short f;
int apartandcancelflag = 0;
int problemtype = get_problemtype();
int topic = get_currenttopic();
int comdenomflag = get_comdenomflag();
int whichpass = get_whichpass();
int pathlength = get_pathlength();
unsigned short *path = get_path();
int i=0;
if(whichpass == 5 && seminumerical(t) && contains_double(t))
{ o[i] = devalop; ++i;
*nops = i;
return;
}
if(
(contains_at_toplevel(num,FACTORIAL) || g == FACTORIAL) &&
(contains_at_toplevel(denom,FACTORIAL) || h == FACTORIAL)
)
{ o[i] = factorialrecursion; ++i;
}
if(g=='/' && h != '/')
{ o[i] = compoundfractions2; ++i; /* (a/b)/c = a/(bc) */
}
if(h == '/')
{ if(ONE(num))
{ o[i] = invertandmultiply2; ++i;
}
else
{ o[i] = invertandmultiply; ++i;
}
}
if( (get_ringflag() & RATRING) && pulloutrational_aux(num,denom))
{ o[i] = pulloutrational; ++i;
/* careful that this doesn't loop with multiplyfractions */
/* Therefore we don't do it just for an integer denom;
in automode pulloutrational is associated mainly to sums,
but is called here for special situations involving roots */
*nops = i;
return;
}
if(equals(denom,two) && FUNCTOR(num) == '+' && ARITY(num) == 2 &&
FUNCTOR(ARG(0,num)) == '^' && equals(ARG(0,ARG(0,num)),eulere) &&
get_currenttopic() != _hyperfunctions &&
/* if it's _hyperfunctions we're supposed to express things in
terms of exponentials. */
problemtype != TRIG_IDENTITY && // 1.27.25
!iscomplex(num) /* don't use this on complex arguments */
)
{ o[i] = coshdefrev; ++i;
o[i] = sinhdefrev; ++i;
}
if(topic == _polar_form && iscomplex(num) && !iscomplex(denom) && !inpowerflag)
/* example, e^(it)/sqrt 2 = (1/sqrt 2) e^it; but don't do it in the
exponent, or you get e^((1/2) pi i) instead of e^(pi i/2) */
{ o[i] = pulloutreal; ++i;
}
if(NOTDEFINED(denom))
{ o[i] = nonzerooverinfinity; ++i;
*nops = i;
return;
}
if(NOTDEFINED(num))
{ o[i] = infinityovernonzero; ++i;
*nops = i;
return;
}
if(ONE(num) && equals(denom,complexi))
{ o[i] = recipofi; ++i;
}
if(iscomplex(denom) &&
h != '^' /* don't use it if denom is e^ix */
)
{ if(FUNCTOR(denom) == '*')
{ term c,s;
o[i] = recipofi3; ++i; /* a/bi = -ai/b */
ratpart2(denom,&c,&s);
/* don't use it if denom is c e^ix either */
if(FUNCTOR(s) != '^')
{ o[i] = cleardenomofi; ++i;
}
}
else
{ o[i] = cleardenomofi; ++i;
}
}
if(intflag && FUNCTOR(ARG(1,t)) == '^' && constant(ARG(0,ARG(1,t)))
&& ! trigrat2(t,get_eigenvariable())
)
/* examples: integral(sin(x)/e^x,x) should become
integral(e^-x sin x,x),
but integrand sin^4 x / cos^2 should not become sin^4 x cos^(-2) x
*/
{ o[i] = introducenegexp; ++i;
}
/* The following code was added for version 5 */
if(problemtype == POWERSERIES &&
!seriesflag && FUNCTOR(ARG(1,t))=='^' && FRACTION(ARG(1,ARG(1,t)))
) // prepare for binomial series
{ o[i] = introducenegexp; ++i;
}
/* eliminatenegexpdenom is in pre_ops */
if(get_polyvalnegexpflag() == 1 &&
!INTEGERP(ARG(1,t)) && /* don't make 1/2 into 2^(-1) */
(pathlength < 3 || path[pathlength-3] != '/') && /* simplify compound fractions first */
(pathlength < 3 || path[pathlength-3] != '^') && /* in (a/b)^k, wait for a^k/b^k to happen
rather than creating (ab^-1)^k */
(pathlength < 5 || path[pathlength-3] != '-' || path[pathlength-5] != '^')
/* similarly with (-a/b)^k */
) /* else eliminateconstnegexpnum is called in preops */
{ o[i] = introducenegexp; ++i;
o[i] = introducenegexp1; ++i;
}
else if(get_polyvalnegexpflag() == 2 &&
iscomplex(denom)
)
{ o[i] = introducenegexp; ++i;
o[i] = introducenegexp1; ++i;
}
else if(intflag /* handle integral(1/x^(1/2),x) => integral(x^(-1/2),x) */
&& !get_infractionflag() /* don't do this in compound fractions */
&& FUNCTOR(denom) == '^'
&& ( FRACTION(ARG(1,denom)) ||
(ISATOM(ARG(1,denom))) // example: integral(1/e^v, v) 1.30.25
)
&& ISATOM(ARG(0,denom))
&& constant(num)
&& !inlogflag
)
{ o[i] = introducenegexp; ++i;
}
else if(difflag && /* d/dx( u/(x-1)^2 = d/dx u (x-1)^(-2) */
!inlogflag && /* don't introduce neg exps inside a log in a deriv */
! (constant(num) && FUNCTOR(denom) == '^' &&
equals(ARG(0,denom),get_eigenvariable())
) && /* e.g. on d/dx ( c/x^2); inversepower will be used
instead of introducenegexp */
!use_logdif(t,get_eigenvariable())
/* don't screw up logarithmic differentiation problems */
)
{ if(FUNCTOR(num) == '+')
{ o[i] = apartandcancel; ++i;
apartandcancelflag = 1; /* don't try it again below */
}
o[i] = introducenegexp; ++i;
o[i] = sqrtexpdenom; ++i;
o[i] = rootexpdenom; ++i;
}
/* next line deals with e.g. (a/b + c)/d => (a/b)/d + c/d; */
if(g=='+' &&
(pathlength < 3 || path[pathlength-3] != '^') &&
/* don't use apart just inside a power, wait for the power to be
pushed into the fraction. In particular when there are fractional
exponents in a fraction inside a power, comdenom is used, and we
don't want to loop e.g. on (x^1/2 + 1/x^1/2)^2. */
(pathlength < 3 || (((f = path[pathlength-3]) != 0) && !TRIGFUNCTOR(f))) &&
/* don't use apart just inside a trig or arctrig functor, to avoid changing
sin((u+v)/2) to sin(u/2 + v/2) which will be attacked by sinsum instead
of half-angle formulas. */
!contains_sqrt(ARG(1,t)) && /* rationalizedenom should be used first */
!(numerical(t) && contains(ARG(1,t),SQRT)) /* don't thrash on algebraic expressions */
)
{ int k;
term sum,arg;
unsigned short mm;
sum = ARG(0,t);
mm = ARITY(sum);
for(k=0; k<mm; k++)
{ arg = ARG(k,sum);
if(FUNCTOR(arg)=='-')
arg = ARG(0,arg);
if(fractional_factor(arg))
break;
}
if(k < mm)
{ o[i] = apart; ++i;
}
}
if(
(intflag || indexflag || seriesflag) &&
!get_infractionflag() && !comdenomflag &&
!insqrtflag && !inrootflag && !infractexpflag &&
/* don't attack integral(sqrt((1+x)/(1-x)),x) by polynomial division */
!stop_apart(t)
)
{ x = get_eigenvariable();
if(rational_function(t,x))
{ if(FRACTION(t) && FUNCTOR(ARG(0,t)) == '+' &&
ispolyin(ARG(0,t),x) && /* num is a polynomial in x */
(
equals(ARG(1,t),x) || /* demon is x or */
(FUNCTOR(ARG(1,t)) == '^' && /* a power of x */
equals(ARG(0,ARG(1,t)),x) &&
INTEGERP(ARG(1,ARG(1,t)))
)
)
)
{ o[i] = apart; ++i;
}
else
{ o[i] = polydivop; ++i;
o[i] = factordenominator; ++i;
o[i] = partialfractionsop; ++i;
o[i] = completethesquare1; ++i;
o[i] = apart; ++i;
}
}
if(pathlength >= 3 && (path[pathlength-3]==INTEGRAL || path[pathlength-3] == SUM))
{ o[i] = apartandcancel; ++i;
o[i] = apart; ++i;
}
}
/* The following code gets sin((arctan(8)-pi_term)/2) simplified
by applying apart to the argument of sin
*/
if(g == '+' && pathlength >= 3 &&
TRIGFUNCTOR(path[pathlength-3]) && ARITY(ARG(0,t)) == 2 &&
equals(ARG(1,t),two) &&
( ARCTRIGFUNCTOR(FUNCTOR(ARG(0,ARG(0,t)))) ||
(FUNCTOR(ARG(0,ARG(0,t))) == '*' && ARCTRIGFUNCTOR(FUNCTOR(ARG(1,ARG(0,ARG(0,t))))) && ISINTEGER(ARG(0,ARG(0,ARG(0,t)))) && ARITY(ARG(0,ARG(0,t))) == 2)
) &&
( equals(ARG(1,ARG(0,t)),pi_term) ||
(NEGATIVE(ARG(1,ARG(0,t))) && equals(ARG(0,ARG(1,ARG(0,t))),pi_term))
)
)
{ o[i] = apart; ++i;
}
if(
(problemtype == SIMPLIFY || problemtype == ROOTS) &&
(pathlength < 3 || path[pathlength-3] != '*') &&
/* if the fraction is in a product, multiply it out first */
(
(h == '+' && ARITY(denom) == 2) ||
(h == SQRT && alg_numerical(t)) ||
rationalizable_cubic(denom)
) &&
(!constant(num) || seminumerical(t)) &&
contains_sqrt(denom) &&
!(
h == SQRT && NUMBER(num) && /* leave sin(1/sqrt 2) alone */
(
(pathlength >= 3 && TRIGFUNCTOR(path[pathlength-3])) ||
(pathlength >= 5 && TRIGFUNCTOR(path[pathlength-4]) && path[pathlength-3]=='-')
)
) &&
!get_infractionflag() /* don't rationalize denom in a compound fraction */
)
{ o[i] = rationalizedenom; ++i;
}
else if(seminumerical(t) && rationalizable_cubic(denom))
/* e.g. in cubic equations involving a preliminary substitution to
get rid of the quadratic term, the problemtype doesn't revert to
SIMPLIFY until after all the defined variables are eliminated,
but we need to simplify the solutions after the cubic is solved. */
{ o[i] = rationalizedenom; ++i;
}
if(!get_infractionflag() &&
!comdenomflag && g == '+' &&
!SOLVETYPE(problemtype) &&
(problemtype != TRIG_IDENTITY ||
(
pathlength >= 3 && path[pathlength-3] != TAN && path[pathlength-3] != COT &&
(pathlength < 5 || path[pathlength-5] != '^')
)
) &&
/* apart makes trouble in trig identities; generally we want to bring
both sides to fractional form and then, if all else fails,
cross multiply. But, we DO need to simplify sin((t + pi_term)/2),
but NOT sin^2((t+pi_term)2)...*/
!(numerical(t) && contains(ARG(1,t),SQRT)) &&
(!contains_sqrt(ARG(1,t)) || iscomplex(ARG(0,t))) &&
/* rationalize the denominator instead of using apart,
but not if the numerator is complex, e.g. (a + bi)/ sqrt(2) which
must become a/sqrt(2) + (b/sqrt(2)) i
*/
!contains_defined_variables(t) &&
/* don't use apart until defined variables have been eliminated,
for example (1-u^2)/u with u = sin t; the numerator
will simplify to cos^2 t, but not if apart is used. */
(!limitflag || inlogflag)
/* inlogflag permits us to use apartandcancel
on ln((x+h)/x) inside a limit, as required in doing
diff(ln x,x) from the definition of derivative;
but we don't want it when doing limits of rational functions. */
)
{ /* use apart on (3+2i)/5 for example */
/* This is not done by 'apart' but by 'complexapart' */
if(get_complex())
{ if(FUNCTOR(ARG(0,t)) == '+' && ARITY(ARG(0,t)) == 2 &&
iscomplex(ARG(1,t)) && !iscomplex(ARG(0,t)) &&
!contains(ARG(0,t),'+') && !contains(ARG(1,t),'+')
)
{ o[i] = complexapart; ++i;
}
else if (!(pathlength >= 2 && path[pathlength-2] == '^' && isinteger(ARG(1,t)))) /* leave (k+1)pi i / 3 alone */
{ o[i] = complexform; ++i;
}
}
if(problemtype != TRIG_IDENTITY && !apartandcancelflag)
{ o[i] = apartandcancel; ++i;
}
if(problemtype == LINEAR_EQUATIONS && !constant(t))
{ o[i] = apart; ++i;
}
else if(pathlength >= 3 && TRIGFUNCTOR(path[pathlength-3]))
/* Example: sin((2t + pi_term)/2) = sin(t + pi/2) */
/* In automode, apartandcancel will not work if only a constant cancels,
as in this example. We have to use apart. But we really want
to use it ONLY if there is going to be a cancellation, because
in general we don't want sin((u+v)/2) to go to sin(u/2 + v/2).
*/
{ char buffer[DIMREASONBUFFER];
term w,q,a,b;
int k,err;
if(equals(denom,two) && ARITY(num) == 2 && equals(ARG(0,num),pi_term) && NEGATIVE(ARG(1,num)))
{ o[i] = apart; ++i;
}
else
{ err = apart(t,zero,&w,buffer);
if(!err && FUNCTOR(w) == '+')
{ for(k=0;k<ARITY(w);k++)
{ q = ARG(k,w);
if(NEGATIVE(q))
q = ARG(0,q);
if(FRACTION(q) && !cancel(q,zero,&a,&b))
break;
}
if(k < ARITY(w))
{ o[i] = apart; ++i;
}
}
}
}
}
/* apart is (a+b)/c = a/c + b/c
apart_and_cancel is apart followed by successful cancelling of at least
one term of the sum.
'Apart' loops with commondenom and with addfractions (a/c + b/c = (a+b)/c)
We use add_fractions to get rid of compound fractions and to
hope (a+b) may cancel with c. Using apartandcancel prevents that loop.
*/
if(g == '+' && h == '+' && !comdenomflag &&
!get_infractionflag() && problemtype < LIMITS &&
!insqrtflag && !inrootflag && !infractexpflag
/* don't attack integral(sqrt((1+x)/(1-x)),x) by polynomial division */
)
{ o[i] = polydivop; ++i; /* polydiv with remainder */
}
/* rationalizefraction is called from autocalc.c
as it takes functor LIMIT */
if( (intflag || seriesflag || indexflag || problemtype == SIMPLIFY) &&
h == '+' && ARITY(denom) == 2 && contains(denom,SQRT) &&
!get_infractionflag()
)
{ o[i] = rationalizedenom; ++i;
/* example, integral(1/(sqrt(x+1) + sqrt x),x) */
}
if( (intflag || seriesflag || indexflag) && contains_sqrt(num) && contains_sqrt(denom))
{ o[i] = rationalizedenom; ++i;
/* example, integral(sqrt(1-x)/sqrt(1+x),x)
This will get us to an integral with only one square root,
in the numerator.
*/
}
if(g == SQRT &&
(
(pathlength >= 3 && TRIGFUNCTOR(path[pathlength-3])) ||
(pathlength >= 5 && TRIGFUNCTOR(path[pathlength-5]) && path[pathlength-3] == '-')
)
)
{ o[i] = cancelsqrt2; ++i; /* sin(1/sqrt 2), not sin(sqrt(2)/2) */
}
if(contains_sqrt(num) || contains_sqrt(denom))
/* since num and/or denom could contain several of ROOT, SQRT, ABSFUNCTOR,
you can't rely on the return value of contains_sqrt; better
try all three. */
{ o[i] = cancelsqrt3; ++i;
o[i] = cancelabs3; ++i;
o[i] = cancelroot3; ++i;
o[i] = cancelsqrtgcd; ++i;
o[i] = cancelrootgcd; ++i;
o[i] = cancelabsgcd; ++i;
}
if(g == '*' || g == '^' || h == '^' || h == '*')
{ o[i] = powerstonum; ++i;
o[i] = powerstodenom; ++i;
}
if(g == '-')
{ o[i] = minusoutfromnum; ++i;
}
if(h == '-')
{ o[i] = minusoutfromdenom; ++i;
}
if(g == '*' && INTEGERP(denom) &&
numerical(ARG(0,num)) &&
!numerical(ARG(1,num)) && /* don't use it on (3 sqrt(13) x)/13
because polyvalop will just undo it */
(get_ringflag() & RATRING) &&
!canonical(0,t) &&
!limitflag && /* it's a waste of time in limits to write 3x/2 as (3/2) x */
(pathlength <= 3 ||
( !TRIGFUNCTOR(path[pathlength-2]) &&
!TRIGFUNCTOR(path[pathlength-3])
)
)
// don't rewrite sin(5 pi/8) as sin ((5/8) pi)
)
{ o[i] = breakfraction2; ++i;
/* convert sqrt(3) x /2 to (sqrt(3)/2) x */
}
if(contains(t,SIN) && contains(t,COS))
/* example: (1-cos x)/sin^2x => (1-cos x)/(1-cos^2 x) */
{ o[i] = sinsqtocossq; ++i;
o[i] = cossqtosinsq; ++i;
}
if(h == SG || (h == '*' && contains_at_toplevel(denom,SG)))
{ o[i] = sgrecip; ++i; /* 1/sg(x) = sg(x) */
}
if(g == '^' && h == '^' &&
FUNCTOR(ARG(0,num)) == TAN && FUNCTOR(ARG(0,denom)) == COS &&
(difflag || pathlength == 1 || (pathlength == 3 && path[0] == '=')) &&
equals(ARG(0,ARG(0,num)),ARG(0,ARG(0,denom)))
)
{ o[i] = tanrule2; ++i;
}
if(g == SIN && h == COS &&
(
difflag ||
(pathlength >= 3 && path[pathlength-3] == ATAN) ||
pathlength == 1 ||
(pathlength == 3 && path[0] == '=')
)
&&
equals(ARG(0,num),ARG(0,denom))
)
{ o[i] = tanrule2; ++i; /* arctan(sin x/cos x) => arctan(tan x) */
}
if(g == '^' && h == '^' &&
FUNCTOR(ARG(0,num)) == COS && FUNCTOR(ARG(0,denom)) == SIN &&
(difflag || pathlength == 1 || (pathlength == 3 && path[0] == '=')) &&
equals(ARG(0,ARG(0,num)),ARG(0,ARG(0,denom)))
)
{ o[i] = cotrule2; ++i;
}
if(g == COS && h == SIN &&
(
difflag ||
problemtype == POWERSERIES ||
(pathlength >= 3 && path[pathlength-3] == ACOT) ||
pathlength == 1 ||
(pathlength == 3 && path[0] == '=')
) &&
equals(ARG(0,num),ARG(0,denom))
)
{ o[i] = cotrule2; ++i; /* arccot(cos x/sin x) => arccot(cot x) */
}
if(ONE(num) && ( h == SIN || (h == '^' && FUNCTOR(ARG(0,denom)) == SIN)) &&
( pathlength == 1 ||
(pathlength >= 3 && path[pathlength-3] == ACSC)
// || (pathlength >= 3 && path[pathlength-3] == '=' && problemtype == TRIG_IDENTITY)
// 1.30.25 commented this out, as it's idiotic to introduce csc if the rest of
// the identity only has sin and cos. Is it ever needed? Why is this here?
)
)
{ o[i] = cscrule2; ++i; /* 1/sin = csc, inside arcscs or at toplevel;
but don't use it at toplevel when solving
equations, as csc x = a is solved by
writing csc x = 1/ sin x, not vice-versa */
}
if(ONE(num) && ( h == COS || (h == '^' && FUNCTOR(ARG(0,denom)) == COS)) &&
( pathlength == 1 ||
(pathlength >= 3 && path[pathlength-3] == ASEC) ||
(pathlength >= 3 && path[pathlength-3] == '=' && problemtype == TRIG_IDENTITY)
)
)
{ o[i] = secrule2; ++i; /* 1/cos = sec, inside arcsec or at toplevel */
}
if(ONE(num) && ( h == TAN || (h == '^' && FUNCTOR(ARG(0,denom)) == TAN)) &&
( difflag ||
pathlength == 1 ||
(pathlength >= 3 && path[pathlength-3] == ACOT) ||
(pathlength >= 3 && path[pathlength-3] == '=' && problemtype == TRIG_IDENTITY)
)
)
{ o[i] = tanrecip; ++i; /* 1/tan = cot, inside arccot or at toplevel when
verifying identities; but don't use it at
toplevel when solving equations, e.g. 1/tan x = 1.
*/
}
if(ONE(num) && ( h == COT || (h == '^' && FUNCTOR(ARG(0,denom)) == COT)) &&
( difflag ||
pathlength == 1 ||
(pathlength >= 3 && path[pathlength-3] == ATAN) ||
(pathlength >= 3 && path[pathlength-3] == '=')
)
)
{ o[i] = cotrecip; ++i; /* 1/cot = tan, inside arctan or at toplevel */
}
if(ONE(num) && ( h == SEC || (h == '^' && FUNCTOR(ARG(0,denom)) == SEC)) &&
( difflag ||
pathlength == 1 ||
(pathlength >= 3 && path[pathlength-3] == ACOS) ||
(pathlength >= 3 && path[pathlength-3] == '=')
)
)
{ o[i] = secrecip; ++i; /* 1/sec = cos, inside arccos or at toplevel */
}
if(ONE(num) && ( h == CSC || (h == '^' && FUNCTOR(ARG(0,denom)) == CSC)) &&
( difflag ||
pathlength == 1 ||
(pathlength >= 3 && path[pathlength-3] == ASIN) ||
(pathlength >= 3 && path[pathlength-3] == '=')
)
)
{ o[i] = cscrecip; ++i; /* 1/csc = sin, inside arcsin or at toplevel */
}
if(limitflag) /* example: lim(x->infinity, x(2x-1)/(x^2+3)) */
{ term x = get_eigenvariable();
if( (!contains(num,'^') && FUNCTOR(num) == '*' && ARITY(num) <= 3
&& contains(ARG(0,num),FUNCTOR(x))
/* don't expand a numerator with a constant first factor */
)
||
(FUNCTOR(num) == '*' && status(limrationalfunction) <= LEARNING
&& polyprod(num,x)
&& contains(ARG(0,num),FUNCTOR(x))
/* don't expand a numerator with a constant first factor */
)
)
{ o[i] = expandnumerator; ++i;}
if( (!contains(denom,'^') && FUNCTOR(denom) == '*' && ARITY(denom) <= 3
&& contains(ARG(0,denom),FUNCTOR(x))
/* don't expand a denominator with a constant first factor */
)
|| (FUNCTOR(denom) == '*' && status(limrationalfunction) <= LEARNING
&& polyprod(denom,x)
&& contains(ARG(0,denom),FUNCTOR(x))
/* don't expand a denominator with a constant first factor */
)
)
{ o[i] = expanddenominator; ++i;}
/* this means that divnumdenom will get called on more complicated
cases of quotients of products of powers of polynomials */
}
if(numerical(t) && contains(ARG(0,t),'^') && contains(ARG(1,t),'^'))
{ o[i] = factorbase; ++i;
}
if(pathlength >= 3 && path[pathlength-3] == LIMIT &&
contains(ARG(1,t),SEC)
)
{ o[i] = secrecip; ++i;
}
if(pathlength >= 3 && path[pathlength-3] == LIMIT &&
contains(ARG(1,t),CSC)
)
{ o[i] = cscrecip; ++i;
}
*nops = i;
}
/*________________________________________________________________________*/
int attractible(term t, term x)
/* x is a variable, t is a compound term (if not return 0);
return 1 if more than one arg of t contains x, else return 0. */
{ int i,k;
unsigned short n,c;
if(!ATOMIC(x) || ATOMIC(t))
return 0;
n = ARITY(t);
c = FUNCTOR(x);
k=0;
for(i=0;i<n;++i)
{ if(contains(ARG(i,t),c))
++k;
if(k==2) return 1;
}
return 0;
}
/*___________________________________________________________________*/
static int fractional_factor(term t)
/* t must be a quotient or product. Return 1 if t is a fraction or
contains a factor which is a fraction, else return 0. */
{ unsigned short n;
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '/')
return 1;
if(FUNCTOR(t) != '*')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(fractional_factor(ARG(i,t)))
return 1;
}
return 0;
}
/*_______________________________________________________________*/
static int sqrt_condition(term u, term t)
/* Return 1 if sqrtofquotient is to be used on t, which is a SQRT of
a quotient. u is the current line of the computation, of which
t is a part. */
/* Don't use it if t is equal to \pm the current line, or is
one factor of the current line and the others are rational numbers
or terms not containing square roots or roots, and the num and denom
are both squarefree */
/* Designed to get (1/2) (sqrt 2/sqrt 3) changed to (1/2) sqrt(2/3) etc. */
{ term num,denom;
if(NEGATIVE(u))
u = ARG(0,u);
assert(FUNCTOR(t) == SQRT && FUNCTOR(ARG(0,t)) == '/');
num = ARG(0,ARG(0,t));
denom = ARG(1,ARG(0,t));
if(!nsquarefree(num) || !nsquarefree(denom))
return 1; /* use it! the resulting sqrt will simplify */
if(equals(t,u))
return 0; /* don't use it */
if(ONE(num))
return 1; /* DO change sqrt(1/x) to 1/sqrt x */
if(ATOMIC(u) || FUNCTOR(u) != '*' || ARITY(u) != 2)
return 0;
if( INTEGERP(ARG(0,u)) || RATIONALP(ARG(0,u)))
return !equals(t,ARG(1,u)); /* don't use it if u = ct */
if( INTEGERP(ARG(0,u)) || RATIONALP(ARG(1,u)))
return !equals(t,ARG(0,u));
return 0;
}
/*_______________________________________________________________*/
static int contains_integer_sqrt(term t)
/* t is a product; return 1 if one of the factors is a SQRT of an integer */
{ unsigned short n = ARITY(t);
int i;
term u;
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == SQRT && INTEGERP(ARG(0,u)))
return 1;
}
return 0;
}
/*_______________________________________________________________*/
static int contains_integer_root(term t, term index)
/* t is a product; return 1 if one of the factors is an
index-th root of an integer */
{ unsigned short n = ARITY(t);
int i;
term u;
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == ROOT && equals(ARG(0,u),index)
&& INTEGERP(ARG(1,u))
)
return 1;
}
return 0;
}
/*________________________________________________________________*/
static int por_aux2(term num, term denom)
/* called by next function below; return 1 if num is a sqrt or n-th root
of an integer, and denom is a product of such a thing and an integer. */
{ if(FUNCTOR(num) == SQRT && FUNCTOR(denom) == '*' && ARITY(denom)==2
&& INTEGERP(ARG(0,num)) && contains_integer_sqrt(denom)
) /* as in sqrt 3/(2 sqrt 5) which we want to go to (1/2)( sqrt 3/sqrt 5),
whence quotientofsqrts will make it (1/2)sqrt(3/5) */
return 1;
if(FUNCTOR(num) == ROOT && FUNCTOR(denom) == '*' && ARITY(denom)==2
&& INTEGERP(ARG(1,num)) && contains_integer_root(denom,ARG(0,num))
)
return 1;
return 0;
}
/*________________________________________________________________*/
int pulloutrational_aux(term num, term denom)
/* return 1 if pulloutrational should be used on num/denom */
{ if(por_aux2(num,denom))
return 1;
if(por_aux2(denom,num))
return 0;
if(FUNCTOR(num) =='*' && ARITY(num) == 2 &&
INTEGERP(ARG(0,num)) && contains_integer_sqrt(num) &&
FUNCTOR(denom) == '*' && ARITY(denom) == 2 &&
INTEGERP(ARG(0,denom)) && contains_integer_sqrt(denom)
) /* example: (4 \sqrt 3)/(5 \sqrt 7) = (4/5)\sqrt (3/7) */
return 1;
if(FUNCTOR(num) =='*' && ARITY(num) == 2 &&
INTEGERP(ARG(0,num)) && FUNCTOR(ARG(1,num)) == ROOT &&
INTEGERP(ARG(0,ARG(1,num))) &&
FUNCTOR(denom) == '*' && ARITY(denom) == 2 &&
INTEGERP(ARG(0,denom)) && FUNCTOR(ARG(1,denom)) == ROOT &&
equals(ARG(0,ARG(1,denom)),ARG(0,ARG(1,num)))
)
return 1;
return 0;
}
/*_________________________________________________________*/
static int in_sum(void)
/* return 1 if we are working on a summand and on the way up */
/* when going up, the current functor is found at the end of the path.
See documentation of 'push' and 'pop' in autosimp.c */
{ int pathlength = get_pathlength();
unsigned short *path = get_path();
int n;
if(pathlength == 0)
return 0;
if(pathlength == 1)
return path[0] == '+' ? 1: 0;
n = pathlength-1;
if(n&1) /* pathlength even, means it terminates in an object or atom */
--n;
if(path[n] == '^')
n-=2;
if(n<0)
return 0;
if(path[n] == '*')
n-=2;
if(n<0)
return 0;
if(path[n] == '-')
n-=2;
if(n<0)
return 0;
if(path[n] == '+')
return 1;
return 0;
}
/*__________________________________________________________*/
int sqrtexp_conditions(term t)
/* Should we use sqrtexp on sqrt t ? If so return 1 */
{ const int difflag = get_difflag();
int currenttopic = get_currenttopic();
int problemtype = get_problemtype();
int seriesflag = get_seriesflag();
if(problemtype == POWERSERIES && !seriesflag && !constant(t))
{ int i, flag = 0;
term x = get_eigenvariable(); // the series variable, since !seriesflag
if(FUNCTOR(t) == '+' && ARITY(t) == 2)
{ flag = 0;
for(i=0;i<2;i++)
{ if(contains(ARG(i,t),FUNCTOR(x)))
++flag;
}
if(flag == 1) // prepare for using the binomial series
return 1; // sqrt(constant + f(x))
}
}
if(currenttopic == _cubic_one_root ||
currenttopic == _complex_cubics ||
(currenttopic == _diff_from_def && difflag) ||
(currenttopic == _diff_exp_from_defn && difflag)
)
return 0; /* don't rewrite sqrt(93) as 93^(1/2) under the cube root.*/
if(get_radicalflag() == -1)
{ int pathlength = get_pathlength();
unsigned short *path = get_path();
if(pathlength >= 3 &&
path[pathlength-3] == LIMIT
)
return 0; /* don't do it just inside a limit;
only if deeper within a limit. */
if(pathlength >= 5 &&
path[pathlength-5] == LIMIT &&
path[pathlength-3] == '/'
)
return 0; /* don't do it to num and denom just inside a limit either */
return 1;
}
if(iscomplex(t) ||
(NEGATIVE(t) && obviously_positive(ARG(0,t)))
)
return 1; /* needed in de_moivre */
if(!get_intflag() || constant(t))
return 0; /* use it only inside integrals, and don't use it on \sqrt 2 */
if(ISATOM(t) || monomial(t)) /* handle 1/\sqrt x => x^(1/2) and \sqrt (x^n) */
return 1;
return 0; /* but don't touch 1/\sqrt (3x^2-1) */
}
/*________________________________________________________*/
static int unsolved_lineqns(term t)
/* t is presumed to be an AND. Return 0 if any of its
arguments fails to be an equation. Otherwise,
return 0 if all the arguments are solved equations,
1 if any is not solved.
The use of this function is to prevent trying to
solve systems that are already solved, e.g. by applying
Cramer's rule again and again.
*/
{ unsigned short n;
term u;
int i;
if(FUNCTOR(t) != AND)
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(FUNCTOR(ARG(i,t)) != '=')
return 0;
}
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(!ISATOM(ARG(0,u)) || !constant(ARG(1,u)))
/* the i-th equation is not solved */
return 1;
}
return 0;
}
/*__________________________________________________________*/
int contains_double(term t)
/* return 1 if t contains an object of type DOUBLE,
0 if not */
{ unsigned short n;
int i;
if(OBJECT(t) && TYPE(t) == DOUBLE)
return 1;
if(ATOMIC(t))
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_double(ARG(i,t)))
return 1;
}
return 0;
}
/*___________________________________________________________*/
int stop_apart(term t)
/* t is a fraction inside an integral
Return 1 if we should NOT use apart on it, 0 otherwise.
Specifically, return 1 under the following conditions:
(1) if the denominator has the
form cos x \pm sin x or 1 \pm cos x or 1 \pm sin x;
(2) or if the parent is a trig functor, so we don't
touch sin((u+v)/2) for example, because we don't want
it to go to sin(u/2 + v/2) which will be attacked by sinsum
instead of by a half-angle formula.
(3) when the integrand has the form linear/sqrt(linear) so
that u = sqrt(linear) should be used on the whole integral.
The old variable x will be quadratic in u so SQRT will be
eliminated.
*/
{ term x = get_eigenvariable();
term denom,p,q;
unsigned short f,g,h;
int pathlength = get_pathlength();
unsigned short *path = get_path();
assert(FRACTION(t));
denom = ARG(1,t);
if(pathlength >= 3)
{ h = path[pathlength-3];
if(TRIGFUNCTOR(h)) /* this includes arctrig functors too */
return 1;
}
if(FUNCTOR(denom) == SQRT && is_linear_in(ARG(0,denom),x))
return 1;
if(FUNCTOR(denom) != '+')
return 0;
if(ARITY(denom) != 2)
return 0;
p = ARG(0,denom);
q = ARG(1,denom);
if(NEGATIVE(p))
p = ARG(0,p);
if(NEGATIVE(q))
q = ARG(0,q);
f = FUNCTOR(p);
g = FUNCTOR(q);
if(f==SIN && g==COS && equals(ARG(0,p),x) && equals(ARG(0,q),x))
return 1;
if(f==COS && g==SIN && equals(ARG(0,p),x) && equals(ARG(0,q),x))
return 1;
if((f == COS || f == SIN) && ONE(q) && equals(ARG(0,p),x))
return 1;
if((g == COS || g == SIN) && ONE(p) && equals(ARG(0,q),x))
return 1;
return 0;
}
/*________________________________________________________________________*/
static int elimfractexp_conditions(unsigned pathlength, unsigned short *path)
/* don't convert fractional exponents to roots while just inside
another exponent. Return 0 to block elimination of fractional exponents,
1 to permit it. */
{ int k;
if(get_problemtype() == POWERSERIES && get_seriesflag() == 0)
return 0; // don't convert fractional exponents to roots in powerseries as it blocks application of binomialseries
if(pathlength < 3)
return 1;
for(k = pathlength-3; k >= 0; k -= 2)
{ if(path[k] == '^')
return 0;
if(path[k] == '/' || path[k] == '*' || path[0] == '-')
continue;
return 1;
}
return 1;
}
/*______________________________________________________________________*/
static int ok_complexexp(unsigned short *path, int pathlength)
/* path gives the position of t in the current line,
with path[pathlength-3] the position of the parent,
which is presumed to be a product.
Return 1 if this parent does not contain complexi
outside of the subterm t. Return 0 if it does.
*/
{ unsigned short n;
int i,k,err;
term u;
unsigned short saveit = path[pathlength-2];
path[pathlength-2] = 0;
err = subterm_at_path(history(get_activeline()),path,&u);
if(err)
return 1;
path[pathlength-2] = saveit;
if(FUNCTOR(u) != '*')
return 1;
k = path[pathlength-2]-1;
if(k < 0)
assert(0);
n = ARITY(u);
for(i=0;i<n;i++)
{ if(i==k)
continue;
if(iscomplex(ARG(i,u)))
return 0;
}
return 1;
}
/*_________________________________________________________________________*/
static int stop_logofproduct(term t)
/* t is a log term. If its arg is linear with a small integer coefficient,
and it's at toplevel or separated by a minus sign from toplevel then
return 1, else return 0. Examples: on log(3x) or - log(3x) return 1; on
log(x/3) return 0, so this will become log x - log 3. Even log(12 x)
will become log x + log 12 rather than remain log(12 x).
*/
{ unsigned short f = FUNCTOR(t);
term u;
unsigned short *path = get_path();
int pathlength = get_pathlength();
if(pathlength > 5)
return 0;
if(pathlength > 3 && path[0] != '-')
return 0;
if(f == LN || f == LOG)
u = ARG(0,t);
else if(f == LOGB)
u = ARG(1,t);
else
return 0;
if(FUNCTOR(u) == '*' && ARITY(u) == 2 &&
ISINTEGER(ARG(0,u)) && INTDATA(ARG(0,u)) < 10 &&
ATOMIC(ARG(1,u))
)
return 1;
return 0;
}
/*_______________________________________________________________________*/
static int log_in_exponent(term t)
/* return 1 if there is a LN, LOG, or LOGB in an exponent in t,
0 otherwise.
*/
{ unsigned short n,f;
int i;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(f == '^')
{ if(log_in_exponent(ARG(0,t)))
return 1;
return contains_log(ARG(1,t));
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(log_in_exponent(ARG(i,t)))
return 1;
}
return 0;
}
/*__________________________________________________________________________*/
static int logbases(term t, term *a, term *b)
/* return 0 if t has no LOGB subterms; return 1 if there is only one
base of LOGB subterms, and put the base in *a; return 2 if there are
two or more, and put the first two in *a and *b. If 1 is returned,
*b is garbage; if 0 is returned, both *a and *b are garbage.
Nested LOGB terms will be ignored. Don't count terms of the form log(b,b^z)
since the base b log will be simplified away when this term is changed to z very soon.
*/
{ unsigned short n,f;
int i,k,count;
term p,q;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(f == LOGB &&
!(FUNCTOR(ARG(1,t)) == '^' && equals(ARG(0,t),ARG(0,ARG(1,t))))
)
{ *a = ARG(0,t);
return 1; /* Don't look in ARG(1,t) */
}
n = ARITY(t);
count = 0;
for(i=0;i<n;i++)
{ k = logbases(ARG(i,t),&p,&q);
if(k == 0)
continue;
if(k == 1)
{ if(count && !equals(*a,p))
{ *b = p;
return 2;
}
*a = p;
count = 1;
continue;
}
if(k == 2)
{ if(count == 0)
{ *a = p;
*b = q;
return 2;
}
if(count == 1 && !equals(*a,p))
{ *b = p;
return 2;
}
if(count == 1 && !equals(*a,q))
{ *b = q;
return 2;
}
assert(0);
}
}
return count;
}
/*_______________________________________________________________________*/
static int stop_changebase(void)
/* return 1 if all log bases in the active line are the same,
and there are no logs in exponents. */
{ term t,a,b;
int activeline = get_activeline();
int nlogbases;
if(activeline < 0)
return 0;
t = history(activeline);
nlogbases = logbases(t,&a,&b);
if(nlogbases > 1)
return 0;
if(log_in_exponent(t))
return 0;
return 1;
}
/*_________________________________________________________________________*/
static int rationalizable_cubic(term denom)
/* return 1 if denom is of the form root(3,a+sqrt(b))
or a constant times such a term, zero otherwise. Any odd root
is allowed, not just a cube root.
*/
{ unsigned short h = FUNCTOR(denom);
unsigned short n;
int i, rootflag;
term u,v,index;
if(h == ROOT)
{ index = ARG(0,denom);
if(!isodd(index))
return 0;
u = ARG(1,denom);
if(FUNCTOR(u) != '+' || ARITY(u) != 2)
return 0;
v = ARG(1,u);
if(NEGATIVE(v))
v = ARG(0,v);
if(FUNCTOR(v) == SQRT)
return 1;
if(FUNCTOR(v) == '*' && contains_sqrt(v))
return 1;
}
if(h == '*')
{ n = ARITY(denom);
rootflag = 0;
for(i=0;i<n;i++)
{ if(FUNCTOR(ARG(i,denom)) == ROOT)
{ if(rootflag)
return 0;
rootflag = i+1;
}
}
if(!rootflag)
return 0;
if(rationalizable_cubic(ARG(rootflag-1,denom)))
return 1;
}
return 0;
}
/*______________________________________________________________________*/
static int insumfract(void)
/* return 1 if the path terminates in a term which is either
a (possibly negated) summand, or a factor of such a summand. */
{ unsigned short *path = get_path();
int k = get_pathlength()-3;
if(k < 0)
return 0;
while(k >= 0 && (path[k] == '-' || path[k] == '*'))
k -= 2;
if(k < 0)
return 0;
if(path[k] == '+')
return 1;
return 0;
}
/*_____________________________________________________________________*/
static int sg_ok(void)
/* t is an ABSFUNCTOR term in an indefinite integral. Return 1 if it's a
good idea to replace |u| by u sgn u; return 0 if not. We need to
do this after trig substitutions, e.g. to change abs(tan theta)^3 to
tan^3 theta sg(tan theta), etc. But,
ln(|x|) = ln(x sgn(x)) = ln(|x|) + ln(|sgn(x)|)
leads to a regress, and
arcsec |x| = arcsec(x sgn x) is a dead end.
We don't want to use this law anywhere inside a log or an arctrig
functor, e.g. not in arccos(1/|x|), so it won't do just to look at
path[pathlength-3].
*/
{ int k = get_pathlength()-3;
unsigned short *path = get_path();
if(k < 0)
return 0; /* assert(0) */
while(k >= 0)
{ if(UNARY(path[k]) && path[k] != '-')
return 0;
if(path[k] == LOGB || path[k] == ROOT || path[k] == DIFF)
return 0;
if(path[k] == INTEGRAL)
return 1;
k -=2;
}
return 0; /* assert(0), this is only called when t is somewhere in an integral */
}
/*_______________________________________________________________________*/
static int contains_root_in_denom(term t)
/* return 1 if t contains a ROOT in a denominator */
{ int i;
unsigned short n;
if(ATOMIC(t))
return 0;
if(FRACTION(t))
{ if(contains(ARG(1,t),ROOT))
return 1;
return contains_root_in_denom(ARG(0,t));
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_root_in_denom(ARG(i,t)))
return 1;
}
return 0;
}
/*_______________________________________________________________________*/
static int solved_cubic(term t)
/* return 1 if t is one of two cubic equations ready to have
translatevar used on them.
*/
{ term u,v,w;
int i;
if(FUNCTOR(t) != '=')
return 0;
if(!ISATOM(ARG(0,t)))
return 0;
u = ARG(1,t);
if(FUNCTOR(u) != '+' || ARITY(t) != 2)
return 0;
for(i=0;i<2;i++)
{ v = ARG(i,u);
if(FUNCTOR(v) != '*')
return 0;
w = ARG(ARITY(v)-1,v);
if(contains_root_in_denom(w))
return 0;
if(FUNCTOR(w) != '^')
return 0;
if(!equals(ARG(0,w),eulere))
return 0;
if(!iscomplex(ARG(1,w)))
return 0;
}
return 1;
}
/*_____________________________________________________*/
static int oksqrt_aux(term u, term v)
/* At the top-level call:
u contains a SQRT term, v does not;
v contains a fractional exponent, u does not;
return 1 if OK to leave u/v without converting
the SQRT to fractional exponent.
*/
{ int i,err;
unsigned short n;
term p;
if(ATOMIC(u) || ATOMIC(v))
return 1;
if(FUNCTOR(u) == '+' || FUNCTOR(u) == '*')
{ n = ARITY(u);
for(i=0;i<n;i++)
{ if(!oksqrt_aux(ARG(i,u),v))
return 0;
}
return 1;
}
if(FUNCTOR(v) == '+' || FUNCTOR(v) == '*')
{ n = ARITY(u);
for(i=0;i<n;i++)
{ if(!oksqrt_aux(u,ARG(i,v)))
return 0;
}
return 1;
}
if(NEGATIVE(u))
return oksqrt_aux(ARG(0,u),v);
if(NEGATIVE(v))
return oksqrt_aux(u,ARG(0,v));
if(FUNCTOR(u) == SQRT && !INTEGERP(ARG(0,u)))
return 0;
if(FUNCTOR(v) == '^' && !(INTEGERP(ARG(0,v)) && SIGNEDRATIONAL(ARG(1,v))))
return 0;
if(FUNCTOR(u) == SQRT && FUNCTOR(v) == '^')
{ err = gcd(ARG(0,u),ARG(0,v),&p);
if(err || !ONE(p))
return 0;
return 1;
}
return 0;
}
/*_____________________________________________________*/
static int oksqrt(term t)
/* return 1 if it's ok to leave both fractional exponents
and square roots in t; return 0 if not. Example: return 1 on
(sqrt(3)-i)/2^(3/4). We want to watch out for cases where
we might miss some simplifications, like sqrt(2)/2^(1/4).
*/
{ int i;
unsigned short n;
term u,v;
if(ATOMIC(t))
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(!oksqrt(ARG(i,t)))
return 0;
}
if(!FRACTION(t))
return 1;
/* Now look for cancellations we might miss if we don't
convert sqrts to fractional exponents */
u = ARG(0,t);
v = ARG(1,t);
if(contains(u,SQRT) && contains_fractional_exponents(u))
return 0;
if(contains(v,SQRT) && contains_fractional_exponents(v))
return 0;
if(contains(v,SQRT) && contains_fractional_exponents(u))
{ term swap = u;
u = v;
v = swap;
}
if(!(contains(u,SQRT) && contains_fractional_exponents(v)))
return 1;
return oksqrt_aux(u,v);
}
/*_______________________________________________________*/
static int powers_match(term t)
/* t is a sum; this is the condition for using addseries or subseries.
All the summands should be series (or negative series) or constants times series,
and the exponents on the general terms should be the same, and the index
variables must be the same. Return 1 if these conditions are met, 0 if not.
*/
{ int n = ARITY(t);
int i;
int ct = 0;
term x = get_eigenvariable();
term u,c,v,w,exp;
if(FUNCTOR(t) != '+')
return 0;
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(NEGATIVE(u))
u = ARG(0,u);
twoparts(u,x,&c,&v);
if(FUNCTOR(v) != SUM)
continue;
else
++ct;
w = ARG(0,v);
twoparts(w,x,&c,&v);
if(FUNCTOR(v) != '^')
return 0;
if(!equals(x,ARG(0,v)))
return 0;
if(ct == 1)
exp = ARG(1,v);
else
{ if(!equals(exp,ARG(1,v)))
return 0;
}
}
return 1;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists