Sindbad~EG File Manager
/* auto mode for differentiation, limits, and integration */
/*
M. Beeson
Original date 9.10.91
Last modified 2.27.99
6.14.00 modified stop_lhopital
3.9.01 added contains_trigsquare and code that uses it, to integrate x cos^2 x
3.10.01 last modified for version 1.624
6.14.04 last modified
1.28.06 changed conditions for difpoly
4.12.06 added limabs to autolimit
5.6.13 include stdef.h
5.9.13 modified autoint to not evaluate integrals in POWERSERIES except inside the series;
but intseries (term by term integration) is used.
changed limitflag to get_limitflag()
5.29.13 modified autocalc not to differentiate ln(a/b) or ln(ab) directly.
5.30.13 modified use_logdif, but only for topic _logarithmic_differentiation.
6.10.13 added intsub at line 1286 to deal with integral(x e^(-x^2),x)
9.26.14 removed unused function contains_half_power.
10.22.23 and its prototype
5.8.24 corrected 't' to 'u' in the DIFF case of autodif
2.16.25 commented out unused 'logflag' in autolimit
*/
#include <assert.h>
#include <string.h>
#include <stddef.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h" /* coord */
#include "mpdoc.h"
#include "tdefn.h"
#include "checkarg.h" /* operator typedef */
#include "ops.h"
#include "calc.h" /* prototypes of operators */
#include "probtype.h"
#include "prover.h" /* for NOTDEFINED */
#include "polynoms.h"
#include "match.h"
#include "cancel.h"
#include "factor.h"
#include "automode.h"
#include "integral.h" /* islinear */
#include "mplimits.h" /* LIMITAND */
#include "order.h" /* constant */
#include "symbols.h"
#include "trigpoly.h" /* algpoly */
#include "trig.h" /* TRIGFUNCTOR */
#include "pvalaux.h" /* twoparts */
#include "cflags.h"
#include "intsub.h" /* linearsqrt */
#include "islinear.h"
#include "autosimp.h" /* get_whichpass */
#include "limval.h" /* contains_exp */
#include "autocalc.h"
#include "limval2.h" /* contains_in_exp */
#include "gcdsub.h" /* get_gcdsub */
#include "series.h" /* intseries etc. */
#include "limval2.h" /* lhopital_certain */
/* Public functions, called in automode.c */
void autodif(term, int, actualop *, int *);
void autoint(term, term *, int, actualop *, int *);
void autolimit(term, int, actualop *, int *);
static int trigpower_quotient(term u, term x);
static int pimultiple(term a);
static int odd_mul_piover2(term a);
static int polysqrt(term t, term x);
static int contains_trigsquare(term u,term x);
/*________________________________________________________________*/
void autodif(term t, int i, actualop *o, int *nops)
/* t has functor DIFF; put into array o starting at i the operators
that might be used to simplify t; return the final value of i in *nops */
{ unsigned g;
term u = ARG(0,t);
int topic = get_currenttopic();
assert(FUNCTOR(t)==DIFF);
if(ARITY(t) == 3) /* a higher derivative, t = diff(u,x,n) */
{ if(ISATOM(u)) /* leave d^n y/dx^n alone when y is an atom */
{ *nops = i;
return;
}
if(OBJECT(u))
{ o[i] = difconstant; ++i;
*nops = i;
return;
}
if(!PREDEFINED_FUNCTOR(FUNCTOR(u)) &&
ISATOM(ARG(0,u))
)
{ *nops = i; /* leave d^n /dx^n f(x) alone */
return;
}
if(equals(ARG(2,t),two))
{ o[i] = difdif; ++i;
}
else
{ o[i] = difdifn; ++i;
}
*nops = i;
return;
}
assert(ARITY(t) == 2);
if(!get_limitflag() && get_problemtype() == DIFFERENTIATE_FROM_DEFN)
{ o[i] = defnofderivative; ++i;
*nops = i;
return;
}
o[i] = difconstant; ++i; /* e.g. d/dx ln(10); argument can be complicated */
if(ATOMIC(u))
{ o[i] = difidentity; ++i;
*nops = i;
return;
}
g = FUNCTOR(u);
if(
ispolyin(u,ARG(1,t)) &&
status(difpoly)>= KNOWN
)
/* difpoly doesn't work on x/a anyway although it passes ispolyin */
{ o[i] = difpoly; ++i;
*nops = i;
return;
}
switch(g)
{ case '+' :
o[i] = difsum; ++i;
break;
case '-' :
o[i] = difminus; ++i;
break;
case '*' :
o[i] = diflinear; ++i;
if(topic == _logarithmic_differentiation ||
( status(logdif) >= LEARNING &&
use_logdif(u,ARG(1,t))
)
)
{ o[i] = logdif; ++i;
break;
}
o[i] = difproduct; ++i;
break;
case '/' :
o[i] = diflinear2; ++i;
o[i] = difinversepower; ++i;
o[i] = difrecip; ++i;
if(topic == _logarithmic_differentiation ||
(status(logdif) >= LEARNING && use_logdif(u,ARG(1,t)))
)
{ o[i] = logdif; ++i;
break;
}
o[i] = difquotient; ++i;
break;
case '^' :
o[i] = difpower; ++i;
o[i] = difpower2; ++i;
o[i] = difexp; ++i;
o[i] = difexp2; ++i;
o[i] = difatox; ++i;
o[i] = difatox2; ++i;
o[i] = difexponential; ++i;
break;
case ROOT:
o[i] = difroots; ++i;
break;
case SQRT:
if(topic == _logarithmic_differentiation ||
( status(logdif) >= LEARNING &&
(FUNCTOR(ARG(0,u)) == '*' || FUNCTOR(ARG(0,u)) == '/') &&
use_logdif(ARG(0,u),ARG(1,t))
)
)
{ o[i] = logdif; ++i;
}
o[i] = difsqrt; ++i;
o[i] = difsqrt2; ++i;
break;
case SIN:
o[i] = difsin; ++i;
o[i] = difsin2; ++i;
break;
case COS:
o[i] = difcos; ++i;
o[i] = difcos2; ++i;
break;
case TAN:
o[i] = diftan; ++i;
o[i] = diftan2; ++i;
break;
case SEC:
o[i] = difsec; ++i;
o[i] = difsec2; ++i;
break;
case CSC:
o[i] = difcsc; ++i;
o[i] = difcsc2; ++i;
break;
case COT:
o[i] = difcot; ++i;
o[i] = difcot2; ++i;
break;
case ATAN:
o[i] = difatan; ++i;
o[i] = difatan2; ++i;
break;
case ASIN:
o[i] = difasin; ++i;
o[i] = difasin2; ++i;
break;
case ACOS:
o[i] = difacos; ++i;
o[i] = difacos2; ++i;
break;
case ACOT:
o[i] = difacot; ++i;
o[i] = difacot2; ++i;
break;
case ASEC:
o[i] = difasec; ++i;
o[i] = difasec2; ++i;
break;
case ACSC:
o[i] = difacsc; ++i;
o[i] = difacsc2; ++i;
break;
case ATANH:
o[i] = difatanh; ++i;
break;
case ASINH:
o[i] = difasinh; ++i;
break;
case ACOSH:
o[i] = difacosh; ++i;
break;
case ACOTH:
o[i] = difacoth; ++i;
break;
case ASECH:
o[i] = difasech; ++i;
break;
case ACSCH:
o[i] = difacsch; ++i;
break;
case LN :
switch(FUNCTOR(ARG(0,ARG(0,t))))
{ case ABSFUNCTOR:
o[i] = diflnabs; ++i;
o[i] = diflnabs2; ++i;
break;
case COS:
o[i] = diflncos; ++i;
break;
case SIN:
o[i] = diflnsin; ++i;
break;
case SINH:
o[i] = diflnsinh; ++i;
break;
case COSH:
o[i] = diflncosh; ++i;
break;
case '/': break; /* use ln(a/b) = ln a - ln b first */
case '*': break; /* use ln(ab) = ln a + ln b first */
default:
o[i] = difln; ++i;
o[i] = difln2; ++i;
break;
}
break;
case SINH:
o[i] = difsinh; ++i;
break;
case COSH:
o[i] = difcosh; ++i;
break;
case TANH:
o[i] = diftanh; ++i;
break;
case COTH:
o[i] = difcoth; ++i;
break;
case CSCH:
o[i] = difcsch; ++i;
break;
case SECH:
o[i] = difsech; ++i;
break;
case ABSFUNCTOR:
o[i] = difabs; ++i;
o[i] = difabs2; ++i;
break;
case SG:
o[i] = difsg; ++i;
break;
case BESSELJ:
case BESSELY:
case BESSELI:
case BESSELK: break; /* FINISH THIS */
case SUM:
o[i] = difsigma; ++i;
break;
case VECTOR:
o[i] = difvector; ++i;
break;
case MATRIX:
o[i] = difmatrix; ++i;
break;
case DIFF:
if(ARITY(u)==2)
{ o[i] = secondderiv; ++i;
break;
}
if(ARITY(u)==3)
{ o[i] = highderiv; ++i;
break;
}
break;
default:
o[i] = chainrule; ++i;
break;
}
*nops = i;
}
/*________________________________________________________________*/
void autolimit(term t, int i, actualop *o, int *nops)
/* t has functor LIMIT; put into array o starting at i the operators
that might be used to simplify t; return the final value of i in *nops */
{ unsigned g,h,k;
term u,v,a,x,num,denom;
int lnabsflag,tflag;
unsigned n;
int changelimitvarflag = 0;
assert(FUNCTOR(t)==LIMIT);
u = LIMITAND(t);
a = ARG(1,ARG(0,t)); /* where the limit is going to */
x = ARG(0,ARG(0,t)); /* the limit variable */
/* limconst, limident, and limpoly are in pre_ops */
g = FUNCTOR(u);
switch(g)
{ case '+' :
o[i] = limdif; ++i;
o[i] = limsum; ++i;
o[i] = limsum3; ++i; /* eliminate neg exponents in sum */
o[i] = limsum4; ++i; /* cot = 1/tan etc in sum */
if(status(sumleadingterm) >= LEARNING)
{ o[i] = sumleadingterm; ++i;
/* example, ln(t) + 1/t + ... */
}
o[i] = limsum1; ++i; /* factor denominators */
o[i] = limsum2; ++i; /* take common denom in sum */
o[i] = rationalizesum; ++i;
break;
case ABSFUNCTOR:
o[i] = limabs;++i;
break;
case SUM:
o[i] = limsum; ++i;
break;
case '-' :
/* limlinear and limminus are in pre_ops */
break;
case '*' :
/* limlinear is in pre_ops */
n = ARITY(u);
lnabsflag = tflag = 0;
// logflag = 0; // logflag is never actually used
for(k=0;k<n;k++)
{ v = ARG(k,u);
h = FUNCTOR(v);
// if(h== LN)
// ++logflag; // it's never actually used, so never mind setting it
if(h == ABSFUNCTOR && FUNCTOR(ARG(0,v)) == LN)
++lnabsflag;
if(h == '^' && (FUNCTOR(ARG(0,v)) == SIN || FUNCTOR(ARG(0,v))==COS))
++tflag;
if(h == SIN || h == COS)
++tflag;
}
if(n==2 && ZERO(a) && status(limtlnt) >= LEARNING)
{ if(equals(ARG(0,u),x) && FUNCTOR(ARG(1,u)) == LN && equals(ARG(0,ARG(1,u)),x))
{ o[i] = limtlnt; ++i;
}
if(equals(ARG(0,u),x) &&
FUNCTOR(ARG(1,u)) == '^' &&
FUNCTOR(ARG(0,ARG(1,u))) == LN &&
equals(ARG(0,ARG(0,ARG(1,u))),x)
)
{ o[i] = limtlntpower; ++i;
}
if(FUNCTOR(ARG(0,u)) == '^' &&
equals(ARG(0,ARG(0,u)),x) &&
FUNCTOR(ARG(1,u)) == LN &&
equals(ARG(0,ARG(1,u)),x)
)
{ o[i] = limtpowerlnt; ++i;
}
if(FUNCTOR(ARG(0,u)) == '^' &&
equals(ARG(0,ARG(0,u)),x) &&
FUNCTOR(ARG(1,u)) == '^' &&
FUNCTOR(ARG(0,ARG(1,u))) == LN &&
equals(ARG(0,ARG(0,ARG(1,u))),x)
)
{ o[i] = limtpowerlntpower; ++i;
}
}
o[i] = limprod; ++i;
if(ARITY(u) == 2 && contains_at_toplevel(u,LN))
{ o[i] = limprod2right; ++i;
}
o[i] = pulloutnonzerolimit; ++i;
if((lnabsflag) && status(lhopital) > LEARNING)
{ o[i] = limpowertimeslnabs; ++i;
}
if(tflag)
{ o[i] = squeezetheorem; ++i;
}
o[i] = createcompoundfraction; ++i;
/* isolateln, isolatelnpower, negexptodenom, exptodenom,
and trigtodenom are used in menu mode, but in auto
mode createcompoundfraction does all the work of these
operators */
break;
case '/' : /* limlinear is in pre_ops */
num = ARG(0,u);
denom = ARG(1,u);
if(NOTDEFINED(a))
{ o[i] = inflimpower1; ++i;
}
if(get_currenttopic() != _lhopitals_rule)
/* under this topic, these limits are derived */
{ o[i] = limsin1; ++i;
o[i] = limtan1; ++i;
o[i] = limcos1; ++i;
o[i] = limcos2; ++i;
o[i] = limln1; ++i;
o[i] = limexp1; ++i;
o[i] = limexp2; ++i;
o[i] = limsinh1; ++i;
o[i] = limtanh1; ++i;
o[i] = limcosh1; ++i;
o[i] = limcosh2; ++i;
}
if(equals(a,infinity))
{ if(FUNCTOR(num) == LN &&
equals(ARG(0,num),x) &&
equals(denom,x)
)
{ o[i] = limlntovert; ++i;
}
if(FUNCTOR(num) == '^' &&
FUNCTOR(ARG(0,num)) == LN &&
equals(ARG(0,ARG(0,num)),x) &&
equals(denom,x)
)
{ o[i] = limlntpowerovert; ++i;
}
if(FUNCTOR(num) == LN &&
equals(ARG(0,num),x) &&
FUNCTOR(denom) == '^' &&
FUNCTOR(ARG(0,denom)) == LN &&
equals(ARG(0,ARG(0,denom)),x)
)
{ o[i] = limlntovertpower; ++i;
}
if(FUNCTOR(num) == '^' &&
FUNCTOR(ARG(0,num)) == LN &&
equals(ARG(0,ARG(0,num)),x) &&
FUNCTOR(denom) == '^' &&
equals(ARG(0,denom),x)
)
{ o[i] = limlntpowerovertpower; ++i;
}
if(FUNCTOR(denom) == LN &&
equals(ARG(0,denom),x) &&
equals(num,x)
)
{ o[i] = limtoverlnt; ++i;
}
if(equals(num,x) &&
FUNCTOR(denom) == '^' &&
FUNCTOR(ARG(0,denom)) == LN &&
equals(ARG(0,ARG(0,denom)),x)
)
{ o[i] = limtoverlntpower; ++i;
}
if(FUNCTOR(denom) == LN &&
equals(ARG(0,denom),x) &&
FUNCTOR(num) == '^' &&
equals(ARG(0,num),x)
)
{ o[i] = limtpoweroverlnt; ++i;
}
if(FUNCTOR(num) == '^' &&
equals(ARG(0,num),x) &&
FUNCTOR(denom) == '^' &&
FUNCTOR(ARG(0,denom)) == LN &&
equals(ARG(0,ARG(0,denom)),x)
)
{ o[i] = limtpoweroverlntpower; ++i;
}
}
/* other known limits of indeterminate quotients go in here */
o[i] = quotientofpowers; ++i;
o[i] = multnumdenom; ++i;
if(NOTDEFINED(a) && rational_function(u,x) &&
get_currenttopic() != _limleadingterm
)
/* limits of rational functions at infinity */
/* when a is finite, use cancelgcd, and
when not both num and denom go to zero,
just use limquotient */
{ if(status(limrationalfunction) >= KNOWN)
{ o[i] = limrationalfunction; ++i;
}
else if(use_leadingterms(u,x))
{ o[i] = limleadingterms; ++i;
/* this will work and not look mysterious
when applied to a rational function */
}
}
o[i] = limrecip; ++i;
if(!NOTDEFINED(a) && !ZERO(a) &&
rational_function(u,x) &&
(FUNCTOR(ARG(0,u)) == '+' || FUNCTOR(ARG(1,u)) == '+')
)
{ o[i] = factorunderlimit; ++i;
}
o[i] = limquotient; ++i;
/* limquotient fails if both num and denom go to zero
or infinity, but it allows one of them to do so or
one to go to zero and the other to infinity.
Therefore, beyond this point we are dealing with
an indeterminate form.
*/
/* Example, lim(x->infinity, x/sqrt(x-1)). If we bring the
sqrt outside we get a limit of a rational function.
Example, lim(x->0, (ln ln x)/sqrt x). If we bring the
sqrt outside we get an impossible mess. Instead we
must use L'Hospital's rule. So use the rules for bringing
roots out VERY sparingly.
*/
if(FUNCTOR(num) == SQRT &&
ispolyin(ARG(0,num),x) &&
ispolyin(denom,x)
)
{ o[i] = limsqrt1; ++i;
o[i] = limsqrt2; ++i;
}
if(FUNCTOR(denom) == SQRT &&
ispolyin(ARG(0,denom),x) &&
ispolyin(num,x)
)
{ if(status(sumleadingterm) >= LEARNING)
{ o[i] = sumleadingterm; ++i;
/* example, x/sqrt(x^2+1) */
}
o[i] = limsqrtdenom1; ++i;
o[i] = limsqrtdenom2; ++i;
}
if(FUNCTOR(num) == ROOT &&
ispolyin(ARG(1,num),x) &&
ispolyin(denom,x)
)
{ o[i] = limroot1; ++i;
o[i] = limroot2; ++i;
}
if(FUNCTOR(denom) == ROOT &&
ispolyin(ARG(1,denom),x) &&
ispolyin(num,x)
)
{ o[i] = limrootdenom1; ++i;
o[i] = limrootdenom2; ++i;
}
if(get_currenttopic() == _lhopitals_rule &&
!stop_lhopital(t) /* e.g. x-sqrt(x^2+x) needs invertlimit first */
)
/* example, (sin^2 x)/x, use L'Hopital under that topic */
{ o[i] = lhopital; ++i;
}
o[i] = pulloutnonzerolimit; ++i;
/* pulloutnonzerolimit must precede divnumdenom, else
divnumdenom makes complicated expressions that could
be avoided and are hard to solve, for example in
differentiating tan x from definition of deriv.
*/
if(algpoly2(num) && algpoly2(denom) &&
status(limrationalfunction) >= KNOWN
)
{ o[i] = sumleadingterm; ++i;
}
if(algpoly(num) && algpoly(denom))
{ if(contains(u,SQRT) || contains(u,ROOT) ||
contains_fractional_exponents(u)
)
/* Example: (1+sqrt(u))/(1-sqrt(u)) should be converted
to a rational function. But, don't use changelimitvariable
on (1+x^4)/x^2 introducing u = x^2.
*/
{ o[i] = changelimitvariable; ++i;
changelimitvarflag = 1;
}
o[i] = divnumdenom; ++i;
}
/* Example: if we don't put divnumdenom before rationalizefraction:
lim((x + sqrt (x^2-x))/x) => lim( x/(x- sqrt (x^2-x)) => loop. */
/* Example: don't use divnumdenom on
lim(x->0,(2x - arcsin x)/(2x + arccos x)).
Use L'Hospital's rule directly. */
/* Note that algpoly will include the first example but not
the second; it will also include all rational functions */
/* Next comes 'limapartandfactor', which does this:
(ab+ac+d)/q = a(b+c)/q + d/q
We need this to find the derivative of sin x
direct from the definition of derivative:
(sin x cos h + cos x sin h - sin x)/h =
(sin x)(cos h - 1)/h + (cos x sin h)/h
But, it makes trouble in taking limits of rational
functions, so we forbid its use in that case; and
if L'Hopital is known, its desirable use can be
blocked by limapartandfactor, so don't use it
then either. But use it anyway if we are
differntiating from defn. In calc 2, L'Hopital
is known even when differentiating from defn,
although it isn't used then in auto mode.
*/
if(
!(ispolyin(num,x) && ispolyin(denom,x)) &&
(status(lhopital) == UNKNOWN ||
get_problemtype() == DIFFERENTIATE_FROM_DEFN
)
)
{ o[i] = limapartandfactor; ++i;
}
if(!changelimitvarflag &&
!contains_in_exponent(u,FUNCTOR(x)) &&
!(FRACTION(u) && trigpolyargs2(ARG(0,u),x) && ispolyin(ARG(1,u),x))
/* example, lim(x->infinity,(x+sin(x))/(2x+3)), which
should be done by L'Hopital */
)
{ o[i] = changelimitvariable; ++i;
changelimitvarflag = 1;
/* example, (x-16)/(x^1/4-2); this goes
MUCH better after u = x^1/4. Rationalize
fraction will work on it and make a mess.
*/
}
if(status(lhopital)==UNKNOWN)
{ o[i] = rationalizefraction; ++i;
}
else if(ispolyin(num,x))
{ o[i] = rationalizedenom; ++i;
/* don't rationalize num, use L'Hopital instead,
e.g. on lim(x->a,(root(3,x)-root(3,a)) /(x-a));
and don't even rationalize denom unless the
numerator is a polynomial, e.g. don't do it on
lim(x->infinity, (ln ln x)/sqrt x), where it
makes an unsolvable mess.
*/
}
if(OBJECT(num))
{ o[i] = limquoinfinite; ++i;
}
if(!stop_lhopital(t))
{ o[i] = lhopital; ++i;
}
o[i] = squeezetheorem; ++i;
break;
case '^' : /* defnofe is in pre_ops */
if(equals(ARG(0,u),eulere))
{ o[i] = limcontinuous; ++i;
}
o[i] = limpower; ++i;
if(equals(ARG(0,u),eulere) && is_linear_in(ARG(1,u),x))
{ /* These two operators will evaluate lim e^u even for
very complicated u as long as limval can compute
lim u to be plus or minus infinity; but we don't
use it unless the exponent is a linear;
otherwise limexponent2 should be used.
Example: lim(x->infinity, e^(-x^2))
should not be done in one step quoting lim(x->-inf,e^x) = 0,
but rather in two steps, passing the limit into the
exponent and then quoting limit of polynomial to get
e^-infinity = 0.
*/
o[i] = limexpinf; ++i;
o[i] = limexpinf2; ++i;
}
if(depends(ARG(0,u),x)) /* non constant base */
{ /* examples: x^(sin x/x) as x->0. Don't use
limexptolog on this. On the other hand
x^x HAS to be written e^x log x.
The operator limexponent will work in
automode only if the limit of the base
is defined and, if it's zero, the limit
of the exponent is not zero; or if the
limit of the base is infinity and the
limit of the exponent is defined and not zero.
On the other hand, limexptolog will work
on anything it gets, as long as the base is
positive and not eulere, provided the
problemtype is not DIFFERENTIATE_FROM_DEFN,
where L'Hopital's rule is not allowed.
In that case it won't work if the limit
tends to the form 1^infinity.
*/
o[i] = limexponent; ++i;
o[i] = limexptolog; ++i;
}
else
{ o[i] = limexponent2; ++i;
}
if(NOTDEFINED(a))
{ o[i] = inflimpower1; ++i;
o[i] = inflimpower2; ++i;
o[i] = limthruexp; ++i;
}
o[i] = liminverseevenpower; ++i;
o[i] = liminverseoddpower; ++i;
o[i] = lim1inverseleft; ++i;
o[i] = lim1inverseright; ++i;
if(NOTDEFINED(a))
{ o[i] = limrecip; ++i;
} /* introduce infinite limits in denominator only
as a last resort */
break;
case ROOT:
o[i] = limoddroot; ++i;
o[i] = limevenroot; ++i;
if(ISINFINITE(a))
{ o[i] = limrootinf; ++i;
}
break;
case SQRT:
o[i] = limsqrt; ++i;
if(ISINFINITE(a))
{ o[i] = limsqrtinf; ++i;
}
break;
case LN:
o[i] = limlnsing; ++i;
if(! (equals(a,infinity) && equals(u,x)) &&
/* lim(x->infinity, ln x) is handled directly rather
than as ln(lim(x->infinity,x) = ln(infinity) = infinity */
! (FRACTION(ARG(0,u)) && ONE(ARG(0,ARG(0,u))))
/* lim ln(1/v) = -lim ln v is better */
)
{ o[i] = limthrulog; ++i;
}
if(ISINFINITE(a))
{ o[i] = limlnright; ++i;
}
break;
case SIN:
o[i] = limoscsin; ++i;
if(ISINFINITE(a))
{ o[i] = liminfsin; ++i;
}
o[i] = limthrusin; ++i;
break;
case COS:
o[i] = limosccos; ++i;
if(ISINFINITE(a))
{ o[i] = liminfcos; ++i;
}
o[i] = limthrucos; ++i;
break;
case TAN:
o[i] = limtansing; ++i;
o[i] = limosctan; ++i;
if(ISINFINITE(a))
{ o[i] = liminftan; ++i;
}
break;
case COT:
if(ISINFINITE(a))
{ o[i] = cottotan; ++i;
}
o[i] = limcotsing; ++i;
break;
case SEC:
if(ISINFINITE(a) || odd_mul_piover2(a))
{ o[i] = secrule; ++i;
}
o[i] = limsecsing; ++i;
break;
case CSC:
if(ISINFINITE(a) || pimultiple(a))
{ o[i] = cscrule; ++i;
}
o[i] = limcscsing; ++i;
break;
case ATAN:
if(ISINFINITE(a))
{ o[i] = limarctaninf; ++i;
}
break;
case TANH:
if(ISINFINITE(a))
{ o[i] = limtanhinf; ++i;
}
break;
}
if(ARITY(u) == 1 && FUNCTOR(u) != '-' &&
FUNCTOR(ARG(0,u)) == '^' &&
equals(ARG(0,ARG(0,u)),eulere) &&
!changelimitvarflag
)
/* example, lim(x->infinity, cos(e^-x)) */
{ o[i] = changelimitvariable; ++i;
changelimitvarflag = 1;
}
/* we also need to use changelimitvariable on lim(x->0, (1-e^(-x))/x),
and also lim(x->0, (1-e^(cx))/x). This only is used in
problemtype DIFFERENTIATE_FROM_DEFN where L'Hopital is forbidden. */
if(!changelimitvarflag &&
ZERO(a) && FRACTION(u) && equals(ARG(1,u),x) &&
FUNCTOR(ARG(0,u)) == '+' && ARITY(ARG(0,u)) == 2 &&
ONE(ARG(0,ARG(0,u))) && NEGATIVE(ARG(1,ARG(0,u))) &&
FUNCTOR(ARG(0,ARG(1,ARG(0,u)))) == '^' &&
equals(ARG(0,ARG(0,ARG(1,ARG(0,u)))), eulere)
)
{ o[i] = changelimitvariable; ++i;
changelimitvarflag = 1;
}
if(!NOTDEFINED(a))
{ o[i] = limcontinuous; ++i; /* FINISH THIS Make this usable at infinity too */
}
if(!changelimitvarflag &&
!(FRACTION(u) && trigpolyargs2(ARG(0,u),x) && ispolyin(ARG(1,u),x)) &&
/* example, lim(x->infinity,(x+sin(x))/(2x+3)), which
should be done by L'Hopital */
(!contains_in_exponent(u,FUNCTOR(x)) || contains_in_exponent(u,'/') || !ISINFINITE(a))
)
{ o[i] = changelimitvariable; ++i; /* This can be used at infinity,
but don't introduce 1/x in the exponent */
changelimitvarflag = 1;
}
if(NOTDEFINED(a) &&
!contains_exp(u,x) &&
!contains_in_exponent(u,FUNCTOR(x)) &&
!contains_calc(u) && // no integrals or derivatives in the limitand; it will fail anyway in those cases
/* don't use invertlim if it will create e^(1/x). */
!(FRACTION(u) && ispolyin(ARG(0,u),x) && ispolyin(ARG(1,u),x))
/* Use L'Hopital instead of invertlim in that case, e.g. (x + sin x)/(x+3)*/
)
{ o[i] = invertlim; ++i;
}
else if(ISINFINITE(a) && FUNCTOR(u) == '+' && polysqrt(u,x) && !contains_calc(u))
/* poly(x) - sqrt(poly(x)) needs invertlimit,
unless rationalizesum works, or changelimitvariable works as in sqrt(1+x^2) - root(4,x) */
{ o[i] = invertlim; ++i;
}
if(
get_whichpass() > 1 ||
(get_whichpass() == 1 && !contains_hypertrig(u))
)
/* as a last resort use leading terms; when whichpass is 1 we still
don't use it as hypertrig functions don't get expanded to
exponentials until whichpass is 2.
*/
{ if(g == '/')
{ o[i] = limleadingterms; ++i;
}
else if(g == '+')
{ o[i] = sumleadingterm; ++i;
}
else
{ o[i] = limleadingterm; ++i;
}
}
*nops = i;
}
/*________________________________________________________________*/
void autoint(term t, term *arg, int i, actualop *o, int *nops)
/* t has functor INTEGRAL; put into array o starting at i the operators
that might be used to simplify t; return the final value of i in *nops
*/
{ unsigned short g,h;
unsigned short const * path = get_path();
term u,v,x,a,b,power,newpower,num,denom;
int currenttopic = get_currenttopic();
int problemtype = get_problemtype();
int pathlength = get_pathlength();
int j;
if(problemtype == POWERSERIES)
{ o[i] = intseries; ++i; // integrate power series term by term
/* don't try to evaluate an integral that was just introduced by f = integral(diff(f,x),x);
but after the derivative is expanded in a series and the integral is pushed into the series, it
will need to be evaluated. So, SUM should be on the path. */
for(j=0;j<pathlength; ++j)
{ if(path[j] == SUM)
break;
}
if(j==pathlength && !contains(t,SUM))
{ /* this integral isn't inside SUM, so don't try to evaluate it until the
integrand has been expanded in a series. */
*nops = i;
return;
}
}
assert(FUNCTOR(t)==INTEGRAL);
u = ARG(0,t); /* the integrand */
x = ARG(1,t); /* the variable of integration */
if(ARITY(t) == 4 && equals(ARG(2,t),ARG(3,t)))
{ o[i] = integrateemptyinterval; ++i;
*nops = i;
return;
}
if(ATOMIC(u))
{ o[i] = int1; ++i;
o[i] = intconst; ++i;
o[i] = intident; ++i;
*nops = i;
return;
}
if(ARITY(t) == 4 && contains(u,ABSFUNCTOR) && FUNCTOR(u) != ABSFUNCTOR)
{ o[i] = insertpoint; ++i;
/* when FUNCTOR(u) == ABSFUNCTOR, breakabsint is used. */
}
if(!contains(u,FUNCTOR(x)))
{ o[i] = intconst; ++i; /* example: u = sin 3 */
*nops = i;
return;
/* even if u does depend on x indirectly by definitions,
none of the integration operators below will help. */
}
if(contains(u,SG))
{ o[i] = sgint; ++i;
}
g = FUNCTOR(u);
/* intpoly is in preops, so polynomials are integrated already if
intpoly is KNOWN */
/* odd powers of sec x (more than 3) have to use intsecpower;
sec^3 x can be done by parts;
as can products sec^n x tan^m x where n is odd and m is even
We have to trap these special cases and might as well do it
sooner as later. */
if( g=='^' && !matchstring(u,x,"^(sec(x),a)",&a) &&
INTEGERP(a) && ISODD(a)
)
{ if(equals(a,three) &&
status(intsecpower) == UNKNOWN &&
equals(t,history(get_currentline())) /* so equatetoproblem can work */
)
{ value(sum(a,tnegate(two)),&newpower);
*arg = make_power(ARG(0,u),newpower);
o[i] = integratebyparts; ++i;
*nops = i;
return;
}
o[i] = intsecpower; ++i;
*nops = i;
return;
}
switch(g)
{ /* intminus, intsum, intlinear are in pre_ops */
case '^' :
v = ARG(0,u);
h = FUNCTOR(v);
power = ARG(1,u);
if(equals(power,minusone))
{ if(equals(v,x))
{ o[i] = intrecip; ++i;
}
else if(FUNCTOR(v) == '+' &&
(equals(ARG(0,v),x) || equals(ARG(ARITY(v)-1,v),x))
)
{ o[i] = intrecip2; ++i;
}
else
{ o[i] = intrecip3; ++i;
}
}
o[i] = intpower; ++i;
if(!depends(v,x) &&
!equals(power,x) && /* c^x is integrated by a special rule */
depends(power,x)
)
{ o[i] = intexponential; ++i;
}
if(equals(v,eulere))
{ if(equals(power,x))
{ o[i] = intexp1; ++i; /* integral e^t dt = e^t */
}
if(FUNCTOR(power) == '*')
{ o[i] = intexp2; ++i; /* integral e^at dt =(1/a) e^(at) */
}
if(NEGATIVE(power))
{ o[i] = intexp3; ++i; /* integral e^(-t)dt = -e^(-t) */
o[i] = intexp4; ++i; /* integral e^(-at)dt = -(1/a) e^(-at) */
}
if(FRACTION(power))
{ o[i] = intexp5; ++i; /* integral e^(t/a)dt = a e^(t/a) */
}
o[i] = inttoerf; ++i; /* integral e^(-t^2) dt = sqrt pi/2 Erf(t) */
}
else
{ o[i] = intexp6; ++i; /* integral c^t dt = (1/ln c) c^t */
}
break;
case '/' :
if(polyquo(u,x)) /* rational function in quotient-of-polynomials form */
{ if(!contains(ARG(0,u),FUNCTOR(x)))
{ term denom = ARG(1,u);
if(equals(denom,x))
{ o[i] = intrecip; ++i;
}
else if(FUNCTOR(denom) == '+' &&
(equals(ARG(0,denom),x) || equals(ARG(ARITY(denom)-1,denom),x))
)
{ o[i] = intrecip2; ++i;
}
else
{ o[i] = intrecip3; ++i;
}
o[i] = intinversepower; ++i;
o[i] = inttoatan; ++i;
}
if(FRACTION(u) && equals(ARG(0,u),x))
{ o[i] = intsub; ++i;
/* example: x/(x^2+1) */
}
*nops = i;
return; /* Don't try substitution. Without this it
tries u = x+1 on integral((x+1)/(x^3-x+1),x) */
}
denom = ARG(1,u);
num = ARG(0,u);
if(FUNCTOR(denom) == '^' &&
equals(ARG(0,denom),x) &&
!contains(num,FUNCTOR(x)) &&
!contains(ARG(1,denom),FUNCTOR(x))
)
{ o[i] = intinversepower; ++i;
}
if(FUNCTOR(denom) == '+' && ARITY(denom) == 2 &&
(ONE(ARG(0,denom)) || ONE(ARG(1,denom)))
) /* given 1/(1-cos x), multiply num and denom by (1+cos x) */
{ term p = ONE(ARG(0,denom)) ? ARG(1,denom) : ARG(0,denom);
term q;
if(NEGATIVE(p))
{ q = ARG(0,p);
if(FUNCTOR(q) == COS && equals(ARG(0,q),x))
{ o[i] = trigrationalizedenom1; ++i;
}
else if(FUNCTOR(q) == SIN && equals(ARG(0,q),x))
{ o[i] = trigrationalizedenom2; ++i;
}
}
else if(FUNCTOR(p) == COS && equals(ARG(0,p),x))
{ o[i] = trigrationalizedenom4; ++i;
}
else if(FUNCTOR(p) == SIN && equals(ARG(0,p),x))
{ o[i] = trigrationalizedenom5; ++i;
}
}
if(is_sincos(num,x) == -1 && is_sincos(denom,x)==1)
{ /* example: (sin x - cos x)/(sin x + cos x) */
o[i] = trigrationalizedenom3; ++i;
/* In this case it's better to multiply by cos x + sin x
than by cos x - sin x though both will work */
}
if(is_sincos(num,x) == 1 && is_sincos(denom,x) == -1)
{ o[i] = trigrationalizedenom6; ++i;
}
if(is_sincos(denom,x) == -1)
{ o[i] = trigrationalizedenom3; ++i;
}
/* Example: 1/(sin x + cos x). This can be done by
multiplying num and denom by (sin x - cos x), then multiplying out
in the denom, then using apart and a suitable substitution. But
it's better just to use the Weierstrass substitution to start with.
if(is_sincos(denom,x) == 1)
{ o[i] = trigrationalizedenom6; ++i;
}
*/
if(trigpower_quotient(u,x))
{ if(currenttopic == _int_by_substitution)
{ o[i] = choosesubstitution; ++i;
}
trigpowers(t,i,o,nops);
return;
}
break;
case '*' :
if(trigpower(u,x))
/* sin^m x cos^n x or sec^n x tan^m x */
{ if(currenttopic == _int_by_substitution)
{ o[i] = choosesubstitution; ++i;
/* Give it a chance--if it can't work the
ops thrown in by trigpowers will do it,
but they do one-step substitutions,
while under this topic we want multi-step
solutions. */
}
trigpowers(t,i,o,nops);
return;
}
if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == SEC &&
FUNCTOR(ARG(1,u)) == TAN &&
status(intsub) > LEARNING
)
{ o[i] = inttosec2; ++i;
/* If inttosec could work it would have gone under trigpowers */
}
if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == CSC &&
FUNCTOR(ARG(1,u)) == COT &&
status(intsub) > LEARNING
)
{ o[i] = inttocsc2; ++i;
/* If inttocsc could work it would have gone under trigpowers */
}
if(ARITY(t) == 4)
{ o[i] = breakabsint; ++i;
}
if(ARITY(u) == 2 && FUNCTOR(ARG(1,u)) == '^' &&
equals(ARG(1,ARG(1,u)),two) && FUNCTOR(ARG(0,ARG(1,u))) == SIN
)
/* Example, integral(x sin^2 x,x). We have to
integrate this by changinging sin^2 x to (1-cos(x/2)),
but for example
integral(x sin^2 x cos x,x)
is NOT done this way, but by parts. So this can't be
controlled down in the integrand; I had to make sinsqhalf2
work on an integral to handle these examples correctly,
calling set_pathtail to make it look like the sin^2 term
is worked on.
*/
{ o[i] = sinsqhalf2; ++i;
}
break;
case SQRT:
o[i] = intsqrt; ++i;
break;
case SIN:
o[i] = intsin; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intsin2; ++i;
}
break;
case COS:
o[i] = intcos; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intcos2; ++i;
}
break;
case TAN:
o[i] = inttan; ++i;
if(status(intsub) > LEARNING)
{ o[i] = inttan2; ++i;
}
break;
case SEC:
o[i] = intsec; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intsec2; ++i;
}
break;
case CSC:
o[i] = intcsc; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intcsc2; ++i;
}
break;
case COT:
o[i] = intcot; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intcot2; ++i;
}
break;
case LN :
if(currenttopic != _int_by_parts1 &&
currenttopic != _int_by_parts2
)
{ o[i] = intln; ++i;
}
break;
case SINH:
o[i] = intsinh; ++i;
break;
case COSH:
o[i] = intcosh; ++i;
break;
case TANH:
o[i] = inttanh; ++i;
break;
case COTH:
o[i] = intcoth; ++i;
break;
case CSCH:
o[i] = intcsch; ++i;
break;
case SECH:
o[i] = intsech; ++i;
break;
case SG:
o[i] = intsg; ++i;
break;
case ABSFUNCTOR:
o[i] = intabs; ++i;
o[i] = breakabsint; ++i;
break;
/* No formulas for integrating these--
ATAN, ASIN, ACOS can be done by parts */
case ATAN:
case ASIN:
case ACOS:
case BESSELJ:
case BESSELK:
case BESSELI:
case BESSELY:
break; /* FINISH THIS */
case SUM:
o[i] = intsigma; ++i;
break;
case VECTOR:
o[i] = intvector; ++i;
break;
case MATRIX:
o[i] = intmatrix; ++i;
break;
}
if(status(simpleint) >= KNOWN)
{ o[i] = simpleint; ++i;}
/* you have to try intsub even if simpleint has failed, e.g. to
get integral(cos^2(2x),x), because after the substitution some trig
work is required that simpleint doesn't do. */
if(g == '^' && ISINTEGER(ARG(1,u)) && !islinear(ARG(0,u),x,&a,&b))
{ if(linearsqrt(ARG(0,u),x)&& (get_whichpass() || status(intsub) > UNKNOWN ))
/* (1 + sqrt x)^n can be done by substitution */
{ o[i] = intsub; ++i;
}
else
{ o[i] = expandintegrand; ++i;
/* integral(x^3-1)^2,x) dx for example */
}
}
if(possible_trigsub(u,x))
{ o[i] = trigsubsin; ++i;
o[i] = trigsubtan; ++i;
o[i] = trigsubsec; ++i;
/* we don't use the hypertrig substitutions in auto mode */
}
else if(use_trigsubtan(u,x)) /* catch 1/(a^2+x^2)^n */
{ o[i] = trigsubtan; ++i;
}
else if(use_trigsubsec(u,x))
{ o[i] = trigsubsec; ++i;
}
if(status(intsub) >= KNOWN)
{ if((g != '/' && g!= '*') || !intsub_in_preops(u,x))
{ o[i] = intsub; ++i;
}
}
else if(status(intsub) >= LEARNING)
{ if( (g != '/' && g != '*') || !intsub_in_preops(u,x))
{ o[i] = choosesubstitution; ++i;
}
/* trysubstitution and changeintegrationvariable are in pre_ops */
}
o[i] = equatetoproblem; ++i; /* BEFORE integratebyparts, to prevent infinite
regress on e.g. e^x sin x */
if(g == '*' || g == '^' || g == '/')
{ o[i] = expandintegrand; ++i;}
if(g == '*' || g == '/')
{ o[i] = multiplyoutintegrand; ++i;}
/* u = x e^(-x^2) for example gets here */
if(g == '*' && ARITY(u) == 2 && ispolyin(ARG(0,u),x)
&& FUNCTOR(ARG(1,u)) == '^' && equals(ARG(0,ARG(1,u)),eulere)
&& ispolyin(ARG(1,ARG(1,u)),x)
)
{o[i] = intsub; ++i;
}
/* trig (or hypertrig) substitutions are used on integrands
containing a square root of a quadratic. By this time completethesquare1
has done its work, so the quadratic will have no linear term.
Use the Weierstrass substitution on rational functions of trig functions.
The Weierstrass substitution will also apply to more general
integrands, however integration by parts must be tried first,
e.g. sin(x) ln (cos x) becomes a mess if you use Weierstrass,
but works in one step by parts.
On the other hand, some integrands will satisfy trigpoly2 but are
done by parts, for example x/cos(x). But these won't pass stricttrigpoly2.
*/
if(FRACTION(u) && stricttrigpoly2(ARG(0,u),x) && stricttrigpoly2(ARG(1,u),x))
{ o[i] = weierstrass; ++i;
*nops = i;
return;
}
/* Now, we DON'T want to ALWAYS try integration by parts, because in most
cases it just leads to a mess. In case u is a product, the operator
itself rejects cases that look unpromising. Sometimes it works
on a quotient, e.g. (ln x)/x; sometimes it works on a power,
e.g (ln x)^2. Otherwise the only hope is if integral(x du/dx, x)
can be done. That does sometimes happen, e.g. with u = ln x
or u = arctan x, but some cases should be rejected as hopeless.
Indeed, unless du/dx is a rational function or a function of x^2 it
seems hopeless. These rather complicated rejections are handled
by the operator itself; here we only reject the case of an
absolute value.
*/
path = get_path();
if(g != ABSFUNCTOR && status(integratebyparts) >= LEARNING &&
path[0] != '=' /* after equatetoproblem, don't keep on integrating by parts */
)
{ o[i] = integratebyparts; ++i;
}
if(ftrig(u,x) && !contains(u,ABSFUNCTOR) && !contains(u,SG))
{ o[i] = weierstrass; ++i;
}
h = contains_trigsquare(u,x);
if(h) /* example, integral (x cos^2 x,x) gets here */
{ switch(h)
{ case CSC:
o[i] = intcscsq; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intcscsq2; ++i;
}
o[i] = intsubcot; ++i;
break;
case COT:
o[i] = intcotsq; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intcotsq2; ++i;
}
break;
case SEC:
o[i] = intsecsq; ++i;
if(status(intsub) > LEARNING)
{ o[i] = intsecsq2; ++i;
}
o[i] = intsubtan; ++i;
break;
case TAN:
o[i] = inttansq; ++i;
if(status(intsub) > LEARNING)
{ o[i] = inttansq2; ++i;
}
break;
case SIN:
o[i] = intsinsq; ++i;
o[i] = intsubcos; ++i;
break;
case COS:
o[i] = intcossq; ++i;
o[i] = intsubsin; ++i;
break;
}
}
*nops = i;
}
/*____________________________________________________________*/
int trigpower(term u, term x)
/* return 1 if u is a product sin^n x cos^m x (n,m = 1 allowed,
but not n,m = 0), or sec^n x tan^m x; or with the other order
of the factors. */
{ term a,b;
unsigned f,h;
if(FUNCTOR(u) != '*' || ARITY(u) != 2)
return 0;
a = ARG(0,u);
b = ARG(1,u);
f = FUNCTOR(a);
if( f == '^' && INTEGERP(ARG(1,a)))
{ a = ARG(0,a);
f = FUNCTOR(a);
}
h = FUNCTOR(b);
if( h == '^' && INTEGERP(ARG(1,b)))
{ b = ARG(0,b);
h = FUNCTOR(b);
}
if(ARITY(a) != 1 || ARITY(b) != 1)
return 0;
if(f != SIN && f != COS && f != TAN && f != SEC)
return 0;
if(h != SIN && h != COS && h != TAN && h != SEC)
return 0;
if(!equals(ARG(0,a),x) || !equals(ARG(0,b),x))
return 0;
if( (h == SIN && f == COS) ||
(h == COS && f == SIN) ||
(h == TAN && f == SEC) ||
(h == SEC && f == TAN)
)
return 1;
return 0;
}
/*____________________________________________________________*/
static int trigpower_quotient(term u, term x)
/* return 1 if u is a quotient sin^n x/cos^m x (n,m = 1 allowed,
and n = 0, but not m = 0), or cos^m x/ sin^n x (in which case m=0
is allowed but not n = 0). Here n and m must be specific
positive integers. Similarly for tan and sec, cot and csc.
Return 0 if u is not such a quotient.
*/
{ term a,b;
unsigned f,h;
if(FUNCTOR(u) != '/')
return 0;
a = ARG(0,u);
b = ARG(1,u);
f = FUNCTOR(a);
h = FUNCTOR(b);
if(ONE(a))
{ if(h == '^' && INTEGERP(ARG(1,b)))
{ b = ARG(0,b);
h = FUNCTOR(b);
}
if(TRIGFUNCTOR(h) && equals(ARG(0,b),x))
return 1;
return 0;
}
if( f == '^' && INTEGERP(ARG(1,a)))
{ a = ARG(0,a);
f = FUNCTOR(a);
}
h = FUNCTOR(b);
if( h == '^' && INTEGERP(ARG(1,b)))
{ b = ARG(0,b);
h = FUNCTOR(b);
}
if(ARITY(a) != 1 || ARITY(b) != 1)
return 0;
if(!((f == SIN && h == COS) || (f == COS && h == SIN)) &&
!((f == SEC && h == TAN) || (f == TAN && h == SEC)) &&
!((f == CSC && h == COT) || (f == COT && h == CSC))
)
return 0;
if(!equals(ARG(0,a),x) || !equals(ARG(0,b),x))
return 0;
return 1;
}
/*__________________________________________________________________*/
/* Strategy for trig power integrands sin^n cos^m (including n or m zero)
1. m odd: write cos^2 in terms of sin and substitute u = sin x.
2. n odd: write sin^2 in terms of cos and substitute u = cos x.
3. Both n and m even: write sin^2 and cos^2 in terms of cos 2x.
This reduces to an integral of the same form but with lower
power.
Strategy for trig power integrands tan^n sec^m (including n or m zero)
4. m even > 0: save out one sec^2 and write the rest in terms of tan^2.
5. m odd > 0 and n > 0:
save out sec tan and write the rest in terms of sec^2.
6. m == 0, n even: write tan^2 = sec^2-1, so we get (sec^2-1)^k;
expand by the binomial theorem getting integrals involving
only sec, see below.
7. m == 0, n odd: write tan^2 = sec^2-1, so we get (sec^2-1)^k tan,
reducing to case 4 except for the last term integral(tan x, x),
which is ln(abs(sec x)).
8. m == 1, n==0: integral(sec x,x) = ln abs(sec x + tan x)
9. m odd > 1, n==0: Use a reduction formula (obtained by
integrating by parts) to reduce to a sum of integrals,
one with m decreased and n zero, one with m the same but n > 0,
which can be done by case 5. You have to keep iterating
this reduction formula; you don't get a closed formula for
the answer.
Strategy for trig power integrands sin^n / cos^m or cos^m/sin^m (n !=m)
10. if n odd, set u = cos x;
11. if m odd, set u = sin x
12. If both even, proceed as in case 3.
In autoint, the cases where m or n is zero are handled separately,
and the cases m and n both nonzero come to the following function for
a decision what to do.
*/
/*____________________________________________________________*/
void trigpowers(term t, int i, actualop *o, int *nops)
/* finish autoint when the integrand is a product of
trigonometric powers, sin^m cos^n or sec^m tan^n; or a
quotient of powers sin^m /cos^n or cos^m / sin^n, or a reciprocal
of a trig function or a reciprocal of a power of a trig function.
Outputs are the same as for autoint. */
/* Powers of a single trig function are dealt with in pre_ops */
{ term u = ARG(0,t); /* the integrand */
term p,q,n,m; /* u = p^n q^m or p^n/q^n */
term swap;
int swapflag = 0;
term x = ARG(1,t); /* the variable of integration */
unsigned f,h,ff;
assert(FUNCTOR(u) == '*' || FUNCTOR(u) == '/');
assert(ARITY(u) == 2);
p = ARG(0,u); /* temporarily */
q = ARG(1,u);
f = FUNCTOR(p);
h = FUNCTOR(q);
if(FRACTION(u) && ONE(p) && TRIGFUNCTOR(h) && equals(ARG(0,q),x))
{ /* reciprocals of trig functions */
switch(h)
{ case SIN:
o[i] = intsubcos; ++i;
break;
case COS:
o[i] = intsubsin; ++i;
break;
case TAN:
o[i] = intsubsec; ++i;
/* dx/tan x = tan x sec x dx/ tan^2 x = du/(u^2-1) with u = sec x */
break;
case SEC:
o[i] = secrule; ++i;
break;
case CSC:
o[i] = cscrule; ++i;
break;
case COT:
o[i] = cottotan; ++i;
break;
}
}
if(FRACTION(u) && ONE(p) && h == '^' &&
TRIGFUNCTOR(FUNCTOR(ARG(0,q)))
)
{ switch(FUNCTOR(ARG(0,q)))
{ case SIN:
o[i] = cscrule2; ++i;
break;
case COS:
o[i] = secrule2; ++i;
break;
case TAN:
o[i] = tanrecip; ++i;
break;
case COT:
o[i] = cotrecip; ++i;
break;
case SEC:
o[i] = secrecip; ++i;
break;
case CSC:
o[i] = cscrecip; ++i;
break;
}
}
if(f == '^')
{ f = FUNCTOR(ARG(0,p));
n = ARG(1,p);
p = ARG(0,p);
}
else
n = one;
if( h == '^')
{ h = FUNCTOR(ARG(0,q));
m = ARG(1,q);
q = ARG(0,q);
}
else
m = one;
/* Now u = p^n q^m as advertised, or p^n/q^m */
if(!equals(ARG(0,p),x) || !equals(ARG(0,q),x))
{ *nops = i;
return;
}
if(f == COS && h == SIN)
{ swap = p; p=q; q = swap; /* interchange p and q */
swap = m; m=n; n = swap; /* interchange m and n */
ff = f; f = h; h = ff; /* interchange f and h */
swapflag = 1;
}
if(f == TAN && h == SEC)
{ swap = p; p=q; q = swap; /* interchange p and q */
swap = m; m=n; n = swap; /* interchange m and n */
ff = f; f = h; h = ff; /* interchange f and h */
swapflag = 1;
}
if(f == COT && h == CSC)
{ swap = p; p=q; q = swap; /* interchange p and q */
swap = m; m=n; n = swap; /* interchange m and n */
ff = f; f = h; h = ff; /* interchange f and h */
swapflag = 1;
}
if(f == SIN && h == COS && ISEVEN(n) && ISEVEN(m))
{ o[i] = intsinsq; ++i; /* use half-angle identities */
o[i] = intcossq; ++i;
*nops = i;
return;
}
if(f == SIN && h == COS && ISODD(m) && ISODD(n))
/* get integral( sin x cos^m, x) and integral( sin^n x cos x, x)
done with the simplest substitution */
{ short order;
tcompare(m,n,&order);
if(ONE(n) || ONE(m))
{ o[i] = intsub; ++i; /* just use u = sin(x) or u = cos(x),
no need to use a trig square identity first */
}
else if(order == 1) /* n < m */
{ o[i] = intsubcos; ++i;
}
else
{ o[i] = intsubsin; ++i;
}
*nops = i;
return;
}
if(f == SIN && h == COS && ISODD(m))
{ o[i] = intsubsin; ++i;
*nops = i;
return;
}
if(f == SIN && h == COS && ISODD(n))
{ o[i] = intsubcos; ++i;
*nops = i;
return;
}
assert((f==SEC && h==TAN) || (f==CSC && h==COT));
if(iseven(n)) /* even power of secant */
{ o[i] = f==SEC ? intsubtan : intsubcot; ++i;
*nops = i;
return;
}
if(ONE(m)) /* sec^n x tan x */
{ if(ONE(n))
{ o[i] = f == SEC ? inttosec : inttocsc; ++i;
}
o[i] = f == SEC ? intsubsec : intsubcsc; ++i;
*nops = i;
return;
}
if(isodd(m)) /* sec^n x tan^odd x */
{ o[i] = f == SEC ? tantosecinint : cottocscinint; ++i;
set_expandflag(0x100); /* expand the powers of 1-sec^2 */
set_factorflag(0); /* and don't loop */
*nops = i;
return;
}
if(isodd(n)) /* odd power of sec, even power of tan */
{ if(FUNCTOR(u) == '/')
/* write everything in terms of sin and cos; it will
suffice to rewrite the denominator, then the usual
trig code will rewrite the num; but since only
tan and sec (or cot and csc) occur, we have to give
it a kick before anything will happen */
{ if(swapflag)
{ o[i] = f == SEC ? secrecip : cscrecip; ++i;
}
else
{ o[i] = f == SEC ? tanrecip2 : cotrecip2; ++i;
}
}
else
{ o[i] = f == SEC ? tantosecinint : cottocscinint; ++i;
}
*nops = i;
return;
}
assert(0); /* all cases in which it's called should be covered */
}
/*___________________________________________________*/
static int complicated(term t)
/* return a measure of how complicated t is. This counts the number of
sums occurring as factors of t, counting powers of sums according to the
power, but powers 5 or more all count 5. */
{ int count;
unsigned n,i;
term u;
long power;
unsigned f = FUNCTOR(t);
if(f == '^' && FUNCTOR(ARG(0,t)) == '+')
{ if(ISINTEGER(ARG(1,t)))
{ power = INTDATA(ARG(1,t));
if(power >= 5)
return 5;
return (int) power;
}
return 0; /* can't actually get here */
}
if(f == '*')
{ n = ARITY(t);
count = 0;
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == '+')
count += complicated(u);
if(FUNCTOR(u) == '+')
++count;
}
return count;
}
if(f == '+')
return ARITY(t)-1;
return 0;
}
/*___________________________________________________*/
/* x isn't used; but maybe it will be in the future if this
needs to be made more sophisticated */
int use_leadingterms(term u, term x)
/* u is a rational function of x. To calculate a limit of u
at infinity or minusinfinity, should we use
the operator limleadingterms? or should we try divnumdenom and limquo?
Return 1 if we should use limleadingterms, e.g. if it's KNOWN or
if it looks like divnumdenom might lead to out-of-memory errors
We DO use it on very complicated rational functions no matter what
its status is.
*/
{ if(status(limleadingterms) >= KNOWN)
return 1;
if(!FRACTION(u))
return 0; /* assert(0) */
if(complicated(ARG(0,u)) + complicated(ARG(1,u)) >= 5)
return 1;
return 0;
}
/*__________________________________________________________*/
int use_logdif(term u, term x)
/* u is a product or quotient; should du/dx be done by logarithmic
differentiation? Return 1 if yes, 0 if no. */
{ unsigned f = FUNCTOR(u);
term v;
int count;
unsigned n,i;
assert(f == '/' || f == '*');
if(f == '/')
{ if(!depends(ARG(0,u),x))
return 0;
if(!depends(ARG(1,u),x))
return 0;
if(FUNCTOR(ARG(0,u)) == '*' && use_logdif(ARG(0,u),x))
return 1;
if(FUNCTOR(ARG(1,u)) == '*' && use_logdif(ARG(1,u),x))
return 1;
/* example that gets here: (x-1)^2 (x-2)^3/(x-3)^4 */
return 0;
}
/* Now f == '*' */
count = 0;
n = ARITY(u);
if(n ==2 && get_currenttopic() != _logarithmic_differentiation)
return 0; /* Example: on x^e e^-x don't use it */
for(i=0;i<n;i++)
{ v = ARG(i,u);
if(depends(v,x) && (contains(v,'^') || contains(v,SQRT) || contains(v,ROOT)))
++count;
}
if(count >= 2)
return 1;
return 0;
}
/*______________________________________________________________________*/
static int binary_quadratic(term u, term x, term *a, term *b)
/* return 1 if u has the form a \pm bx^2 (where b is allowed to be
negative or a fraction, and u = \pm dx^2/c counts with b = d/c).
There can be more than one term 'a' which doesn't contain x.
Return 0 if u does not have this form.
If *a and *b are not ILLEGAL at input, then ONLY those values
of a and b are allowed, not different ones. If *a and *b are
ILLEGAL at input, any values are allowed:
instantiate *a and *b to the coefficients in a + bx^2; but
*a cannot be zero (that is, there must actually be a term in x).
*/
{ int count = 0;
int i,flag;
term v,s,c;
if(FUNCTOR(u) != '+')
return 0;
for(i=0;i<ARITY(u);i++)
{ if(contains(ARG(i,u),FUNCTOR(x)))
{ ++count;
flag = i;
}
}
if(count != 1)
return 0;
/* Now there's exactly one nonconstant term */
if(FUNCTOR(*a) != ILLEGAL && !equals(ARG(i ? 0 : 1,u),*a))
return 0; /* fail, coefficients don't match */
else
*a = ARG( flag ? 0 : 1, u); /* instantiate *a */
v = ARG(flag,u);
if(NEGATIVE(v))
v = ARG(0,v);
twoparts(v,x,&c,&s);
if(FUNCTOR(s) != '^' || !equals(ARG(1,s),two) || !equals(ARG(0,s),x))
return 0;
if(FUNCTOR(*b) != ILLEGAL && !equals(c,*b))
return 0; /* fail, coefficients don't match */
*b = c; /* instantiate *b */
return 1;
}
/*______________________________________________________________________*/
static int pt_aux(term t, term x, term *a, term *b)
/* return 2 or more if t contains a subterm or subterms
sqrt( \pm a \pm x^2), or ( \pm a \pm x^2)^( \pm n/2), or with bx^2/c instead of x^2,
and is otherwise is a rational function in x. The return value
is 1 + the number of such subterms. If there are several such
subterms they must be identical, i.e. the same coefficients.
Return 1 if it is a rational function.
Return 0 otherwise.
*/
{ unsigned f = FUNCTOR(t);
unsigned i,n = ARITY(t);
int err,ans;
term u;
if(f == '-')
return pt_aux(ARG(0,t),x,a,b);
if(f == SQRT ||
(f == '^' && FRACTION(ARG(1,t)) && equals(ARG(1,ARG(1,t)),two))
)
{ u = ARG(0,t);
if(binary_quadratic(u,x,a,b))
return 2;
return 0;
}
if(ATOMIC(t))
return 1;
if(f == '^' && isinteger(ARG(1,t)))
return pt_aux(ARG(0,t),x,a,b);
if(f != '*' && f != '+' && f != '/')
return contains(t,FUNCTOR(x)) ? 0 : 1;
ans = 1;
for(i=0; i<n; i++)
{ err = pt_aux(ARG(i,t),x,a,b);
if(err == 0)
return 0;
ans += (err-1);
}
return ans;
}
/*______________________________________________________________________*/
int use_trigsubtan(term t,term x)
/* t is an integrand, x the variable of integration.
Return 1 in certain special cases where x = tan \theta should
be used, namely when t = 1/(r+cx^2)^n don't forget negative
exponents, and half-integer exponents.
*/
{ term u,v,base,power,c,s;
if(FUNCTOR(t) == '^' && NEGATIVE(ARG(1,t)))
{ base = ARG(0,t);
power = ARG(0,ARG(1,t));
}
else if(FRACTION(t) && ONE(ARG(0,t)) && FUNCTOR(ARG(1,t)) == '^')
{ base = ARG(0,ARG(1,t));
power = ARG(1,ARG(1,t));
}
else
return 0;
if(FUNCTOR(base) != '+' || ARITY(base) != 2)
return 0;
if(!isinteger(power) && !(RATIONALP(power) && equals(ARG(1,power),two)))
return 0;
if(contains(ARG(0,base), FUNCTOR(x)))
{ v = ARG(1,base);
u = ARG(0,base);
}
else
{ u = ARG(1,base);
v = ARG(0,base);
}
if(contains(v,FUNCTOR(x)))
return 0;
if(NEGATIVE(v))
return 0;
twoparts(u,x,&c,&s);
if(FUNCTOR(s) != '^' || !equals(ARG(1,s),two))
return 0;
return 1;
}
/*______________________________________________________________________*/
int use_trigsubsec(term t,term x)
/* t is an integrand, x the variable of integration.
Return 1 in certain special cases where x = sec \theta should
be used, namely when t = 1/(cx^2-r)^n ; don't forget negative
exponents, and half-integer exponents.
*/
{ term u,v,base,power,c,s;
if(FUNCTOR(t) == '^' && NEGATIVE(ARG(1,t)))
{ base = ARG(0,t);
power = ARG(0,ARG(1,t));
}
else if(FRACTION(t) && ONE(ARG(0,t)) && FUNCTOR(ARG(1,t)) == '^')
{ base = ARG(0,ARG(1,t));
power = ARG(1,ARG(1,t));
}
else
return 0;
if(FUNCTOR(base) != '+' || ARITY(base) != 2)
return 0;
if(!isinteger(power) && !(RATIONALP(power) && equals(ARG(1,power),two)))
return 0;
if(contains(ARG(0,base), FUNCTOR(x)))
{ v = ARG(1,base);
u = ARG(0,base);
}
else
{ u = ARG(1,base);
v = ARG(0,base);
}
if(contains(v,FUNCTOR(x)))
return 0;
if(!NEGATIVE(v))
return 0;
twoparts(u,x,&c,&s);
if(FUNCTOR(s) != '^' || !equals(ARG(1,s),two))
return 0;
return 1;
}
/*______________________________________________________________________*/
int possible_trigsub(term t, term x)
/* return 1 or more if t is a rational function except for some subterms
sqrt( \pm a \pm x^2), or ( \pm a \pm x^2)^( \pm n/2), or the above with bx^2/c instead of x^2.
Such a term must actually occur if 1 is returned. The return value is
the number of such subterms.
However, reject a few cases which are better done by
ordinary substitution, namely x f(x) and x/f(x) where
f is a square root or power of a term a+bx^2.
*/
{ unsigned f = FUNCTOR(t);
term u,v;
int ans;
term a,b;
SETFUNCTOR(a,ILLEGAL,0);
SETFUNCTOR(b,ILLEGAL,0);
if( (f == '*' || f == '/') &&
ARITY(t) == 2 &&
equals(ARG(0,t),x)
)
{ u = ARG(1,t);
if(FUNCTOR(u) == SQRT ||
(FUNCTOR(u) == '^' && FRACTION(ARG(1,u)) && equals(ARG(1,ARG(1,u)),two))
)
{ v = ARG(0,u);
if(binary_quadratic(v,x,&a,&b))
return 0; /* intsub will work better */
}
}
ans = pt_aux(t,x,&a,&b);
if(!ans)
return 0;
return ans-1;
}
/*______________________________________________________________________*/
int ftrig(term t, term x)
/* return nonzero if t is a function of trig functions of x
(If it is, the Weierstrass substitution can be used.)
Return value 1 means t is constant, 2 means it actually
contains a trig function of x, 0 means it contains x in
some other context.
*/
{ unsigned i,n;
int ans,k;
unsigned f = FUNCTOR(t);
if(ISATOM(t))
return equals(t,x) ? 0 : 1;
if(OBJECT(t))
return 1;
n = ARITY(t);
if(n == 1 && TRIGFUNCTOR(f) && equals(ARG(0,t),x))
return 2;
ans = ftrig(ARG(0,t),x);
if(ans == 0)
return 0;
for(i=1;i<n;i++)
{ k = ftrig(ARG(i,t),x);
if(k==0)
return 0;
if(k==2)
ans = 2;
}
return ans;
}
/*______________________________________________________________*/
int is_sincos(term t, term x)
/* return 1 if t = sin x + cos x;
return -1 if t = sin x - cos x or cos x - sin x;
return 0 otherwise.
*/
{ term p,q;
unsigned f,g;
int sign = 1;
if(FUNCTOR(t) != '+')
return 0;
if(ARITY(t) != 2)
return 0;
p = ARG(0,t);
q = ARG(1,t);
if(ATOMIC(p) || ATOMIC(q))
return 0;
if(NEGATIVE(p) && NEGATIVE(q))
return 0;
if(NEGATIVE(p))
{ sign = -1;
p = ARG(0,p);
}
if(NEGATIVE(q))
{ sign = -1;
q = ARG(0,q);
}
if(ARITY(p) != 1 || ARITY(q) != 1)
return 0;
if(!equals(ARG(0,p),x) || !equals(ARG(0,q),x))
return 0;
f = FUNCTOR(p);
g = FUNCTOR(q);
if(f == SIN && g == COS)
return sign;
if(f == COS && g == SIN)
return sign;
return 0;
}
/*_________________________________________________________________________*/
static int evil_roots(term t,term x)
/* return 1 if t is a root or sqrt containing x, or is a product or sum containing
a factor or summand with evil roots. */
{ unsigned short n,f;
int i;
if(NEGATIVE(t))
t = ARG(0,t);
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(f == ROOT || f == SQRT)
return contains(t,FUNCTOR(x));
if(f != '*')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(evil_roots(ARG(i,t),x))
return 1;
}
return 0;
}
/*_________________________________________________________________________*/
static int contains_log2(term t)
/* return 1 if t is a product containing a log or power of a log term as a factor */
{ unsigned short i,n,f;
if(FUNCTOR(t) != '*')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ f = FUNCTOR(ARG(i,t));
if(f == '^')
f = FUNCTOR(ARG(0,ARG(i,t)));
if(f == LN || f == LOG || f == LOGB)
return 1;
}
return 0;
}
/*_________________________________________________________________________*/
int stop_lhopital(term t)
/* t is a limit term. Return 1 if L'Hospital's rule should NOT be used
on t. Return 0 if it should be tried. */
{ term x,u,num,denom;
if(get_problemtype() == DIFFERENTIATE_FROM_DEFN)
return 1; /* NEVER use lhopital under DIFFERENTIATE_FROM_DEFN */
if(FUNCTOR(t) != LIMIT)
return 1; /* assert(0) */
u = LIMITAND(t);
x = ARG(0,ARG(0,t));
/* t is lim(x->a,u) or lim(x->a,dir,u) */
if(!FRACTION(u))
return 1; /* this includes x - sqrt(x^2+x) */
num = ARG(0,u);
denom = ARG(1,u);
if(lhopital_certain(num,denom,x))
return 0; // it will work!
/* check for the case (a sqrt b)/(c sqrt d) where both b and d
contain x. This just reduces to another limit of the same form,
equally indeterminate. The same with roots in place of sqrts. */
if(evil_roots(denom,x) &&
(evil_roots(num,x) || !contains(num,LN))
)
return 1;
/* This used to read (evil_roots(num,x) && evil_roots(denom,x)).
But consider for example x+sqrt(x^2+x) in the denom.
the denom; this goes nowhere. Perhaps evil_roots(denom) would be
best, but the code above allows for the possibility of differentiating
away a log in the numerator.
*/
if(contains_fractional_exponents(t))
return 1;
if(contains_sqrt(t))
return 1;
/* |x|^c/x */
if(equals(denom,x) &&
FUNCTOR(num) == '^' &&
FUNCTOR(ARG(0,num)) == ABSFUNCTOR &&
equals(ARG(0,ARG(0,num)),x)
)
return 1;
/* (e^(u ln v))/x */
/* This becomes e^(u ln v)/v times (v'u + u'v ln v) and if the
latter is nonzero it gets pulled out and a loop is created. */
if(equals(denom,x) &&
FUNCTOR(num) == '^' &&
contains_at_toplevel(ARG(1,num),LN)
)
return 1;
/* Examples: 3^(x-1)/5^x or x 3^(x-1)/5^x lead to an infinite regress */
/* But (e^x + x)/e^x works fine by L'Hopital, so don't overdo it. */
if(
(FUNCTOR(num) == '*' || FUNCTOR(num) == '^') &&
(FUNCTOR(denom) == '*' || FUNCTOR(denom) == '^') &&
contains_in_exponent(num,FUNCTOR(x)) &&
contains_in_exponent(denom,FUNCTOR(x))
)
return 1;
/* Next, stop lhopital on things like n^2 ln n / ((n+1)^2 ln(n+1)) */
if(FUNCTOR(num) == '*' && contains_log2(num))
return 1;
if(FUNCTOR(denom) == '*' && contains_log2(denom))
return 1;
return 0;
}
/*_______________________________________________________________*/
static int pimultiple(term a)
/* return 1 if a is an integer multiple of pi */
{ term p;
polyval(make_fraction(a,pi_term),&p);
return isinteger(p);
}
/*________________________________________________________________*/
static int odd_mul_piover2(term a)
/* return 1 if a is an odd multiple of pi/2 */
{ term p;
polyval(make_fraction(product(two,a),pi_term),&p);
return isinteger(p) && isodd(p);
}
/*________________________________________________________________*/
static int polysqrt(term t, term x)
/* return 1 if t is a sum of polynomials or sqrts or roots or fractional powers of polynomials in x */
{ unsigned short n;
int i;
term u;
if(FUNCTOR(t) != '+')
return 0;
n = ARITY(t);
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(NEGATIVE(u))
u = ARG(0,u);
if(FUNCTOR(u) == SQRT)
u = ARG(0,u);
else if(FUNCTOR(u) == ROOT && INTEGERP(ARG(0,u)))
u = ARG(1,u);
else if(FUNCTOR(u) == '^' && RATIONALP(ARG(1,u)))
u = ARG(0,u);
else if(FUNCTOR(u) == '^' && NEGATIVE(ARG(1,u)) && RATIONALP(ARG(0,ARG(1,u))))
u = ARG(0,u);
if(!ispolyin(u,x))
return 0;
}
return 1;
}
/*____________________________________________________________________________________*/
static int contains_trigsquare(term u, term x)
/* return trig functor f if u contains a subterm f^{2n}(...),
and the integral of u should be done by making a double-angle
substitution for this term.
If such a term is the denominator of u and no such term
occurs in the numerator, return 0; Example, 1/sin^2 x, which
should become csc^2 x in an integral.
Also return 0 if u contains no trig squares.
*/
{ int i;
unsigned short f,n;
term v;
if(ATOMIC(u))
return 0;
if(FRACTION(u))
{ f = contains_trigsquare(ARG(0,u),x);
if(f)
return f;
v = ARG(1,u);
if(FUNCTOR(v) == '^' && ISINTEGER(ARG(1,v)) && EVEN(ARG(1,v)))
return 0;
}
n = ARITY(u);
if(FUNCTOR(u) == '^' && ISINTEGER(ARG(1,u)) && EVEN(ARG(1,u)))
{ f = FUNCTOR(ARG(0,u));
if(TRIGFUNCTOR(f) && contains(ARG(0,u),FUNCTOR(x)))
return f;
}
for(i=0;i<n;i++)
{ f = contains_trigsquare(ARG(i,u),x);
if(f)
return f;
}
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists