Sindbad~EG File Manager
/*
M. Beeson; automode strategy for sums and products in Mathpert
1.7.91 original date
3.19.99 modified
1.5.00 added code to block addfractions on complex numbers
1.6.00 added ...|| topic == _trigproduct lines
1.6.00 removed 'finishedfactoring'
1.10.00 added differenceofnth2
1.15.00 modified conditions for rectangulartopolar in autoproduct
1.15.00 removed conditions for using contentfactor in a complex exponent
2.3.00 modified conditions for complexform
2.3.00 added complexexpression(t) as a precondition for using common denominators
2.3.00 added rectangulartopolar in autoproduct
10.3.02 changed the line bearing this date, because VS.NET caught an error.
1.27.06 added abstimessg
1.29.06 added !difflag at line 294
*/
#define AUTOMODE_DLL
#include <assert.h>
#include <string.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "document.h"
#include "tdefn.h"
#include "checkarg.h" /* for operator typedef */
#include "ops.h" /* for prototypes of operators */
#include "operator.h"
#include "trig.h"
#include "calc.h"
#include "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 "eqn.h"
#include "pvalaux.h"
#include "symbols.h"
#include "cflags.h" /* display_on() etc. */
#include "factor.h" /* numerical_poly */
#include "cancel.h"
#include "autosum.h"
#include "trigpoly.h" /* algpoly */
#include "binders.h" /* get_limit_info */
#include "autosimp.h" /* get_pathlength, get_path */
#include "automode.h"
#include "autoeqn.h" /* noxious */
#include "preops.h" /* get_trigpolyflag */
#include "deval.h" /* seminumerical */
static int not_quadratic(term t);
static int essentially_linear(term);
static int cfc(void);
static int fc(void);
static int no_factor_functor(unsigned short f);
static int quadratic_in(term t, term x);
static int tanflag(term);
static int comdenom_conditions(int problemtype, term t);
static int stop_factorundersqrt(term t);
static int stop_factorunderroot(term t);
static int all_trig(term t);
static int trig_in_denom(term t);
static int stop_complexnegexp(term t);
static int reversesinsq_conditions(term t);
static int different_trig_args(term t);
/*______________________________________________________________*/
static int hintflag;
MEXPORT_AUTOMODE void set_hintflag(int n)
{ hintflag = n;
}
/*______________________________________________________________*/
#define HIEVENPOWER(u) (FUNCTOR(u) == '^' && INTEGERP(ARG(1,u)) && ISEVEN(ARG(1,u)) && INTDATA(ARG(1,u)) > 2 && TRIGFUNCTOR(FUNCTOR(ARG(0,u))))
/*_______________________________________________________________*/
void autosum(term t,actualop *o, int *nops)
/* post_ops for the case of a sum t */
/* the trig (and arctrig) functors are numbered 1-12; we set the k-th bit of
trigflag (or k-1st, if you number from 0)
when we meet the k-th trig functor as a summand
OR A FACTOR OF A SUMMAND. Then later we use the following macro to
determine which functors were encountered: */
#define INDICATES(trigflag,f) ((trigflag) & (1 << TRIGINDEX(f)))
/* Similarly, trigsqflag keeps track of the occurrences of squares of
the trig functors. */
/* The 13-th bit (bit 12, since they start at 0)
of trigflag is set if we encounter a non-atomic argument
of a trig function. */
{ int i=0,j,k;
unsigned short n = ARITY(t);
unsigned short f = FUNCTOR(t);
int atantan2flag =0;
int trigexpandflag= get_trigexpandflag();
int comdenomflag = get_comdenomflag();
int logcollectflag = get_logcollectflag();
int factorflag = get_factorflag();
int problemtype = get_problemtype();
int topic = get_currenttopic();
int intflag = get_intflag();
int indexflag = get_indexsumflag();
int seriesflag = get_seriesflag();
int pathlength = get_pathlength();
int expandflag = get_expandflag();
int contentfactorflag = 0; /* don't try contentfactor more than once */
unsigned short const *path = get_path();
unsigned short m,g,h;
int trigpolyflag;
term summand,temp;
int stopfactor = 0;
int fractionflag,varflag,constflag,trigflag,localexpandflag,minusflag,
logflag,infinityflag,trigsqflag,localintflag,iflag,cofiflag,sincosflag;
infinityflag=fractionflag=varflag=constflag=localintflag=minusflag=
trigflag=localexpandflag=logflag=trigsqflag=iflag=cofiflag=sincosflag=0;
assert(f=='+');
if(problemtype == LINEAR_EQUATIONS &&
pathlength <= 6 &&
path[0] == AND &&
path[2] == '=' && path[3] == 1 &&
LINEUP(history(get_currentline()))
)
/* Once variables are lined up, don't do anything to the sums
on the left of = in systems of linear equations. For
example, don't change -x + y to y-x */
{ *nops = i;
return;
}
if(get_whichpass() == 5 && seminumerical(t) && contains_double(t))
{ o[i] = devalop; ++i;
*nops = i;
return;
}
for(j=0;j<n;j++)
{ temp = ARG(j,t);
g = FUNCTOR(temp);
if(g == CONSTANTOFINTEGRATION)
{ atantan2flag = atantan2flag == 1 ? 3 : 2;
++cofiflag;
}
if(ATOMIC(temp))
{ if(equals(infinity,temp))
infinityflag = 1;
if(get_complex() && FUNCTOR(temp) == 'i')
++iflag; /* count occurrences of complexi */
continue;
}
if(g=='-')
{ summand = ARG(0,temp);
++minusflag;
g = FUNCTOR(summand);
if(FUNCTOR(temp) == CONSTANTOFINTEGRATION)
atantan2flag = atantan2flag == 1 ? 3 : 2;
if(ATOMIC(summand))
{ if(equals(infinity,summand))
infinityflag = 1;
if(get_complex() && FUNCTOR(summand) == 'i')
++iflag;
continue;
}
}
else
summand = temp;
if(get_problemtype() == IMPLICIT_DIFF)
{ if(contains(summand,DIFF))
++varflag;
else
++constflag;
}
else if(SOLVETYPE(problemtype))
{ if(contains(summand,FUNCTOR(get_eigenvariable())))
++varflag;
else
++constflag;
}
else
{ if(constant(summand))
++constflag;
else
++varflag;
}
if(g==INTEGRAL)
++localintflag;
if(g == '*' && contains_monomially(summand,INTEGRAL))
++localintflag;
if(g == '*')
atantan2flag = tanflag(summand) | atantan2flag;
if(g == '/')
fractionflag = 1;
if(trigfunctor(g))
{ trigflag |= (1<<TRIGINDEX(g)); /* see comments above function */
if(!ATOMIC(ARG(0,summand)) && TRIGFUNCTOR(g))
/* don't set this bit for arctrig functions of non-atomic args */
trigflag |= 0x1000; /* set bit 12 */
}
if(g == '^' && trigfunctor(FUNCTOR(ARG(0,summand))))
{ trigflag |= (1<<TRIGINDEX(FUNCTOR(ARG(0,summand))));
if(equals(ARG(1,summand),two))
trigsqflag |= (1<<TRIGINDEX(FUNCTOR(ARG(0,summand))));
if(!ATOMIC(ARG(0,ARG(0,summand))) &&
TRIGFUNCTOR(FUNCTOR(ARG(0,summand)))
/* don't set this bit for arctrig functions of non-atomic args */
)
trigflag |= 0x1000;
}
/* logflag uses 2 bits each to 'count'
ln, log, logb crudely, setting the second
bit when a second occurrence is encountered.
We collect both constant and nonconstant logs,
for example to solve log 2 + log x = 2 => log 2x = 1.
On the other hand consider 3^(log x) = 3x =>
10^(log3 log x) = 3x => log 3 log x = log(3x) =>
log 3 log x - log(3x) = 0 which can't be finished
without expanding log(3x).
*/
if(g == LN)
logflag |= (logflag & 1 ? 2 : 1);
if(g == LOG)
logflag |= (logflag & 4 ? 8 : 4);
if(g == LOGB)
logflag |= (logflag & 16 ? 32 : 16);
if(g == ATAN)
{ if(FUNCTOR(ARG(0,summand)) == TAN)
atantan2flag = atantan2flag == 2 ? 3 : 1;
/* we set bit 0 of this flag when atan(tan(x)) is
encountered and bit 1 when an indefinite integral
or constant of integration is encountered */
}
if(g == INTEGRAL && ARITY(summand) == 2)
{ atantan2flag = atantan2flag == 1 ? 3 : 2;
}
if(g == '*' && ARITY(summand) == 2)
{ term a = ARG(0,summand);
term b = ARG(1,summand);
unsigned short fa = FUNCTOR(a);
unsigned short fb = FUNCTOR(b);
if(
(fa == SIN || fa == COS) &&
(fb == SIN || fb == COS) &&
!contains_arctrigs(a) &&
!contains_arctrigs(b)
)
++sincosflag;
}
if(g == '*') /* is there a product whose factors include a sum ? */
/* or whose factors include an integral power of a sum? */
/* or a log, ln, or logb ? */
{ m = ARITY(summand);
for(k=0;k<m;k++)
{ temp = ARG(k,summand);
h = FUNCTOR(temp);
if(
( h=='+' ||
(h == '^' && FUNCTOR(ARG(0,temp))=='+' &&
ISINTEGER(ARG(1,temp))
)
)
&&
( !comdenomflag || indenomflag ||
innumflag || !fractionflag
)
)
/* don't expand outside fractions in commondenom problems;
example: n(n+1) + 1/(n+1) + 1/n ;
but: we DO expand in fractions, as in
(x(x+1) +x )/((x-1)(x-2))
even if this is in a common denom problem
and we DO expand when there are NO fractions as in
2a^2 - (a-3)(2a+1)
*/
{ ++localexpandflag;
break;
}
if(ISATOM(temp) && h == 'i' && get_complex())
++iflag;
if(trigfunctor(h))
{ trigflag |= (1<<TRIGINDEX(h)); /* see comments above function */
if(!ATOMIC(ARG(0,temp)))
trigflag |= 0x1000; /* set bit 12 */
}
if(h == '^' && trigfunctor(FUNCTOR(ARG(0,temp))))
{ unsigned short hh = FUNCTOR(ARG(0,temp));
trigflag |= (1<<TRIGINDEX(hh));
if(equals(ARG(1,temp),two))
trigsqflag |= (1<<TRIGINDEX(hh));;
}
}
}
if(g == '^' && FUNCTOR(ARG(0,summand))=='+' && INTEGERP(ARG(1,summand)) && !difflag)
++localexpandflag;
}
if(constflag && varflag==1 && !get_infractionflag() && SOLVETYPE(problemtype))
fractionflag = 0; /* don't use common denoms then */
/* but do use commondenom or even addfractions
on either all-constant terms or all-variable terms;
and do use them inside compound fractions. The aim is
to not use common denominators when solving
(1/2) x + 3 = 6; we will transfer 3 first. */
if(infinityflag)
{ o[i] = addinfinity; ++i;
}
if(cofiflag && !localintflag && constflag)
{ o[i] = absorbconstant; ++i;
}
if(atantan2flag == 3)
{ o[i] = atantan2; ++i;
}
if(iflag == 1 && topic != _complex_arithmetic &&
(
( pathlength >= 3 &&
path[pathlength-3] == '^' &&
path[pathlength-2] == 1 /* we are in the base of an exponent */
)
||
( pathlength >= 3 &&
path[pathlength-3] == LN
)
||
( topic == _polar_form &&
(
pathlength <= 1 ||
(pathlength == 3 && path[0] == '*')
)
)
/* if you don't put some restrictions on the path you'll get an infinite
regress on i = e^(i pi/2) = e^(pi/2 e^(i pi/2)) = ... as
the i in the exponent is continually replaced by e^(i pi/2) */
)
)
{ o[i] = rectangulartopolar; ++i;
}
if(topic != _factor_by_grouping || get_currentline() > 2)
{/* In factor by grouping, don't collect right away but give
content_factor a chance first */
if(get_complex() &&
(
pathlength <= 1 ||
(pathlength == 3 && path[0] == '=') ||
(pathlength == 5 && path[0] == OR && path[2] == '=')
)
)
{ if(problemtype == SOLVE_EQUATION && contains(t,FUNCTOR(get_eigenvariable())))
/* example, ix^2 + x + i, don't write it as x + (x^2 + 1)i */
{ o[i] = writeaspoly; ++i;
}
else /* after solving, when simplifying the right side */
{ o[i] = complexform; ++i; /* preempt collectall and orderterms */
}
}
if(status(collectall) >= KNOWN)
{ o[i] = collectall; ++i;
}
else
{ o[i] = collectterms; ++i;
}
}
if(fractionflag &&
(!limitflag || (pathlength >= 3 && path[pathlength-3] != LIMIT)) &&
/* don't add fractions when limsum could be used. If the
individual limits are indeterminate, then eventually
commondenom will be used to add them.
*/
(
!iscomplex(t) || n > 2 ||
(iscomplex(ARG(1,t)) && iscomplex(ARG(0,t))) ||
(FRACTION(ARG(0,t)) && iscomplex(ARG(1,ARG(0,t)))) ||
(NEGATIVE(ARG(0,t)) && FRACTION(ARG(0,ARG(0,t))) && iscomplex(ARG(1,ARG(0,ARG(0,t)))))
/* don't add fractions in sqrt(3)/2^(3/4) + i/2^(3/4) */
)
)
{ o[i] = addfractions; ++i;
}
if(limitflag || inpowerflag || logcollectflag || SOLVETYPE(problemtype))
/* when autosum is called from ssolve, we have SOLVETYPE(problemtype)
temporarily, and logcollectflag can be zero, because it has not
been changed along with problemtype */
{ if(logflag & 2) /* two or more lns */
{ o[i] = minusflag ? collectlns2 : collectlns; ++i;
if(n > 2 && minusflag)
{ o[i] = collectlns; ++i;
/* e.g. ln x + ln y - log z; collectlns2 only works
on ln x - ln y */
}
}
if(logflag & 8) /* two or more logs */
{ o[i] = minusflag ? collectlogs2 : collectlogs; ++i;
if(n > 2 && minusflag)
{ o[i] = collectlogs; ++i;
}
}
if(logflag & 32) /* two or more logb's */
{ o[i] = minusflag? collectlogb2 : collectlogb; ++i;
if(n > 2 && minusflag)
{ o[i] = collectlogb; ++i;
}
}
}
trigpolyflag = get_trigpolyflag();
if(trigflag &&
trigpolyflag != -1 && /* this means it's an algebraic identity */
(
(
!(trigexpandflag & 2) && /* only one trig function occurs in
the entire problem. */
!(trigexpandflag & 1) /* all the trig functions have the same args,
or all have atomic args */
) ||
/* If both these conditions hold it's
a purely algebraic problem, so don't do any trig,
UNLESS
we're doing an integral; or
we're at toplevel, or in a fraction or power at toplevel etc;
or the parent functor is a SQRT, and arity is 2.
This will still make us miss f(1-sin^2) for complicated f.
*/
intflag || /* but not || indexflag as there's no use
doing trig manipulations in indexed sums */
pathlength == 1 ||
( pathlength == 3 && path[0] == '/') || /* example: (1-sin^2 t)/sin t */
( pathlength == 5 && path[0] == '-' && path[2] == '/') ||
( pathlength == 3 && path[0] == '^' && path[1] == 1) || /* (1-sin^2 t)^5 */
( pathlength == 5 && path[0] == '/' && path[3] == '^' && path[4] == 1) ||
( pathlength >= 3 && path[pathlength-3] == SQRT && n == 2)
)
)
{ if(INDICATES(trigsqflag,SIN) && INDICATES(trigsqflag,COS))
{ /* sin^2 + cos^2 = 1 is in pre_ops, so it gets used before
double angle formulas in sin^2 2x + cos^2 2x + sin x, and
before half-angle formulas in integral((sin^2 x + cos^2 x),x).
1 - sin^2 u = cos^2 u and 1 - cos^2 u = sin^2 u
are in pre_ops, so they get used before factoring,
but only for arity 2, so they must appear here for larger
arities when required.
*/
if(ARITY(t) == 2 && all_trig(t))
{ o[i] = reversedoublecos1; ++i; /* cos^2 x - sin^2 x = cos 2x */
}
if(problemtype == TRIG_IDENTITY || FACTORFLAG || ARITY(t)==2)
{ if(trigpolyflag != COS)
{ o[i] = sinsquare2; ++i; /* 1-sin^2 = cos^2 */
}
/* We use sinsqtocosq unless trigpolyflag says we're
trying to get rid of sin. But then we're not so liberal
about using cossqtosinsq; we use it only if trigpolyflag
says we ARE trying to get rid of cos. The intention is
to prevent loops or near-loops using these two operations.
*/
if(trigpolyflag == COS)
{ o[i] = cossqtosinsq; ++i;
}
/* rewrite cos^2 as 1-sin^2 */
/* example: sin^2x - cos^2x = 0 */
/* or: sin^2x - cos^2x = 1 - 2cos^2x */
/* but watch out for sin^4 x + 2 sin^2x cos^2x + cos^4 x,
which should be factored instead. So, on the first
pass when factoring is off, don't use this operator
on sums with 3 or more terms. You do HAVE to use
it on sums of arity 2 BEFORE they get factored,
as the above examples show. */
}
}
else if(INDICATES(trigsqflag,SIN) /* and cos^2 isn't present */
&& (!SOLVETYPE(problemtype)
/* when solving trig equations, don't rewrite
sin^2 as cos^2 if sin is the only trig function present */
|| ((trigflag & ~(1 << TRIGINDEX(SIN))) & 0x0fff) != 0
/* something besides SIN is present */
)
)
{ if(trigpolyflag != COS)
{ o[i] = sinsquare2; ++i; /* 1-sin^2 = cos^2 */
}
if(TRIGCONTAINS(trigflag,COS) && trigpolyflag == SIN)
/* example: 2 sin^2 x + 3 cos x - 3, the
sin^2 should be written as 1-cos^2 so the expression
becomes a polynomial in cos. */
{ o[i] = sinsqtocossq; ++i;
o[i] = sinsquare2; ++i;
}
}
else if(INDICATES(trigsqflag,COS) /* and sin^2 isn't present */
&& ( !SOLVETYPE(problemtype)
/* when solving trig equations, don't rewrite
cos^2 as sin^2 if cos is the only trig function present */
|| ((trigflag & ~(1 << TRIGINDEX(COS))) & 0x0fff) != 0
/* something besides COS is present */
)
)
{ if(trigpolyflag != SIN)
{ o[i] = sinsquare3; ++i; /* 1-cos^2 = sin^2 */
}
if(TRIGCONTAINS(trigflag,SIN) && trigpolyflag == COS)
/* rewrite cos^2 as sin^2-1 so it becomes a
polynomial in sin
*/
{ o[i] = cossqtosinsq; ++i;
o[i] = sinsquare3; ++i;
}
}
if(ARITY(t) == 2 && INDICATES(trigsqflag,SIN) && !INDICATES(trigsqflag,COS) && all_trig(t))
{ o[i] = reversedoublecos2; ++i; /* 1 - 2 sin^2 x = cos 2x */
}
if(ARITY(t) == 2 && INDICATES(trigsqflag,COS) && !INDICATES(trigsqflag,SIN) && all_trig(t))
{ o[i] = reversedoublecos3; ++i;
}
if(INDICATES(trigsqflag,TAN) && ARITY(t) == 2 && pathlength == 1)
{ o[i] = tansquare1; ++i; /* tan^2 u + 1 = sec^2 u */
/* finish the derivative of tan x from defn, for example;
but if you ALWAYS do this, you'll screw up polynomials
in tan x */
}
if(INDICATES(trigsqflag,TAN) && INDICATES(trigsqflag,SEC))
{ o[i] = secsqminustansq; ++i; /* sec^2 u - tan^2 u = 1 */
if(trigpolyflag != SEC)
{ o[i] = tansquare1; ++i; /* tan^2 u + 1 = sec^2 u */
}
if(trigpolyflag != TAN)
{ o[i] = tansquare2; ++i; /* sec^2 u - 1 = tan^2 u */
}
if(trigexpandflag & 0x0010)
{ o[i] = secsqtotansq; ++i; /* sec^2 u = tan^2 u + 1 */
}
}
else if(INDICATES(trigsqflag,TAN) && /* and sec^2 isn't present */
(!SOLVETYPE(problemtype)
/* when solving trig equations, don't rewrite
tan^2 as sec^2 if tan is the only trig function present */
|| ((trigflag & ~(1 << TRIGINDEX(TAN))) & 0x0fff) != 0
/* something besides TAN is present */
) &&
trigpolyflag != SEC
)
{ o[i] = tansquare1; ++i;
}
else if(INDICATES(trigsqflag,SEC) && /* and tan^2 isn't present */
(!SOLVETYPE(problemtype)
/* when solving trig equations, don't rewrite
sec^2 as tan^2 if sec is the only trig function present */
|| ((trigflag & ~(1 << TRIGINDEX(SEC))) & 0x0fff) != 0
/* something besides SEC is present */
) &&
trigpolyflag != TAN
)
{ o[i] = tansquare2; ++i; /* sec^2 u - 1 = tan^2 u */
if(trigpolyflag == SEC)
{ o[i] = secsqtotansq; ++i; /* sec^2 u = tan^2 u + 1 */
}
}
if(INDICATES(trigsqflag,CSC) && trigpolyflag == CSC)
{ o[i] = cotsquare2; ++i; /* csc^2 u - 1 = cot^2 u */
if(trigpolyflag == CSC)
{ o[i] = cscsqtocotsq; ++i; /* csc^2 u = cot^2 u + 1 */
}
}
if(INDICATES(trigsqflag,COT) && trigpolyflag == COT)
{ o[i] = cotsquare1; ++i; /* cot^2 u + 1 = csc^2 u */
}
if(INDICATES(trigsqflag,COT) && INDICATES(trigsqflag,CSC))
{ o[i] = cscsqminuscotsq; ++i; /* csc^2 u - cot^2 u = 1 */
if(trigpolyflag != CSC)
{ o[i] = cotsquare1; ++i; /* cot^2 u + 1 = csc^2 u */
}
if(trigpolyflag != COT)
{ o[i] = cotsquare2; ++i; /* csc^2 u - 1 = cot^2 u */
}
}
else if(INDICATES(trigsqflag,COT) && /* and csc^2 isn't present */
SOLVETYPE(problemtype) &&
/* when solving trig equations, don't rewrite
cot^2 as csc^2 if cot is the only trig function present */
((trigflag & ~(1 << TRIGINDEX(COT))) & 0x0fff) != 0 &&
/* something besides COT is present */
trigpolyflag != CSC
)
{ o[i] = cotsquare1; ++i;
if(trigpolyflag == COT)
{ o[i] = cotsqtocscsq; ++i; /* cot^2 u = csc^2 u - 1 */
}
}
else if(INDICATES(trigsqflag,CSC) && /* and cot^2 isn't present */
(!SOLVETYPE(problemtype)
/* when solving trig equations, don't rewrite
csc^2 as cot^2 if csc is the only trig function present */
|| ((trigflag & ~(1 << TRIGINDEX(CSC))) & 0x0fff) != 0
/* something besides CSC is present */
) &&
trigpolyflag != COT
)
{ o[i] = cotsquare2; ++i; /* csc^2 u - 1 = cot^2 u */
if(trigpolyflag == CSC)
{ o[i] = cscsqtocotsq; ++i; /* csc^2 u = cot^2 u + 1 */
}
}
/* A couple of ad-hoc rules about factoring certain trig identities
which could also be done by expanding: */
if( n==3 && (trigexpandflag & 8))
/* only even powers of trig functions appear */
{ o[i] = factorsquareofdif; ++i; /* sin^4 + 2sin^2cos^2 + cos^4 */
o[i] = factorsquareofsum; ++i;
}
if( n== 2 && (trigexpandflag & 8)) /* sin^4 - cos^4 */
/* only even powers of trig functions appear */
{ term u = ARG(0,t);
term v = ARG(1,t);
int sign = 0;
if(NEGATIVE(u))
{ u = ARG(0,u);
sign = 1;
}
if(NEGATIVE(v))
{ v = ARG(0,v);
sign = sign ? 0 : 1;
}
if(sign &&
(HIEVENPOWER(u) || OBJECT(u)) &&
(HIEVENPOWER(v) || OBJECT(v))
)
{ o[i] = differenceofsquares; ++i;
}
}
if(
(trigexpandflag & 8) && !((trigexpandflag & 2) && !(trigexpandflag & 1)) &&
/* only even powers of trig functions appear */
/* and not only one function */
/* Given a polynomial f(sin^2 x, cos^2 x), we
need to make it a polynomial of sin^2 x.
But, we don't want to use cossqtosinsq on
sin^2 t/cos^22 = tan^2 t !
Also, it looks awkward in 1-sin^2 x = cos^2 x
to rewrite the right side instead of the left.
And it looks worse in sin^2 x = 1 - cos^2 x to
write it 1-(1-sin^2 x). However,
you don't want to use 1-sin^2x => cos x indiscriminately
in pre_ops, because if in a fraction you may need
to factor and cancel. But if NOT in a fraction,
I can't see why wouldn't want to do this in
pre_ops, so it would never get here. */
!get_infractionflag() &&
/* that ought to take care of it. Rational functions
in sin^2 and cos^2 will eventually get
cross-multiplied. */
!trig_in_denom(t) /* in cos^2 x / sin^2 x - 2 cos^2 x,
don't change cos^2 to 1- sin^2, first
use common denoms */
)
{ if(INDICATES(trigflag,COS) && trigpolyflag != SIN)
{ o[i] = cossqtosinsq; ++i;
}
if(INDICATES(trigflag,CSC) && trigpolyflag != COT)
{ o[i] = cscsqtocotsq; ++i;
}
if(INDICATES(trigflag,SEC) && trigpolyflag != TAN)
{ o[i] = secsqtotansq; ++i;
}
}
}
if(pathlength >= 3 && path[pathlength-3] == SQRT)
stopfactor = stop_factorundersqrt(t); /* don't factor sqrt(2x + 4) */
if(pathlength >= 3 && path[pathlength-3] == ROOT)
stopfactor = stop_factorunderroot(t); /* don't factor sqrt(2x + 4) */
/* The following prevents contentfactoring e^ix - e^-ix or even e^x-e^-x */
if(!stopfactor && contains(t,'e'))
stopfactor = stop_complexnegexp(t);
/* The following prevents content-factoring a + ai = (1+i)a */
if(!stopfactor && get_complex() && complexexpression(t))
stopfactor = 1;
/* The following stops contentfactoring in e.g.
(x - (1/2) - sqrt(5)/2) as a factor on one side of an inequality
*/
if(!stopfactor && pathlength == 5 &&
SOLVETYPE(problemtype) &&
INEQUALITY(path[0]) &&
path[2] == '*' && path[4] == '+' &&
equals(ARG(0,t),get_eigenvariable())
)
{ for(j=1;j<ARITY(t);j++)
{ if(!econstant(ARG(j,t)))
break;
}
if(j==ARITY(t))
stopfactor = 1;
}
if(! SOLVETYPE(problemtype) && !limitflag && !localintflag &&
! EXPANDFLAG &&
problemtype != LINEAR_EQUATIONS &&
problemtype != ROOTS &&
!difflag && !intflag && !indexflag && !seriesflag &&
!contains(t,CONSTANTOFINTEGRATION) &&
/* don't content-factor after an integral is finished */
(localexpandflag || get_infractionflag() ||
(!TRIG_TOPIC(topic) && !CALCULUS_TOPIC(topic)) ||
(problemtype == TRIG_IDENTITY &&
(
(pathlength >= 5 && path[pathlength-5] == '=' && path[pathlength-3] == '*') ||
(pathlength >= 3 && path[pathlength-3] == '=')
)
)
/* in trig identities, content-factor in sums at toplevel or in products
at toplevel. This eliminates useless factoring while still enabling
us to find a common factor of both sides. */
) &&
!(pathlength >= 3 && no_factor_functor(path[pathlength-3])) &&
/* NEVER factor inside an arctrig functor */
/* !localintflag says don't factor a sum of integrals */
!algebraic_number(t) && !complex_number(t) &&
!(contains(t,PI_ATOM) && is_linear_in(t,pi)) &&
!stopfactor &&
!(problemtype == INTEGRATION && contains(t,CONSTANTOFINTEGRATION)) &&
/* it looks silly to write (1/2) a + c = (1/2) (a + 2c) */
topic != _trig_product /* on this topic we use distriblaw to reduce to a sum
of trig functions */
)
{ o[i] = contentfactor; ++i;
contentfactorflag = 1;
}
else if(!stopfactor &&
!(SOLVETYPE(problemtype) && ARITY(t) == 2 && equals(ARG(0,t),get_eigenvariable())) &&
/* don't factor (x-1/2) into 1/2(2x-1) */
(topic != _complete_the_square || inhibited(alltoleft)) &&
(
inhibited(alltoleft) || /* after completethesquare before factoring is done */
(FACTORFLAG && !constant(t) &&
(!(intflag || seriesflag || indexflag) || (!insqrtflag && !inrootflag && !infractexpflag)) &&
problemtype != LINEAR_EQUATIONS
)
)
)
/* for example inside ABS inside an integral; but don't
factor under sqrts or roots in integrals, because it blocks
completethesquare1. */
{ o[i] = contentfactor; ++i;
contentfactorflag = 1;
}
else if(limitflag &&
!(pathlength >= 3 && no_factor_functor(path[pathlength-3]))
)
{ o[i] = factoroutconstant; ++i;
}
else if(intflag &&
(path[pathlength-3] == INTEGRAL ||
(pathlength >= 5 && path[pathlength-3] == '/' && path[pathlength-5] == INTEGRAL)
)
)
/* example, integral((-2yu + y + yu^2)/((u-1)(u+1)),u);
we need to factor out 'y' from the numerator and then
from the integral, after which the rest of the numerator
will factor and cancel. */
{ o[i] = factoroutconstant; ++i;
}
if(fractionflag &&
((!difflag && !intflag && !seriesflag && !indexflag) || (get_infractionflag() && fc()))
)
/* It's easier to differentiate or integrate the sum of fractions;
but a compound fraction, like x/(x+2/x), should be simplified
first */
{ if( !algebraic_number(t) &&
(!get_complex() || !complexexpression(t)) &&
(
(comdenomflag && !(stopfactor && path[pathlength-3] == '*')) ||
comdenom_conditions(problemtype,t)
)
)
{ o[i] = factordenominator; ++i;
if( /* t is left side of equation with right side 0 */
(
( pathlength >= 3 && path[0] == '=' && path[1] == 1 &&
path[2] == '+' && path[3] == 0 &&
ZERO(ARG(1,history(get_currentline())))
) ||
( pathlength >= 5 && path[0] == OR && path[2] == '=' && path[3] == 1 &&
path[4] == '+' && path[5] == 0 &&
ZERO(ARG(1,ARG(path[1]-1,history(get_currentline()))))
)
)
&&
!contains(t,DIFF)
&&
!immediate_comdenom(t)
)
{ /* example, (x-1)(x+1)/(3x) - 3x/((x-1)(x+1)) + 3/2 = 0 */
set_substitutionflag(VISIBLESUBS);
o[i] = makesubstitution; ++i;
}
if(topic == _complete_the_square && /* t is the left side of the equation */
( pathlength >= 3 && path[0] == '=' && path[1] == 1 &&
path[2] == '+' && path[3] == 0
)
)
; /* don't use common denoms */
else if(status(commondenomandsimp) >= KNOWN)
{ o[i] = commondenomandsimp; ++i;
}
else if(status(commondenom) >= KNOWN)
{ o[i] = commondenom; ++i;
}
else
{ o[i] = findcommondenom; ++i;
}
}
}
if(!localexpandflag && /* but otherwise wait till after we expand */
!localintflag /* don't reorder till all the integration is done. */
/* If you can't finish the integration it really doesn't
matter in what order you leave the terms anyway. */
)
{ if( 4 <= n && n <= 6 &&
(topic == _factor_by_grouping || topic == _advanced_factoring)
)
{ o[i] = collectall; ++i;
o[i] = factorbygrouping; ++i;
/* try it before orderterms, because it looks silly to
order the terms when they're already grouped for
factorbygrouping */
}
if(!stop_orderterms(t))
{ o[i] = orderterms; ++i;
}
}
if( topic == _cubic_one_root ||
topic == _complex_cubics
)
{ o[i] = factorbypolydiv; ++i;
}
if( FACTORFLAG && !constant(t) &&
!CANTFACTOR(t) && !IRREDUCIBLE(t) &&
!stopfactor &&
!suppress_factoring(problemtype,1) &&
topic != _cubic_one_root &&
topic != _complex_cubics
)
/* The part about FACTORFLAG causes numerators
and denominators to be factored, but not if they
are in COMPOUND fractions. */
/* The part about suppress_factoring prevents factoring sums which
occur in sums, as 3x - 2(x^2-1) */
{ /* Note: if these are changed, change factorops in factor.c too */
o[i] = factoroutnumber; ++i;
/* don't clear denoms in a sum of the form (x - a) which occurs in a
product on one side of an inequality or equation */
if(topic != _complete_the_square &&
!(pathlength >= 5 && path[pathlength-3] == '*' && INEQUALITY(path[pathlength-5]))
)
{ o[i] = cleardenoms; ++i;
}
if(!contentfactorflag &&
(SOLVETYPE(problemtype) || limitflag) &&
!(pathlength >= 3 && trigfunctor(path[pathlength-3])) &&
!(pathlength >= 3 && no_factor_functor(path[pathlength-3])) &&
topic != _cubic_one_root &&
topic != _complex_cubics &&
(topic != _complete_the_square || pathlength > 3)
)
{ o[i] = contentfactor; ++i;
contentfactorflag = 1;
}
/* contentfactor is otherwise included above */
if((!SOLVETYPE(problemtype) || problemtype == INEQUALITIES) &&
/* quadraticformula will be used on an equation,
not on a sum, when solving equations; but for
inequalities, we must do it here because there's
nothing like spliteqn until we have linear factors.
*/
problemtype != LIMITS &&
problemtype != LHOPITAL &&
(get_ringflag() & ALGINT) /* don't use this when ringflag is set
to exclude factors involving radicals */
&& !insqrtflag && !inrootflag && /* don't use it under sqrt or root */
!infractexpflag /* or in the base of a fractional exponent */
)
/* The part about LHOPITAL says not to bother factoring numerator and
denom first when using L'Hopital's rule. */
{ o[i] = quadraticformula; ++i;
/* In 0 < (x-a)(x-b)(x^2 + cx + d), you should complete the
square so that the quadratic can be recognized as positive
and dropped. It would have been factored by now if if
had real roots.
*/
if(
(pathlength >= 3 && INEQUALITY(path[pathlength-3])) ||
(pathlength >= 5 && INEQUALITY(path[pathlength-5])) &&
path[pathlength-3] == '*'
)
{ o[i] = completethesquare; ++i;
/* if there are no real roots we should complete the square
so that the inequality can be reduced to true or false.
*/
}
}
if(problemtype != INDUCTION && n==2 &&
!insqrtflag && !inrootflag && !infractexpflag
// && !(trigexpandflag & 8) /* don't factor cos^4 x - sin^4 x */
/* No longer necessary as differenceofsquares should already have
taken care of this example above, and (trigexpandflag & 8) is
nonzero even on pure polynomial input which we DO want to factor.*/
)
{ term a = ARG(0,t);
term b = ARG(1,t);
if(NEGATIVE(a))
a = ARG(0,a);
if(NEGATIVE(b))
b = ARG(0,b);
if(
!(FUNCTOR(a) == '^' && (equals(ARG(1,a),two) || equals(ARG(1,b),three))) &&
!(FUNCTOR(a) == '^' && (equals(ARG(1,b),two) || equals(ARG(1,b),three))) &&
/* Don't use it on a sum or difference of squares or cubes,
that's already been tried (or blocked on purpose) in pre_ops */
!(pathlength >= 5 && path[pathlength-3] == '/' && path[pathlength-5] == '+')
/* Don't use it in a fraction which is part of a sum, because common denoms
will be used. If it actually will cancel after factoring,
then cancelgcd will factor it. Example: v + (v^5-1)/v^4 = 0
*/
)
{ o[i] = differenceofnthpowers; ++i;
o[i] = differenceofnth2; ++i;
/* this creates sigma terms and screws up induction proofs, that's
why we don't use it on problemtype INDUCTION */
o[i] = sumofnthpowers; ++i;
}
}
if( topic != _simple_factoring &&
topic != _trig_product &&
// !SOLVETYPE(problemtype) && /* includes IMPLICIT_DIFF */
(!comdenomflag || !get_infractionflag())
&& !insqrtflag && !inrootflag && !infractexpflag
&& !essentially_linear(t)
)
/* while doing common denoms, cancelgcd will find any useful
factors; however it looks better to use the above ops instead.
But these time-consuming ops should not be used in fractions.
For example, 5x^3 - 12y^3 takes a long time to fail to factor,
and it's better to use cancelgcd. But, if NOT in a fraction
and with factoring turned on, we have to put up with the wait.
*/
{ int subflag = get_substitutionflag();
if(subflag == VISIBLESUBS && pathlength <=1 &&
!contains_bound_vars(t) /* don't wind up with d/d(u^2) for example */
)
{ o[i] = makesubstitution; ++i;
}
else if(problemtype != SIMPLIFY && subflag != VISIBLESUBS)
{ o[i] = invisiblesub; ++i;
}
o[i] = factorquartic; ++i; /* before factorbypolydiv */
if(localexpandflag == n)
{ /* every term contained a sum; these sums might have a common
factor, as in 2(x^2-1) + 3(x^2+2x+1) = 2(x-1)(x+1) + 3(x+1)^2
= (x+1)(2(x-1) + 3(x+1)) */
o[i] = contentgcd; ++i;
}
if( (inq_display_on() || hintflag)
/* don't use this in think_ahead, because it
takes a long time and you wouldn't see any progress display;
if hintflag is on, though, user has pressed 'h' so we
do need to use it */
&& (not_quadratic(t)) /* don't use on quadratics; if it
could work, factorquadratic would too */
&& !(pathlength >= 3 && path[pathlength-2] == '=' && NEGATIVE(ARG(0,t)))
/* don't work on -ax^3... = 0 because changesigns will
be applied next and cause a duplication of effort.
Just let the sign be changed first. */
)
{ o[i] = factorbypolydiv; ++i; /* brother of guesslinearfactor */
}
}
else if(n==3)
{ o[i] = factorquartic; ++i; }
/* because of x^4 - 2x^2y^2 + y^4, we put factorquartic
AFTER invisiblesub, so this gets done as (x^2-y^2)^2 */
if(n==4 || n==5 || n == 6)
{ o[i] = collectterms; ++i;
o[i] = factorbygrouping; ++i;
}
if(n==3)
{ /* a(b+c) + ab + ac needs factorbygrouping */
int j;
term w;
for(j=0;j<3;j++)
{ w = ARG(j,t);
if(FUNCTOR(w) == '*' && contains(w,'+'))
{ o[i] = factorbygrouping; ++i;
break;
}
}
}
if( topic != _simple_factoring)
{ o[i] = squarefreefactors; ++i;
}
if( numerical_poly(t) &&
contains(t,'^') && /* don't try this on linear polynomials */
SOLVETYPE(problemtype) &&
/* don't supersede quadraticformula,
which is used on equations,
and so hasn't yet been tried. */
!quadratic_in(t,get_eigenvariable()) &&
! (path[0] == '=' && path[2] == '+' && NEGATIVE(ARG(0,t))) &&
/* don't work on -ax^3... = 0 because changesigns will
be applied next and cause a duplication of effort.
Just let the sign be changed first. */
! (path[0] == OR && path[2] == '=' && path[4] == '+' && NEGATIVE(ARG(0,t))) &&
topic != _complex_cubics &&
topic != _cubic_one_root &&
pathlength >= 3 && INEQUALITY(path[pathlength-1])
/* don't do it in the numerators of fractions, for instance */
)
{ o[i] = factornumerically; ++i; /* as a last resort */
}
}
if(INDICATES(trigflag,COS) &&
reversesinsq_conditions(t)
)
{ o[i] = reversesinsq; ++i;
}
if(INDICATES(trigflag,ATAN))
{ o[i] = sumofarctan; ++i;
}
if(INDICATES(trigflag,ASIN) && INDICATES(trigflag,ACOS))
{ o[i] = sumofarcsin; ++i;
}
if(localexpandflag &&
!contains(t,CONSTANTOFINTEGRATION) &&
(!get_complex() || !complexexpression(t))
)
{ o[i] = expand; ++i;
if(!stop_orderterms(t)) // modified 10.3.02 when I moved to VS.NET
{ o[i] = orderterms; ++i; /* postponed above */
}
}
if(pathlength >= 3 && path[pathlength-3] == SQRT)
{ o[i] = factorsquareofsum; ++i;
o[i] = factorsquareofdif; ++i;
/* example: sqrt(36a^4 - 12a^2b^2 + b^4) */
}
if(pathlength >= 3 &&
( path[pathlength-3] == SQRT || path[pathlength-3] == ROOT) &&
is_complex(t)
)
{ o[i] = rectangulartopolar; ++i; /* need polar form to evaluate sqrt(3-4i) */
}
if(n > 2 && SOLVETYPE(problemtype))
{ o[i] = writeaspoly; ++i;
/* writeaspoly chooses the term or variable we are solving
for and writes the sum as a polynomial in that term */
}
*nops = i;
}
/*__________________________________________________________________*/
void autoproduct(term t,actualop *o, int *nops)
/* do the work of post_ops for a product term */
{ int i=0,j;
unsigned short n = ARITY(t);
unsigned short g,h;
int coshsinhflag = 0;
term coshsinharg = zero; /* must be initialized to avoid a warning */
int intflag = get_intflag();
int indexflag = get_indexsumflag();
int seriesflag = get_seriesflag();
int problemtype = get_problemtype();
int topic = get_currenttopic();
int logcollectflag = get_logcollectflag();
int expandflag = get_expandflag();
int factorflag = get_factorflag();
int comdenomflag = get_comdenomflag();
int radicalflag = get_radicalflag();
int pathlength = get_pathlength();
unsigned short const *path = get_path();
term u,logrootindex,cancelled,trash;
int complexflag = get_complex();
int sumflag, vectorflag, trigflag, ntrigflag,logflag, nlogs, nconst,iflag,complexpolyflag;
int infinityflag, sqrtflag, rootflag, sigmaflag, absflag, fractintpowerflag,fractexpflag,sgflag;
int logsqrtflag=0, logrootflag=0, complexargflag = 0, powerofsumflag = 0;
sumflag = vectorflag = trigflag = ntrigflag = logflag = nlogs = nconst = iflag = complexpolyflag = 0;
infinityflag = sqrtflag = rootflag = sigmaflag = absflag = fractintpowerflag = fractexpflag = sgflag = 0;
if(get_whichpass() == 5 && seminumerical(t) && contains_double(t))
{ o[i] = devalop; ++i;
*nops = i;
return;
}
if(pathlength >= 5 &&
path[pathlength-3] == SQRT &&
intflag /* sqrt of product in an integrand */
)
{ unsigned short j;
term x = get_eigenvariable();
for(j=0;j<n;j++)
{ if(!is_linear_in(ARG(j,t),x))
break;
}
if(j==n)
{ /* product of linear functions in a square root */
/* example: integral(x^2 sqrt((x-3)(3-x)),x),
which can be done by trig substitution once the (x-3)(3-x) is multiplied out.
*/
o[i] = multiplyoutandsimp; ++i;
}
}
if(pathlength >= 3 && path[pathlength-3] == DIFF &&
topic == _diff_polynomial && /* product rule and chain rule are not to be used */
contains(t,'+')
)
{ o[i] = multiplyoutandsimp;++i;
}
for(j=0;j<n;j++)
{ u = ARG(j,t);
if(ISINFINITE(u))
infinityflag = 1;
if(constant(u))
++nconst;
if(iscomplex(u))
{ ++complexargflag;
if(equals(u,complexi))
{ ++iflag;
++complexpolyflag;
continue;
}
}
if(ATOMIC(u))
continue;
g = FUNCTOR(u);
switch(g)
{ case '/' :
break;
case '+' :
if(SOLVETYPE(problemtype) && econstant(u) && !econstant(t))
break; /* constant sums should be left alone; for example,
(3x + 2) dy/dx should not be distributed in
implicit diff problems */
++sumflag;
h = contains_sqrt(u);
if(h==ABS)
++absflag;
else if(h==SQRT)
++sqrtflag;
else if(h==ROOT)
++rootflag;
if(contains_fractional_exponents(u))
++fractexpflag;
if(complexflag && iscomplex(u) && ispolyin(u,complexi))
++complexpolyflag;
if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == COSH &&
FUNCTOR(ARG(1,u)) == SINH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(1,u))) &&
(coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
)
{ ++coshsinhflag;
coshsinharg = ARG(0,ARG(0,u));
}
else if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == SINH &&
FUNCTOR(ARG(1,u)) == COSH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(1,u))) &&
(coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
)
{ ++coshsinhflag;
coshsinharg = ARG(0,ARG(0,u));
}
else if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == SINH &&
NEGATIVE(ARG(1,u)) &&
FUNCTOR(ARG(0,ARG(1,u))) == COSH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(0,ARG(1,u)))) &&
(coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
)
{ ++coshsinhflag;
coshsinharg = ARG(0,ARG(0,u));
}
else if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == COSH &&
NEGATIVE(ARG(1,u)) &&
FUNCTOR(ARG(0,ARG(1,u))) == SINH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(0,ARG(1,u)))) &&
(coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
)
{ ++coshsinhflag;
coshsinharg = ARG(0,ARG(0,u));
}
break;
case MATRIX:
vectorflag = 1;
break;
case VECTOR:
vectorflag = 1;
break;
case SQRT:
++sqrtflag;
break;
case ROOT:
++rootflag;
break;
case SUM:
sigmaflag = 1;
break;
case ABS:
++absflag;
break;
case SG:
++sgflag;
break;
case SIN:
++ntrigflag;
trigflag |= (1 << TRIGINDEX(SIN));
break;
case COS:
++ntrigflag;
trigflag |= (1 << TRIGINDEX(COS));
break;
case TAN:
++ntrigflag;
trigflag |= (1 << TRIGINDEX(TAN));
break;
case SEC:
++ntrigflag;
trigflag |= (1 << TRIGINDEX(SEC));
break;
case CSC:
++ntrigflag;
trigflag |= (1 << TRIGINDEX(CSC));
break;
case COT:
++ntrigflag;
trigflag |= (1 << TRIGINDEX(COT));
break;
case LN:
if(!constant(u))
{ logflag = LN;
++nlogs;
}
if(FUNCTOR(ARG(0,u))==SQRT)
logsqrtflag = LN;
if(FUNCTOR(ARG(0,u))==ROOT)
{ logrootflag = LN;
logrootindex = ARG(0,ARG(0,u));
}
break;
case LOG:
if(!constant(u) || constant(t))
/* we need to change 2 log 2 to log 2^2, that's why
the part about constant(t); otherwise 10^(2 log 2)
does not simplify to 4 */
{ logflag = LOG;
++nlogs;
}
if(FUNCTOR(ARG(0,u))==SQRT)
logsqrtflag = LOG;
if(FUNCTOR(ARG(0,u))==ROOT)
{ logrootflag = LOG;
logrootindex = ARG(0,ARG(0,u));
}
break;
case LOGB:
if(!constant(u) || constant(t))
{ logflag = LOGB;
++nlogs;
}
if(FUNCTOR(ARG(1,u))==SQRT)
logsqrtflag = LOGB;
if(FUNCTOR(ARG(1,u))==ROOT)
{ logrootflag = LOGB;
logrootindex = ARG(0,ARG(1,u));
}
break;
case '^':
if(FRACTION(ARG(1,u)))
{ if(INTEGERP(ARG(0,u)))
++fractintpowerflag;
++fractexpflag;
}
if(FUNCTOR(ARG(0,u)) == '+')
++powerofsumflag;
break;
}
}
if(FUNCTOR(ARG(0,t)) == '^' &&
FUNCTOR(ARG(1,t)) == '^' &&
equals(ARG(1,ARG(0,t)),ARG(1,ARG(1,t))) &&
ISINTEGER(ARG(0,ARG(0,t))) &&
ISINTEGER(ARG(0,ARG(1,t)))
)
/* example: 4^n 27^n x^(6n) should become (4*27)^n x^6n */
{ o[i] = prodofpowers; ++i;
}
if(coshsinhflag > 1)
{ o[i] = difofsquares; ++i;
/* (cosh + sinh)(cosh - sinh) = cosh^2 - sinh^2 = 1.
*/
}
if(absflag && sgflag)
{ o[i] = abstimessg; ++i;
/* |u| sg(u) = u */
}
if(ARITY(t) > 2 && (get_ringflag() & RATRING) &&
RATIONALP(ARG(0,t)) &&
numerical(ARG(1,t)) && !numerical(ARG(2,t))
)
{ o[i] = multiplycoefs; ++i;
/* change (1/2) sqrt(3) x to (sqrt(3)/2) x;
create (3 ln 2/5)x instead of (3/2) ln 2 x
*/
}
if(logflag == LN &&
nlogs == 1 && /* Don't create ln(x^ln x) or even ln (x^ln 2) */
logcollectflag &&
problemtype != DIFFERENTIATE_FROM_DEFN &&
(!limitflag || (pathlength >= 3 && path[pathlength-3] == '+')) &&
/* attract logs in a sum even in a limit. The reason for
!limitflag is that it blocks changelimitvariable in
differentiating a^x from definition. */
/* Note e^(n ln a) => a^n anyway by another operator. */
/* But we don't say !inpowerflag as formerly, because
of e^(2ln x + 5ln y) which
should go to e^(ln x^2 + ln y^5) */
nconst >= n-1 /* otherwise we'll create something like ln(x^x) or ln(x^sqrt x) */
)
{ o[i] = attractlns; ++i;
}
if(sumflag == 1 && !contains_calc(t))
{ o[i] = multiplyifcancels; ++i; /* a(b+c) = ab + ac if there's a cancellation in ab or ac */
}
if(logflag == LOG && nlogs == 1 && (inpowerflag || logcollectflag) && nconst >= n-1)
{ o[i] = attractlogs; ++i;
}
if(logflag == LOGB && nlogs == 1 && (inpowerflag || logcollectflag) && nconst >= n-1)
{ o[i] = attractlogb2; ++i;
}
if(logsqrtflag || logrootflag)
{ term c,s,cancelled,trash;
ratpart2(t,&c,&s);
if(logsqrtflag && INTEGERP(c) && ISEVEN(c))
{ if(logsqrtflag == LN)
{ o[i] = attractlns; ++i;
}
else if(logsqrtflag == LOG)
{ o[i] = attractlogs; ++i;
}
else if(logsqrtflag == LOGB)
{ o[i] = attractlogb2; ++i;
}
}
if(logrootflag && INTEGERP(c) && !cancel(c,logrootindex,&cancelled,&trash))
{ if(logrootflag == LN)
{ o[i] = attractlns; ++i;
}
else if(logrootflag == LOG)
{ o[i] = attractlogs; ++i;
}
else if(logrootflag == LOGB)
{ o[i] = attractlogb2; ++i;
}
}
}
if(infinityflag)
{ o[i] = timesinfinity; ++i;
}
if(limitflag)
{ if(get_complex())
{ o[i] = multiplycomplexconjugates; ++i;
/* must precede difofsquares */
}
o[i] = difofsquares; ++i;
o[i] = makedifofcubes; ++i;
o[i] = makesumofcubes; ++i;
o[i] = difofpowers; ++i;
}
if(ntrigflag > 1 && /* more than one trig function in this product */
(
topic == _trig_product ||
difflag || /* d/dx (sin x cos x) = d/dx (1/2) sin 2x */
(limfractflag == -1 && pathlength >= 3 && path[pathlength-3] != '/') ||
/* use reversedoublesin in limits of fractions where the
product in question is not the numerator or denom itself
but is deeper. So we don't use it on
lim(x->0, sin x cos x)
lim(x->0, (sin x cos x)/x
*/
(pathlength >= 3 && path[pathlength-3] == '=')
/* to solve 2 sin x cos x = 1, write the lhs as sin 2x. From
here we can't check easily whether the other side of the equation
is constant, and in general it is NOT a good thing to do otherwise,
e.g. in verifying sin^4 x = cos^3 x sin x + sin^3 x cos x,
we get this expression in a factor on the left and thrash around
considerably if reversedoublesin is used. Therefore we appeal
to all_trig here to look outside this term.
*/
)
)
{ if(TRIGCONTAINS(trigflag,SIN))
{ if(!intflag && !seriesflag && !indexflag && (topic == _trig_product || all_trig(t)))
{ if(ARITY(t) >= 3 && equals(ARG(0,t),two))
{ o[i] = reversedoublesin2; ++i;
}
else
{ o[i] = reversedoublesin; ++i;
}
/* don't use in integrals, it blocks the Weierstrass substitution */
}
if(!limitflag)
{ o[i] = sinsin; ++i;
o[i] = cossin; ++i;
o[i] = sincos; ++i;
}
}
if(TRIGCONTAINS(trigflag,COS) && !limitflag)
{ o[i] = coscos; ++i;
}
}
if(difflag && sumflag > 1 &&
( path[pathlength-1] == DIFF || /* d/dx (1-cos x)(1 + cos x) */
(path[pathlength-1] == '/' && path[pathlength-3] == DIFF)
/* d/dx (1/((1-cos x)(1+cos x))) */
)
)
{ o[i] = difofsquares; ++i;
o[i] = makedifofcubes; ++i;
o[i] = makesumofcubes; ++i;
}
if(
(sumflag && EXPANDFLAG && complexpolyflag == 0) ||
complexpolyflag > 1
/* (1 + sqrt 2)i does not go into the following code and
hence distriblaw does not get used on it, because
complexpolyflag will be 1 in this case. */
)
{ if(!limitflag) /* else it's already been added */
{ if(get_complex())
{ o[i] = multiplycomplexconjugates; ++i;
/* must precede difofsquares */
}
if(sumflag > 1)
{ o[i] = difofsquares; ++i;
o[i] = makedifofcubes; ++i;
o[i] = makesumofcubes; ++i;
}
}
if(sumflag > 1)
{ if(status(multiplyoutandsimp) >= KNOWN)
{ o[i] = multiplyoutandsimp; ++i;
}
else
{ o[i] = multiplyout; ++i;
}
}
else if(!limitflag && !get_polyvalfactorflag())
/* leave (sin x)(1 - cos h)/h alone in a limit */
/* and don't use distriblaw if content-factoring is turned on */
{ o[i] = distriblaw; ++i;
}
else
{ o[i] = distribandcancel; ++i;
}
}
else if(
((sqrtflag > 1 || rootflag > 1 || absflag > 1 || fractexpflag > 1) && sumflag) &&
/* example: (2 sqrt 7 + sqrt 2)(3 sqrt 5 - 2 sqrt 3) should be multiplied out */
/* But, we DON't want to multiply out in (x-�3)(x+�3) < 0,
especially if we just factored it. */
( !FACTORFLAG ||
econstant(t)
) &&
! (inpowerflag && get_complex()) &&
! (pathlength == 3 && path[0] == '+' && get_complex() &&
iscomplex(t) && !cancel(t,complexi,&cancelled,&trash)
&&!iscomplex(trash)
) /* don't distribute in x + (a+b)i */
)
{ if(sumflag > 1)
{ if(status(multiplyoutandsimp) >= KNOWN &&
( CALCULUS_TOPIC(topic) ||
(TRIG_TOPIC(topic) && topic >= _logarithms)
)
)
{ o[i] = multiplyoutandsimp; ++i;
}
else
{ o[i] = multiplyout; ++i;
}
}
else
{ o[i] = distriblaw; ++i;
}
*nops = i;
return;
}
else
{ if((sumflag == 1 && DISTRIB && /* and not in a denom in a sum of fractions */
!(pathlength >= 5 && path[pathlength-2]==2 &&
path[pathlength-3]== '/' && path[pathlength-5] == '+'
) &&
!(pathlength >= 7 && path[pathlength-2]==2 &&
path[pathlength-3]== '/' && path[pathlength-5] == '-' &&
path[pathlength]-7 == '+'
) &&
/* No point distributing just inside a logarithm */
!(pathlength >= 3 && (path[pathlength-2] == LN || path[pathlength-2] == LOG)) &&
/* Don't distribute a power of a sum over a sum, e.g. (a+b)^2(c+d).
If you don't intend to expand everything this won't help anyway.
If you do intend eventually to expand everything,
you'll just content_factor it again before expanding.
First the power should be expanded. */
(!powerofsumflag || !sumflag) &&
/* following stops (2k+1)pi i from being distributed */
!(iflag == 1 && complexargflag == 1)
)
||
(get_infractionflag() && cfc() == DIFF &&
topic != _logarithmic_differentiation
)
||
topic == _trig_product
)
/* the conditions in DISTRIB are carefully chosen
so we never get contentfactor and distriblaw
together, because they would loop. The last part
causes distriblaw to be used in fractions inside derivatives,
except when we're studying logarithmic differentiation.
*/
{ o[i] = distriblaw; ++i;
}
else if(sumflag == 1 && ARITY(t)== 2 && RATIONALP(ARG(0,t))
&& !get_polyvalfactorflag() && !factorflag && !get_infractionflag()
)
{ o[i] = distriblaw; ++i;
/* Always distribute a rational fraction except when
it would loop with contentfactor or when we are in
a compound fraction. */
}
else
{ o[i] = distribandcancel; ++i;
}
if( inhibited(cancelop) /* after rationalizedenom for example */
&& !limitflag /* else it's already been thrown in */
&& !inhibited(arithmetic)
/* after findcommondenom, we don't want to multiply together
factors in the denom before addfractions has been used.
We recognized that situation by inhibited(arithmetic) */
)
{ if(contains(t,'i'))
{ o[i] = multiplycomplexconjugates; ++i;
}
if(
!(get_ringflag() & ALGINT) || /* otherwise we get a loop factoring (x-�a)/(x^2-a) */
(!contains(t,ROOT) && !contains(t,SQRT))
/* but on (1+sin x)/(1-sin x)(1+sin x) we really need to do it! */
)
{ if(get_complex())
{ o[i] = multiplycomplexconjugates; ++i;
/* must precede difofsquares */
}
o[i] = difofsquares; ++i;
o[i] = makedifofcubes; ++i;
o[i] = makesumofcubes; ++i;
}
}
else if(n == 2 && !(get_ringflag() & ALGINT))
{ term u = ARG(0,t);
term v = ARG(1,t);
int uflag = contains_sqrt(u);
int vflag = contains_sqrt(v);
if(NEGATIVE(u))
u = ARG(0,u);
if(NEGATIVE(v))
v = ARG(0,v);
if( uflag == SQRT || vflag == SQRT)
{ if(get_complex())
{ o[i] = multiplycomplexconjugates; ++i;
/* must precede difofsquares */
}
o[i] = difofsquares; ++i;
}
/* Example (�(ab) - b)(�(ab) + b) / (a-b) =
ab-b^2 = b(a-b)/(a-b) = b
This has to be done with factorflag on! But, we don't want
to loop. What about (1-sin �2)(1+sin �2) = 1-sin^2 �2?
That's why we use contains_sqrt(t) instead of contains(t,SQRT).
*/
if( uflag == ROOT || vflag == ROOT)
{ o[i] = makedifofcubes; ++i;
o[i] = makesumofcubes; ++i;
}
}
}
/* if(vectorflag)
{ o[i] = vsimp; ++i;vectorflag = 0;} */
/* * is scalar multiplication of a vector */
/* FINISH THIS */
if(sqrtflag > 1)
{ o[i] = productofsqrts; ++i;
}
if(rootflag > 1)
{ o[i] = productofroots; ++i;
}
if(problemtype >= LIMITS && sqrtflag && radicalflag != 1)
{ o[i] = sqrtexp; ++i; /* x�x => x^(3/2) */
}
if(problemtype >= LIMITS && rootflag && radicalflag != 1)
{ o[i] = rootexp; ++i; /* x�x => x^(3/2) */
}
if((sqrtflag > 1 || rootflag > 1 || absflag > 1) && sumflag==1)
/* sumflag == 1, not just sumflag != 0; we
don't want to use distriblaw on (x-sqrt a)(x-sqrt b) */
{ o[i] = distriblaw; ++i; /* sqrt x (sqrt x + 1) for example */
}
if(absflag > 1)
{ o[i] = multiplyabsval; ++i;
}
if(sigmaflag && EXPANDFLAG)
{ o[i] = productofsigmas; ++i;
}
if(fractintpowerflag >= 2)
{ o[i] = prodofpowers; ++i;
}
if(!vectorflag && !stop_orderfactors(t))
{ o[i] = orderfactors; ++i;
}
else if(constant(t) && /* see matrices.c for explanation of this restriction */
topic != _gauss_jordan
)
{ o[i] = multiplymatrices; ++i;
}
if(rootflag && ntrigflag && pathlength >= 3 && path[pathlength-3] == LIMIT)
/* example, lim(u->0+, root(3,1/u) sin u) */
{ o[i] = pushunderoddroot; ++i;
}
if(iflag == 1 && pathlength >= 3 && path[pathlength-3] == LN)
{ o[i] = rectangulartopolar; ++i;
}
*nops = i;
}
/*_______________________________________________________________*/
static int not_quadratic(term t)
/* return 1 if t contains a power with exponent unequal to 2;
0 if all exponents are equal to 2 (or there are no exponents) */
{ unsigned short n = ARITY(t);
int i;
if(ATOMIC(t))
return 0;
if(FUNCTOR(t) == '^' && !equals(ARG(1,t),two))
return 1;
for(i=0;i<n;i++)
{ if(not_quadratic(ARG(i,t)))
return 1;
}
return 0;
}
/*________________________________________________________________*/
static int essentially_linear(term t)
/* return 1 if t is, for example, 3(t-1) + t, a term composed
of sums and products with only linear terms */
{ int i,ct = 0;
unsigned short f = FUNCTOR(t);
unsigned short n = ARITY(t);
int problemtype = get_problemtype();
term u;
if(ATOMIC(t) || constant(t) || (SOLVETYPE(problemtype) && econstant(t)))
return 1;
if(f == '*')
{ for(i=0;i<n;i++)
{ u = ARG(i,t);
if(constant(u) || (SOLVETYPE(problemtype) && econstant(u)))
continue;
if(ct) /* a previous term was nonconstant; that makes two */
return 0;
++ct;
if(!essentially_linear(u))
return 0;
}
return 1; /* a product of constant terms and at most one linear term */
}
if(f=='+')
{ for(i=0;i<n;i++)
{ u = ARG(i,t);
if(!essentially_linear(u))
return 0;
}
return 1; /* a sum of linear terms */
}
if(f=='-')
return essentially_linear(ARG(0,t));
return 0; /* only +,*,- are acceptable functors in nonconstant terms */
}
/*__________________________________________________________*/
static int cfc(void)
/* cfc = compound fraction in calculus. Return DIFF or INTEGRAL
if we are in a compound fraction inside a diff or integral, according to 'path'. */
{ int i;
int state =0;
unsigned short const *path = get_path();
int pathlength = get_pathlength();
for(i=pathlength-1; i>=0; i-=2)
{ if(path[i] == '/')
++state;
if(state > 1 && (path[i] == DIFF || path[i] == INTEGRAL))
return path[i];
}
return 0;
}
/*__________________________________________________________*/
static int fc(void)
/* fc = fraction in calculus. Return DIFF or INTEGRAL
if we are in a fraction inside a diff or integral, according to 'path'. */
{ int i;
int state =0;
unsigned short const *path = get_path();
int pathlength = get_pathlength();
for(i=pathlength-1; i>=0; i-=2)
{ if(path[i] == '/')
++state;
if(state && (path[i] == DIFF || path[i] == INTEGRAL))
return path[i];
}
return 0;
}
#undef HIEVENPOWER
/*______________________________________________________________________*/
static int no_factor_functor(unsigned short f)
/* return 1 if we shouldn't even content_factor args of f,
0 otherwise. This is the the case for arctrig, hyperbolic, and special
functors, but not for log, and not for ordinary trig functors,
because in identities you sometimes have to content_factor before
using a double angle identity */
{ if(ACOS <= f && f <= ATAN)
return 1;
if(ASINH <= f && f <= ACOTH)
return 1;
return 0;
}
/*______________________________________________________________________*/
static int quadratic_in(term t, term x)
/* return 1 if t is quadratic in x */
{ POLYnomial p;
int ans;
int err = makepoly(t,x,&p);
if(err || DEGREE(p) != 2)
ans = 0;
else
ans = 1;
if(!err)
RELEASE(p);
return ans;
}
/*______________________________________________________________________*/
static int tanflag(term t)
/* t is a product; return 1 if t contains arctan(tan x),
return 2 if it contains an indefinite integral or constant of
integration, return 0 otherwise.
*/
{ unsigned short n;
int i;
term u;
n = ARITY(t);
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == CONSTANTOFINTEGRATION)
return 2;
if(ATOMIC(u))
continue;
if(FUNCTOR(u) == ATAN && FUNCTOR(ARG(0,u)) == TAN)
return 1;
if(FUNCTOR(u) == INTEGRAL && ARITY(u) == 2)
return 2;
}
return 0;
}
/*_________________________________________________________________________*/
static int comdenom_conditions(int problemtype, term t)
/* return 1 if common denoms should be used on sum t */
{ unsigned pathlength = get_pathlength();
unsigned short const *path = get_path();
if(get_infractionflag())
return 1;
if(SOLVETYPE(problemtype) &&
/* so apartandcancel won't be used, which would
create a loop */
attractible(t,get_eigenvariable())
)
return 1;
/* use common denoms just inside arctrig functions, e.g.
on 1 - (sqrt 2)/(1-sqrt 2). Maybe the arg will simplify
to 1/sqrt 2 or something else for which the arctrig
function can be evaluated. */
if(pathlength >= 3 &&
ACOS <= path[pathlength-3] && path[pathlength-3] <= ATAN &&
numerical(t)
)
return 1;
#if 0 //removed 4.5.96, screws up half angle 6
if(pathlength >= 3 &&
(path[pathlength-3] == TAN || path[pathlength-3] == COT) &&
problemtype == TRIG_IDENTITY && ARITY(t) == 2 &&
SIGNEDFRACTION(ARG(0,t)) && SIGNEDFRACTION(ARG(1,t)) &&
iseven(NEGATIVE(ARG(0,t)) ? ARG(1,ARG(0,ARG(0,t))) : ARG(1,ARG(0,t))) &&
iseven(NEGATIVE(ARG(1,t)) ? ARG(1,ARG(0,ARG(1,t))) : ARG(1,ARG(1,t)))
)
/* Example: tan(pi/4 + theta/2); there is a formula for tan(u/2),
but not for sin(u/2) or cos(u/2), so we only do this for tangent.
And we only want to do it in VERY SPECIAL circumstances as
it won't always work, e.g. on tan(pi/3 + theta/3), common denoms
don't do any good at all. The conditions stated here must match
those in postops where apart is used, to avoid a loop.
*/
return 1;
#endif
if(pathlength >= 3 && path[pathlength-3] == '^' &&
contains_fractional_exponents(t)
)
return 1; /* example: (x^(1/2) + 1/x^(1/2))^2 */
if(algebraic_number(t))
return 0; /* example, 3/2 + (1/2) sqrt(17). Leave these alone */
if( problemtype == LINEAR_EQUATIONS &&
seminumerical(t) /* simplify coefficients like sqrt 3 + 4/sqrt 3 */
)
return 1;
return 0;
}
/*______________________________________________________________________*/
int immediate_comdenom(term t)
/* return 1 if we shouldn't try substitutions in solving t = 0 but
should proceed to putting t over a common denom.
Example 1: when t = a + b/c where c is "noxious" (i.e. a sum containing a sqrt)
Example 2: sqrt x + b/sqrt x
Example 3: 4/x + 9/sqrt x + 2
Example 4: sqrt x - 3/(sqrt(4x-2) + 6 sqrt x)
Example 5: (5x-6)^(1/5) + x/(5x-6)^(4/5). This goes much faster by
common denominators than by substitution.
Example 6: All the denoms are multiples of the same quantity, as in
(2+x)/(6x) - 3/(5x) - 2 = 0
*/
{ int i,err,k;
unsigned short n;
term x,temp,u,v,p,q,d;
term sofar,c,s;
int flag = 0;
if(FUNCTOR(t) != '+')
return 0;
n = ARITY(t);
x = get_eigenvariable();
for(i=0;i<n;i++)
{ temp = ARG(i,t);
if(NEGATIVE(temp))
temp = ARG(0,temp);
if(FRACTION(temp))
{ if(!flag && contains(ARG(1,temp),FUNCTOR(x)))
{ flag = 1;
if(FUNCTOR(ARG(1,temp)) == '*')
twoparts(ARG(1,temp),x,&c,&s);
else
{ c = one;
s = ARG(1,temp);
}
sofar = s;
}
else if(flag == 1 && contains(ARG(1,temp),FUNCTOR(x)))
{ if(FUNCTOR(ARG(1,temp)) == '*')
twoparts(ARG(1,temp),x,&c,&s);
else
{ c = one;
s = ARG(1,temp);
}
if(!equals(s,sofar))
flag = 2;
}
if(contains_sqrt2(temp))
break;
d = ARG(1,temp);
if(FUNCTOR(d) == '+')
{ for(k=0;k<ARITY(d);k++)
{ u = ARG(k,d);
if(contains_sqrt2(u))
break;
if(FUNCTOR(u) == '^' && RATIONALP(ARG(1,u)))
break; /* handle Example 5 */
}
if(k<ARITY(d))
break;
}
}
}
if(flag == 1)
return 1; /* the condition mentioned in Example 6 holds */
if(i==n)
return 0; /* there must be at least one fraction involving sqrt x
or root x in the sum, or failing that, as in Example 4,
one of the fractions must have a denom which is a sum
and one of the factors has a sqrt factor. */
if(n > 2)
{ /* handle Example 3. What's significant about that
example is that the numerator turns out to be quadratic in sqrt x.
*/
subst(var0,sqrt1(x),t,&p);
if(equals(p,t))
return 0; /* No sqrt was present in t */
subst(make_power(var0,two),x,p,&q); /* 4/var0^2 + 9/var0 + 2 in Example 3 */
return contains(q,FUNCTOR(x)) ? 0 : 1;
}
u = ARG(0,t);
v = ARG(1,t);
if(SIGNEDFRACTION(u) && noxious(denom(u)))
return 1;
if(SIGNEDFRACTION(v) && noxious(denom(v)))
return 1;
if(!contains_sqrt2(u) && !contains_sqrt2(v))
return 0;
if(SIGNEDFRACTION(u) && !SIGNEDFRACTION(v))
{ temp = u;
u = v;
v = temp;
}
if(SIGNEDFRACTION(v) && !SIGNEDFRACTION(u))
{ d = denom(v);
err = cancel(u,d,&p,&q);
if(err && FUNCTOR(d) == '+')
{ term c,s,c2,s2;
int count=0;
ratpart2(u,&c,&s);
if(FUNCTOR(s) == SQRT)
{ for(k=0;k<ARITY(d);k++)
{ ratpart2(ARG(k,d),&c2,&s2);
if(FUNCTOR(s2) != SQRT)
return 0;
err = cancel(ARG(0,s),ARG(0,s2),&p,&q);
if(err || contains_sqrt2(q))
return 0;
if(!econstant(q))
++count;
if(count > 1)
return 0;
}
}
}
else if(err)
return 0;
if(!contains_sqrt2(q))
return 1; /* sqrt x + 1/sqrt x for example */
}
return 0;
}
/*_________________________________________________________________*/
static int stop_factorundersqrt(term t)
/* t is a sum. Return 1 if there's no point factoring t when it
occurs under a square root. Specifically return 0 if either the
content or principal part of t is not squarefree, when t is a
polynomial, or when t is not a polynomial, return 0 if the content
is not squarefree.
*/
{ term *atomlist;
term x,y,c,s,q;
POLYnomial p;
int err,nvars;
void *savenode;
if(FUNCTOR(t) != '+')
return 1; /* no point trying to factor a non-sum */
err = content_factor(t,&c,&s);
if(err)
return 0;
if(INTEGERP(c) && !nsquarefree(c))
return 0;
if(!contains(t,'^'))
return 1;
nvars = atomsin(t,&atomlist);
if(nvars == 1)
{ x = atomlist[0];
free2(atomlist);
err = makepoly(t,x,&p);
if(err)
return 0;
savenode = heapmax();
q = squarefree(p,&c);
if(ARITY(q) == 1)
{ reset_heap(savenode);
return 1; /* no multiple roots */
}
reset_heap(savenode);
return 0;
}
if(nvars == 2)
{ x = atomlist[0];
y = atomlist[1];
free2(atomlist);
err = homogeneous_poly(t,x,y,&p);
if(err)
return 0; /* can't factor it anyway probably but no harm trying...*/
savenode = heapmax();
q = squarefree(p,&c);
if(ARITY(q) == 1)
{ reset_heap(savenode);
return 1;
}
reset_heap(savenode);
return 0;
}
free2(atomlist);
return 0;
}
/*_________________________________________________________________*/
static int stop_factorunderroot(term t)
/* t is a sum. Return 1 if there's no point factoring t when it
occurs under a cube root. Specifically return 0 if either the
content or principal part of t is not cubefree, when t is a
polynomial, or when t is not a polynomial, return 0 if the content
is not squarefree.
*/
{ term *atomlist;
term x,y,c,s,q;
POLYnomial p;
int err,nvars;
void *savenode;
if(FUNCTOR(t) != '+')
return 1; /* no point trying to factor a non-sum */
err = content_factor(t,&c,&s);
if(err)
return 0;
if(INTEGERP(c) && !rootfree(c,3))
return 0;
if(!contains(t,'^'))
return 1;
nvars = atomsin(t,&atomlist);
if(nvars == 1)
{ x = atomlist[0];
free2(atomlist);
err = makepoly(t,x,&p);
if(err)
return 0;
savenode = heapmax();
q = squarefree(p,&c);
if(ARITY(q) < 3)
{ reset_heap(savenode);
return 1; /* no multiple roots */
}
reset_heap(savenode);
return 0;
}
if(nvars == 2)
{ x = atomlist[0];
y = atomlist[1];
free2(atomlist);
err = homogeneous_poly(t,x,y,&p);
if(err)
return 0; /* can't factor it anyway probably but no harm trying...*/
savenode = heapmax();
q = squarefree(p,&c);
if(ARITY(q) == 1) /* ARITY(q) > 3 was considered. Do we want
root(3,x^2 + 2x + 1) to become root(3,(x+1)^2)? Yes.
So don't put ARITY(q) > 3, which would prevent it.
*/
{ reset_heap(savenode);
return 1;
}
reset_heap(savenode);
return 0;
}
free2(atomlist);
return 0;
}
/*_________________________________________________________________________*/
int contains_arctrigs(term t)
/* Return 1 if t contains an arctrig functor, 0 if not */
{ unsigned short n,f;
int i;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(ACOS <= f && f <= ATAN)
return 1;
n = ARITY(t);
for(i=0;i<n;i++)
{ if(contains_arctrigs(ARG(i,t)))
return 1;
}
return 0;
}
/*_________________________________________________________________________*/
static int all_trig(term t)
/* t is presumed to be a subterm of history(currentline). Return 1
if t contains all the trig functions in history(currentline). Return 0
if there are some trig functions in history(currentline) not included
in t. */
{ int currentline = get_activeline();
term p,u;
if(currentline < 0)
return 0;
u = history(get_activeline());
subst(var0,t,u,&p);
return !contains_trig(p);
}
/*______________________________________________________________*/
static int trig_in_denom(term t)
/* return 1 if t contains a trig function in a denominator, 0 if not. */
{ unsigned short n;
int i;
if(ATOMIC(t))
return 0;
if(FRACTION(t))
{ if(contains_trig(ARG(1,t)))
return 1;
return trig_in_denom(ARG(0,t));
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(trig_in_denom(ARG(i,t)))
return 1;
}
return 0;
}
/*______________________________________________________________*/
static int stop_complexnegexp(term t)
/* return 1 if t is going to content_factor with a content
of the form e^-u. The name is a misnomer because the exponent
is not required to be complex.
*/
{ term c,s;
int err = content_factor(t,&c,&s);
if(err)
return 0;
if(FUNCTOR(c) == '^' &&
equals(ARG(0,c),eulere) &&
NEGATIVE(ARG(1,c)) &&
!constant(ARG(1,c))
)
return 1;
return 0;
}
/*______________________________________________________________________*/
static int reversesinsq_conditions(term t)
/* return 1 if we should rewrite 1-cos u as 2 sin^2(t/2), 0 if not, where
t is passed as a sum to which this rule might possibly be applicable.
This should be done only if afterwards, all the trig functions will have
argument u/2. For example, in sin(u/2) = 1-cos u. This equation can
only be solved otherwise by squaring and later on rejecting the spurious
solutions; the solution by reversesinsq is much nicer. But, there's no
hope of making this decision without examining the whole current line.
*/
{ char buffer[DIMREASONBUFFER];
term v, whole,temp;
int err,k;
unsigned short trigflag;
int problemtype = get_problemtype();
if((problemtype == LIMITS || problemtype == LHOPITAL) &&
status(lhopital) >= LEARNING
)
return 0; /* lim(x->0, (1-cos x)/x) should be done by L'Hopital */
if(problemtype == DIFFERENTIATE)
return 0;
if(get_intflag() || get_difflag())
return 0; /* sin^2 is converted to 1-cos to integrate it;
so using reversesinsq will cause a loop.
Also, sin^2 will be harder to differentiate,
so don't use it in derivatives. However,
it might be a good idea in a limit. */
k = get_activeline();
if(k < 0)
return 0;
whole = history(k);
set_trigflag(whole,&trigflag);
if(!(trigflag & (1 << 12)))
return 0; /* all args of trig functions are atomic */
err = reversesinsq(t,zero,&temp,buffer);
if(err)
return 0; /* it won't work anyway */
subst(temp,t,whole,&v);
if(different_trig_args(v))
return 0;
return 1;
}
/*__________________________________________________________________________*/
static int trig_arg_aux(term t, term *a, term *b)
/* return 0 if t contains no trig functions.
If *a is ILLEGAL, and all trig functions in t have the same arg,
put that arg in *a and return 1.
If *a is not ILLEGAL, and t contains a trig arg different from *a,
put that arg in *b and return 2.
If *a is not ILLEGAL, and t contains only trig args equal to *a,
return 1.
*/
{ unsigned short n,f;
int i,k;
if(ATOMIC(t))
return 0;
f = FUNCTOR(t);
if(TRIGFUNCTOR(f))
{ if(FUNCTOR(*a) == ILLEGAL)
{ *a = ARG(0,t);
return 1;
}
if(!equals(*a,ARG(0,t)))
{ *b = ARG(0,t);
return 2;
}
return 1;
}
n = ARITY(t);
for(i=0;i<n;i++)
{ k = trig_arg_aux(ARG(i,t),a,b);
if(k == 2)
return 2;
}
if(FUNCTOR(*a) == ILLEGAL)
return 0;
return 1;
}
/*__________________________________________________________________________*/
static int different_trig_args(term t)
/* return 1 if t contains trig functions with different args.
Return 0 if all trig functions have the same args or there are
no trig functions.
*/
{ term a,b;
int k;
SETFUNCTOR(a,ILLEGAL,0);
SETFUNCTOR(b,ILLEGAL,0);
k = trig_arg_aux(t,&a,&b);
return k > 1 ? 1 : 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists