Sindbad~EG File Manager
/* M. Beeson, for series.dll
automode control of series operations
3.2.00
modified 4.2.00
4.30.13 added evenoddtest and code that calls it.
5.1.13 added negativeleadingterm and code that calls it.
5.14.13 modified series_ops so it works on a negated series
added arithmetic to series_preops
5.20.13 added cotseries to series_presops
5.31.13 added more conditions under which seriesaddindex and seriessubindex can be used
6.4.13 put in addtozeta and ADDTOZETA
6.6.13 moved the CONVERGENCETEST operations to series_preops (instead of series_ops)
corrected 2305 to 2306
6.7.13 modified series_fremark
6.8.13 put integral_test at the top of series_preops.
put polyvalop in series_toplevel when it will work
6.9.13 modified series_preops to give up when the series is already marked CONVERGENT or DIVERGENT
6.9.13 500 lines of new code: indicator, guess_convergence, and convergence_tests.
6.11.13 added if(problemtype == POWERSERIES) break; twice near the end
corrected series_preops where it's supposed to quit as on 6.9.13
1.9.25 modified convergence_tests about integraltest.
and put condensation_test in under topic root_ratio_test
Added many seriesrev operations to series_preops, also seriesmoreterms
1.10.25 added !used(comparisontest2) etc.
1.13.25 made guess_convergence call summable.
*/
#include <assert.h>
#include <string.h>
#include "globals.h"
#include "graphstr.h"
#include "mpdoc.h"
#include "tdefn.h"
#include "checkarg.h" /* for operator typedef */
#include "ops.h" /* for prototypes of operators */
#include "operator.h"
#include "trig.h"
#include "calc.h"
#include "series.h"
#include "probtype.h" /* set_control_flags needs values of problemtype */
#include "prover.h"
#include "polynoms.h"
#include "exec.h"
#include "algaux.h"
#include "order.h"
#include "automode.h"
#include "scontrol.h"
#include "mtext.h" /* MAXMENUS */
#include "optable.h" /* access_optable */
#include "cflags.h" /* get_currenttopic */
#include "autosimp.h" /* get_pathlength */
#include "pvalaux.h" /* twoparts */
#include "deval.h"
static int ok_convergence_test(void);
static int evenoddtest(term u, term v);
static int negativeleadingterm(term t);
static int used(actualop op);
/*____________________________________________________*/
static int ok_convergence_test(void)
/* return 1 if the path contains only - and * before SUM
*/
{ int i;
int pathlength = get_pathlength();
unsigned short *path = get_path();
if(pathlength == 0)
return 1;
for(i=0;path[i];i+=2)
{ if(path[i] == SUM)
return 1;
if(path[i] != '-' && path[i] != '*')
return 0;
if(path[i+1]==0)
return 0;
}
return 1;
}
/*__________________________________________________________________*/
static int evenoddtest(term u, term v)
/* return 1 if u contains a subterm (-1)^v \pm 1 */
{ int n = ARITY(u);
int i;
term a,b;
if(ATOMIC(u))
return 0;
if(FUNCTOR(u) == '+' && ARITY(u) == 2)
{ a = ARG(0,u);
b = ARG(1,u);
if(FUNCTOR(a) == '^' && equals(ARG(1,a),v) && equals(ARG(0,a),minusone) && (ONE(b) || equals(b,minusone)))
return 1;
if(FUNCTOR(b) == '^' && equals(ARG(1,b),v) && equals(ARG(0,b),minusone) && (ONE(a) || equals(a,minusone)))
return 1;
if(FUNCTOR(a) == '^' && FUNCTOR(ARG(1,a)) == '+' && equals(ARG(0,ARG(1,a)),v) && equals(ARG(0,a),minusone) && (ONE(b) || equals(b,minusone)))
return 1;
if(FUNCTOR(b) == '^' && FUNCTOR(ARG(1,b)) == '+' && equals(ARG(0,ARG(1,b)),v) && equals(ARG(0,b),minusone) && (ONE(a) || equals(a,minusone)))
return 1;
if(ONE(b))
{ term c = a;
a = b;
b = c;
}
if(ONE(a) && FUNCTOR(b) == '-' && FUNCTOR(ARG(0,b)) == '^' && equals(v,ARG(1,ARG(0,b))) && equals(ARG(0,ARG(0,b)),minusone))
return 1;
if(FRACTION(a) && FRACTION(b) && equals(ARG(1,a),ARG(1,b)))
return evenoddtest(sum(ARG(0,u),ARG(0,v)),v);
if(FRACTION(a) && NEGATIVE(b) && FRACTION(ARG(0,b)) && equals(ARG(1,a),ARG(1,ARG(0,b))))
return evenoddtest(sum(ARG(0,a),tnegate(ARG(0,ARG(0,b)))),v);
}
for(i=0;i<n;i++)
if(evenoddtest(ARG(i,u),v))
return 1;
return 0;
}
/*__________________________________________________________________________*/
static int highest_term(term t, term x, term *c, term *deg)
/* assuming t passes ispolyin(t,x), get the highest power of x in *deg
and its coefficient in *c, returning 0 for success and 1 for failure.
*/
{ int err;
POLYnomial p;
err= makepoly(t,x,&p);
if(err)
return 1;
*deg = make_int(ARITY(p)-1);
*c = ARG(ARITY(p)-1,p);
return 0;
}
/*__________________________________________________________________________*/
static int indicator(term in, term n, term *ans)
/* return in *ans the dominant term of u as n goes to infinity, for purposes of
guessing convergence or divergence of sum(u,n,1,infinity). Return 0 for
success, 1 for failure. At successful exit, *ans will be either a power of n,
a power of e, or a LN of something, or 'one', or n.
*/
{ unsigned short f = FUNCTOR(in);
unsigned short m = ARITY(in);
int i,k,err;
term c,deg,u,v,num, denom,p,q,temp;
polyval(in,&u);
if(ATOMIC(u))
{ if(equals(u,n))
*ans = n;
else
*ans = one;
return 0;
}
if(!contains(u,FUNCTOR(n)))
{ *ans = one;
return 0;
}
if(NEGATIVE(u))
u = ARG(0,u); // sign doesn't matter here
if(f == LN)
{ err = indicator(ARG(0,u),n,&temp);
*ans = ln1(temp);
return 0;
}
if(ispolyin(u,n))
{ // can't use 'leading_term' as that gets the leading term for SMALL values of the variable
err = highest_term(u,n,&c,°);
if(err)
return 1;
*ans = make_power(n,deg);
return 0;
}
if( f== SIN || f == COS || f == ATAN || f == TANH || f == COSH || f == SINH)
{ p = ARG(0,u);
err = limval(limit(arrow(n,infinity),p),&temp);
if(err)
return 1;
if(equals(temp,infinity)|| equals(temp, minusinfinity))
{ if(f == SIN || f== COS || f == ATAN || f == TANH)
{ *ans = one;
return 0;
}
*ans = make_term('^',2);
ARGREP(*ans,0,eulere);
ARGREP(*ans,1,one);
return 0;
}
if(equals(temp,zero))
{ if(f == SIN || f == TAN || f == SINH || f == TANH)
{ *ans = make_power(n,minusone);
return 0;
}
if(f == COS || f == COSH)
{ *ans = one;
return 0;
}
}
if(equals(temp,undefined) || WILD(temp))
return 1;
*ans = one; // for any other value of the limit
return 0;
}
if(f == '/')
{ num = ARG(0,u);
denom = ARG(1,u);
err = indicator(num,n,&p);
if(err)
return 1;
err = indicator(denom,n,&q);
if(err)
return 1;
if(equals(p,n) && equals(q,n))
{ *ans = one;
return 0;
}
if(equals(p,n) && FUNCTOR(q) == '^')
{ if(equals(ARG(0,q),eulere))
{ *ans = make_power(eulere,tnegate(ARG(1,q)));
return 0;
}
if(equals(ARG(0,q),n))
{ *ans = make_power(n,sum(one,tnegate(ARG(1,q))));
return 0;
}
assert(0);
}
if(equals(q,n) && FUNCTOR(p) == '^')
{ if(equals(ARG(0,p),eulere))
{ *ans = p;
return 0;
}
if(equals(ARG(0,p),n))
{ *ans = make_power(n, sum(ARG(1,p),minusone));
return 0;
}
}
if(FUNCTOR(p) == '^' && FUNCTOR(q) == '^')
{ if(equals(ARG(0,p),ARG(0,q)))
{ c = sum(ARG(1,p),tnegate(ARG(1,q)));
polyval(c,°);
*ans = make_power(ARG(0,p),deg);
return 0;
}
if(equals(ARG(0,p),eulere))
{ *ans = p;
return 0;
}
if(equals(ARG(0,q),eulere))
{ *ans = make_power(eulere,tnegate(ARG(1,q)));
return 0;
}
assert(0);
}
if(equals(q,one) || FUNCTOR(q) == LN)
{ *ans = p;
return 0;
}
if(equals(p,one) || FUNCTOR(p) == LN)
{ if(FUNCTOR(q) == '^')
{ *ans = make_power(ARG(0,q),tnegate(ARG(1,q)));
return 0;
}
}
if(equals(p,one) && equals(q,n))
{ *ans = make_power(n,minusone);
return 0;
}
if(equals(p,n) && equals(q,one))
{ *ans = n;
return 0;
}
if(FUNCTOR(p) == LN && FUNCTOR(q) == LN)
return 0; // too hard
if(FUNCTOR(p) == LN && equals(q,n))
{ *ans = make_power(n,minusone);
return 0;
}
assert(0);
}
if(f == SQRT)
{ err = indicator(ARG(0,u),n,&v);
if(FUNCTOR(v) == LN)
{ *ans = v;
return 0;
}
if(FUNCTOR(v) == '^')
{ polyval(make_fraction(ARG(1,v),two),&temp);
*ans = make_power(ARG(0,v),temp);
return 0;
}
if (equals(v,n))
{ *ans = make_power(n,make_fraction(one,two));
return 0;
}
if(equals(v,one))
{ *ans = one;
return 0;
}
}
if(f == ROOT)
{ err = indicator(ARG(1,u),n,&v);
if(FUNCTOR(v) == LN)
{ *ans = v;
return 0;
}
if(FUNCTOR(v) == '^')
{ polyval(make_fraction(ARG(1,v),ARG(1,v)),&temp);
*ans = make_power(ARG(1,v),temp);
return 0;
}
if (equals(v,n))
{ *ans = make_power(n,make_fraction(one,two));
return 0;
}
if(equals(v,one))
{ *ans = one;
return 0;
}
}
if(f == '*' || f == '+')
{ term indicators[10];
term base;
int count = 0;
int flag,j;
m = ARITY(u);
k = 0;
if(m > 9)
return 0;
for(i=0;i<m;i++)
{ err = indicator(ARG(i,u),n,&temp);
if(err)
return 1;
if(equals(temp,n))
{ indicators[i] = make_term('^',2);
ARGREP(indicators[i],0,n);
ARGREP(indicators[i],1,one); /* make_power won't make n^1 so we do it directly */
}
else
indicators[i] = temp;
}
for(j=0;j<2;j++)
{ base = j==0 ? eulere : n;
for(i=0;i<m;i++)
{ v = indicators[i];
if(FUNCTOR(v) == '^' && equals( ARG(0,v),base) )
{ ++count;
if(count > 1)
{ term old = indicators[flag];
err = infer(lessthan(ARG(1,old),ARG(1,indicators[i])));
if(!err)
flag = i;
}
else
flag = i;
}
}
if(count == 1 || (count >= 1 && f == '+'))
{ *ans = indicators[flag];
return 0;
}
if(count > 1) // and f == '*' necessarily
{ deg = make_term('+',count);
k=0;
for(i=0;i<m;i++)
{ v = indicators[i];
if(FUNCTOR(v) == '^'&& equals(ARG(0,v),base))
{ ARGREP(deg,k,ARG(1,v));
++k;
}
}
if(k != count)
assert(0);
polyval(deg,&v);
if(!ZERO(v))
{ *ans = make_power(base,v);
return 0;
}
// if v is zero, we should perhaps return 0 for failure, but instead, since we're only guessing,
// we ignore the exponential or polynomial terms whose leading terms cancel, in effect assuming
// those terms exactly cancel, and go on to the polynomial or log terms
}
/* Now there are no terms that are a power of base */
} // end j-loop
// Now for the log terms, which must perforce be all the terms.
if(f=='+')
{ v = make_term('*',m);
for(i=0;i<m;i++)
{ assert(FUNCTOR(indicators[i]) == LN);
ARGREP(v,i,ARG(0,indicators[i]));
}
polyval(v,°);
*ans = ln1(v);
return 0;
}
return 0; // product of more than one log term; two hard
} // end of products and sums
if(f == '^')
{ err = indicator(ARG(0,u),n,&c);
if(err)
return 0;
if(ISINTEGER(ARG(1,u)))
{ if(FUNCTOR(c) == '^')
{ polyval(product(ARG(1,c),ARG(1,u)),&temp);
*ans = make_power(ARG(0,u),temp);
return 0;
}
if(equals(c,one))
{ *ans = one;
return 0;
}
if(FUNCTOR(c) == LN)
{ *ans = c; // for guessing purposes, a power of a log is the same as a log
return 0;
}
}
else
{ if(equals(c,n) || equals(c,eulere))
*ans = make_power(n,ARG(1,u));
else if(!contains(c,FUNCTOR(n)))
*ans = make_power(eulere,ARG(1,u)); // we're just guessing, after all
else
*ans = make_power(n,ARG(1,u)); // another wild guess
return 0;
}
}
return 1;
}
/*_________________________________________________________________________*/
static int summable(term v, term n)
/* return 1 if we're sure MathXpert can add the sum of v from n= 1 to infinity,
(possibly getting the answer infinity).
*/
{ term denom,num,c,s,u;
double z;
polyval(v,&u);
if(FRACTION(u))
{ num = ARG(0,u);
denom = ARG(1,u);
if(!contains(num,FUNCTOR(n)))
{ if(FUNCTOR(denom)== '^' && !contains(ARG(0,denom), FUNCTOR(n)))
{ twoparts(ARG(1,denom),n,&c,&s);
if(equals(s,n))
return 1; // a geometric series
}
if(FUNCTOR(denom)=='^' && seminumerical(ARG(1,denom)))
{ deval(ARG(1,denom),&z);
if(z != BADVAL && z > 1)
return 1; // a value of RiemannZeta
}
if(equals(denom,n))
return 1; // harmonic series
if(FUNCTOR(denom) == '*')
{ twoparts(denom,n,&c,&s);
if(equals(s,n))
return 1; // harmonic series
if(ARITY(denom) == 2)
{ /* tailored for problem 2, where denom is sqrt(n) sqrt(2n) */
term a,b,p1,p2,q1,q2;
a = ARG(0,denom);
b = ARG(1,denom);
if(FUNCTOR(a)== SQRT && FUNCTOR(b) == SQRT)
{ twoparts(ARG(0,a),n,&p1,&q1);
twoparts(ARG(0,b),n,&p2,&q2);
if(equals(q1,n) && equals(q2,n))
return 1; // will become a harmonic sum
}
}
}
}
}
return 0;
}
/*__________________________________________________________________________*/
static int guess_convergence(term u, term n)
/* u is the general term of a series with index variable n */
/* Return 1 to guess "convergent", -1 to guess "divergent", and 0 to not guess. */
{ term t;
int err;
double z;
unsigned short f;
if(summable(u,n))
return 1;
err = indicator(u,n,&t);
if(err) // then take a numerical guess
{ SETVALUE(n,1000);
deval(u,&z);
if(z == BADVAL)
return -1;
if(z > 100)
return -1;
if(z < 0.000001)
return 1;
return 0;
}
/* Now we have a good value from indicator */
if(equals(t,n) || ONE(t))
return -1;
f = FUNCTOR(t);
switch(f)
{ case '^':
assert(equals(ARG(0,t),n) || equals(ARG(0,t),eulere));
SETVALUE(n,1000);
deval(ARG(1,t),&z);
if(z==BADVAL || z >= -1)
return -1; // divergent
return 1; // convergent
case LN:
return -1;
}
assert(0);
return 0;
}
/*___________________________________________________________________________*/
static void convergence_tests(term t, actualop *o, int *nops)
/* decide what convergence tests are applicable to t, which must be an infinite series.
The series hasn't yet been proved CONVERGENT or DIVERGENT.
*/
{ int i = 0;
term v,w;
term n = ARG(1,t);
term u = ARG(0,t);
int currenttopic = get_currenttopic();
int convergence; /* we'll try to set it to 1 for convergence and -1 for divergence */
if(ALREADY(t))
return; // don't mess with series marked ALREADY
if(currenttopic == _integral_test && !used(integraltest))
// under this topic, we only use the integral test, even if the
// series is summable, and we only use it ONCE.
// The integral test leads only to an integral, and never to an
// inequality or another series.
{ o[i] = integraltest; ++i;
*nops = i;
return;
}
/* Now internally check if the n-th term tends to zero or not */
v = limit(arrow(n,infinity),u);
if(
!limval(v,&w) && !ZERO(w) && !equals(w,undefined) &&
!equals(w,bounded_oscillations) && !equals(w,unbounded_oscillations)
&& !used(divergencetest)
)
{ o[i] = divergencetest; ++i; // regardless of the topic
*nops = i; // it's going to work
return;
}
if(currenttopic == _root_ratio_tests)
{ if(get_nvariables() > 1)
{ /* probably a power series, try the root test first */
if(!used(roottest))
{ o[i] = roottest; ++i;
}
if(!used(ratiotest))
{ o[i] = ratiotest; ++i;
}
}
else /* try the ratio test first */
{ if(!used(ratiotest))
{ o[i] = ratiotest; ++i;
}
if(!used(roottest))
{ o[i] = roottest; ++i;
}
}
convergence = guess_convergence(u,n);
if(contains(t,LN) && !used(condensationtest))
{ o[i] = condensationtest; ++i;
}
// condensationtest will also work to show the harmonic series diverges
// so we include code specifically to catch that case and some others:
if(FUNCTOR(t)== SUM && FUNCTOR(ARG(0,t)) == '/')
{ term denom = ARG(1,ARG(0,t));
term num = ARG(0,ARG(0,t));
term n = ARG(1,t);
if(equals(denom, n) // harmonic series among others
|| (FUNCTOR(denom)== '^' && equals(ARG(1,denom),n) && mvpoly(num))
// sum( n/2^n, n, 1, infinity)
)
{ o[i] = condensationtest; ++i;
}
}
}
if(currenttopic == _comparison_test)
{ convergence = guess_convergence(u,n);
if(contains(t,LN) && !used(condensationtest))
{ o[i] = condensationtest; ++i;
}
if(convergence == -1 && !used(comparisontest2))
{ o[i] = comparisontest2; ++i;
}
else if(!used(comparisontest1)) // previous comment said sometimes you need it more than once.
{ o[i] = comparisontest1; ++i;
}
if(convergence != 1 && !used(limitcomparisontest))
{ o[i] = limitcomparisontest; ++i;
}
if(used(comparisontest1) || used(comparisontest2))
{ o[i] = integraltest; ++i;
/* example, sum (n/2^n,n,1,infinity), which arises
after problem 11 in the comparison_test topic.
*/
}
}
*nops = i;
return;
}
/*_________________________________________________________________________________*/
void series_preops(term t, actualop *o, int *nops)
/* called by pre_ops to get operators to apply to an infinite series.
These operators try to bring a series to geometric or telescoping form and sum the series
explicitly. It's necessary to use these in comparison_test too so that the new series
can eventually be added up. Put the operators in starting at o[i]; initial value of *nops is meaningless.
*/
{ unsigned short g = FUNCTOR(ARG(0,t));
int i = 0;
int problemtype = get_problemtype();
if(FUNCTOR(t) != SUM)
{ // assert(0);
*nops = i;
return;
}
if(problemtype == ADDSERIES)
{ term u = ARG(0,t);
term lo = ARG(2,t);
if(ISINTEGER(lo) && INTDATA(lo) > 1 && FUNCTOR(u) == '^' && equals(ARG(1,t),ARG(1,u)))
{ o[i] = seriesmoreterms; ++i;
}
if(FRACTION(u))
{ o[i] = expseriesrev; ++i;
o[i] = negexpseriesrev; ++i;
o[i] = sinseriesrev; ++i;
o[i] = lnseriesrev; ++i;
o[i] = cosseriesrev; ++i;
o[i] = atanseriesrev; ++i;
o[i] = binomialseriesrev;++i;
o[i] = oneminusxseriesrev; ++i;
o[i] = oneplusxseriesrev; ++i;
}
o[i] = xoveroneminusxseriesrev; ++i;
o[i] = xoveroneplusxseriesrev; ++i;
o[i] = oneoveroneplusxkseriesrev; ++i;
o[i] = xmoveroneplusxkseriesrev; ++i;
o[i] = oneoveroneminusxkseriesrev; ++i;
o[i] = xmoveroneminusxkseriesrev; ++i;
o[i] = seriesmoreterms; ++i;
}
if(problemtype == TESTCONVERGENCE &&
(CONVERGENT(t) || DIVERGENT(t)) /* the CONVERGENT bit is set when the convergence or divergence is established */
)
{ *nops = 0;
return; // other tests have already succeeded, we're done
}
if(seminumerical(ARG(0,t))) // constant summand. For example sum(1,k,0,infinity) = 0
{ o[i] = arithmetic; ++i;
}
if(problemtype == POWERSERIES)
{ if(evenoddtest(ARG(0,t),ARG(1,t)))
{ o[i] = seriesevenandodd; ++i;
}
}
switch(g)
{ case '-' :
o[i] = minusoutofsigma; ++i;
break;
case '+' :
o[i] = telescopingseries; ++i;
break;
case '/' :
o[i] = constantoutofsigma; ++i;
if(problemtype == POWERSERIES)
break;
if(problemtype == ADDSERIES)
{
if(ONE(ARG(2,t)) &&
(FUNCTOR(ARG(0,ARG(0,t))) == '^' || ONE(ARG(0,ARG(0,t)))) &&
FUNCTOR(ARG(1,ARG(0,t))) == '^'
)
{ o[i] = xoveroneminusxseriesrev; ++i;
}
if(
(FUNCTOR(ARG(0,ARG(0,t))) == '^' || ONE(ARG(0,ARG(0,t)))) &&
FUNCTOR(ARG(1,ARG(0,t))) == '^'
)
{ o[i] = oneminusxseriesrev; ++i;
/* if the lower limit isn't zero, it calls shiftindex first */
}
}
break;
case '^':
if(problemtype == POWERSERIES)
break;
if(problemtype == ADDSERIES)
{
if(ONE(ARG(2,t)))
{ o[i] = xoveroneminusxseriesrev; ++i;
}
else
{ o[i] = oneminusxseriesrev; ++i;
/* If the lower limit isn't zero,
it calls shiftindex first; it also
will, via geomseries_aux, make other preparatory steps. */
}
}
break;
}
if(problemtype == TESTCONVERGENCE && ( get_pathlength() == 1 || ok_convergence_test() ) ) // only do this at toplevel
{ int k2;
convergence_tests(t,o+i,&k2);
i += k2;
}
*nops = i;
}
/*_________________________________________________________________________________*/
static int negativeleadingterm(term t)
/* return 1 if t is a power series (or SUM in general)
with a negative power of x in the leading term, 0 otherwise.
*/
{ term u,k,x,v,c,m,lo;
double oldval, loval,exp;
int err;
if(FUNCTOR(t) != SUM)
return 0;
u = ARG(0,t);
k = ARG(1,t);
lo = ARG(2,t);
if(!numerical(lo) || !ISATOM(k))
return 0;
x = get_eigenvariable();
if(equals(x,k))
return 0; /* e.g., when the series contains no variable other than k */
twoparts(u,x,&c,&v);
if(FUNCTOR(v) != '^')
return 0;
if(!equals(ARG(0,v),x))
return 0;
m = ARG(1,v);
oldval = VALUE(k);
deval(lo,&loval);
SETVALUE(k,loval);
err = deval(m,&exp);
SETVALUE(k,oldval);
if(err)
return 0;
if(exp < 0)
return 1;
return 0;
}
/*_________________________________________________________________________________*/
void series_ops(term t, actualop *o, int *nops)
/* called by post_ops to get operators to apply to an infinite series */
{ int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
int pathlength = get_pathlength();
unsigned short *path = get_path();
int i = 0;
unsigned short g;
term v,w,u,k,x,c;
int k2;
u = ARG(0,t);
if( problemtype == POWERSERIES &&
( pathlength <=1 || (pathlength==3 && path[0] == '-'))
)
{ // don't leave the exponent as x^(k+3)
k = ARG(1,t);
u = ARG(0,t);
x = get_eigenvariable();
twoparts(u,x,&c,&v);
if(FUNCTOR(v) == '^' && equals(ARG(0,v),x))
{ w = ARG(1,v);
if(FUNCTOR(w) == '+' && ARITY(w) == 2 && equals(ARG(0,w),k))
{ if(ISINTEGER(ARG(1,w)))
{ o[i] =seriessubindex; ++i;}
else if(NEGATIVE(ARG(1,w)) && ISINTEGER(ARG(0,ARG(1,w))))
{ o[i] = seriesaddindex; ++i;}
}
if(FUNCTOR(w) == '+' && ARITY(w) == 2 && FUNCTOR(ARG(0,w)) == '*' && equals(ARG(1,ARG(0,w)),k))
{ long a,b;
if(ISINTEGER(ARG(0,ARG(0,w))) && ISINTEGER(ARG(1,w)))
{ a = INTDATA(ARG(0,ARG(0,w)));
b = INTDATA(ARG(1,w));
if(b % a == 0)
{ o[i] = seriessubindex;++i;
}
}
if(ISINTEGER(ARG(0,ARG(0,w))) && NEGATIVE(ARG(1,w)) && ISINTEGER(ARG(0,ARG(1,w))))
{ a = INTDATA(ARG(0,ARG(0,w)));
b = INTDATA(ARG(0,ARG(1,w)));
if(b %a == 0)
{ o[i] = seriesaddindex; ++i;
}
}
}
}
}
if(FUNCTOR(t) != SUM || !equals(ARG(3,t),infinity))
{ *nops = 0;
return;
}
if(currenttopic != _integral_test)
{ if(FRACTION(u) || (FUNCTOR(u) == '^' && equals(ARG(1,u),minusone)))
{ o[i] = harmonicseries; ++i;
}
if(FRACTION(u) && FUNCTOR(ARG(0,u)) == '^' && equals(ARG(1,u),ARG(1,t)))
{ o[i] = alternatingharmonicseries; ++i;
}
if(FRACTION(u) && ONE(ARG(0,u)) && FUNCTOR(ARG(1,u)) == '^' &&
equals(ARG(0,ARG(1,u)),ARG(1,t))
)
{ if(equals(ARG(1,ARG(1,u)),two))
{ o[i] = zeta2; ++i;
}
else
{ o[i] = addtozeta; ++i;
}
}
}
if(problemtype == ADDSERIES || problemtype == TESTCONVERGENCE)
/* an infinite sum and we're supposed to add it up */
{ g = FUNCTOR(u);
switch(g)
{ case '+' :
o[i] = seriessum; ++i;
o[i] = sigmapoly; ++i;
break;
case '*' : /* fall-through */
case '/' :
o[i] = constantoutofsigma; ++i;
o[i] = sigmapoly; ++i;
if( FUNCTOR(ARG(0,u)) == '^' && equals(ARG(1,u),ARG(1,t)))
{ o[i] = alternatingharmonicseries; ++i;
o[i] = lnseriesrev;++i;
}
}
}
if(problemtype == TESTCONVERGENCE && ( get_pathlength() == 1 || ok_convergence_test() ) ) // only do this at toplevel
{ convergence_tests(t,o+i,&k2);
i += k2;
}
if(problemtype == POWERSERIES)
{ if(negativeleadingterm(t))
{ o[i] = seriesfirstterms; ++i;
}
o[i] = constantoutofsigma; ++i;
}
*nops = i;
}
/*______________________________________________________________________________*/
const char * series_fremark(term t, int flag)
/* called by fremark to test whether we're finished or not.
if flag == 0 it means MathXpert's automode can't do anything more.
If not, return a message. If can't reject the answer,
return NULL.
*/
{ unsigned short f;
if(used4(divergencetest))
return english(2300);
/* You started the divergence test, but didn't finish it yet. */
if(used4(integraltest))
return english(2301);
/* You started the integral test, but didn't finish it yet. */
if(used4(comparisontest1) || used4(comparisontest2))
return english(2302);
/* You started the comparison test, but didn't finish it yet. */
if(used4(limitcomparisontest))
return english(2303);
/* You started the limit comparison test, but didn't finish it yet. */
if(used4(condensationtest))
return english(2304);
/* You started the condensation test, but didn't finish it yet. */
if(used4(roottest))
return english(2305);
/* You started the root test, but didn't finish it yet. */
if(used4(ratiotest))
return english(2306);
/* You started the ratio test, but didn't finish it yet. */
if(used4(finishcomparisontest1) || used4(finishcomparisontest2))
return english(2404);
f = FUNCTOR(t);
if(f == SUM && CONVERGENT(t))
return NULL; /* CONVERGENT means the question of convergence is settled. */
if(!INEQUALITY(f))
{ /* Other than a series with CONVERGENT set,
acceptable answers are, where u is the original series,
of the form u < infinity, u < some estimate,
u = infinity, u = undefined, u = minusinfinity, etc.
*/
if(flag)
return english(2307);
/* The convergence or divergence is not yet settled. */
else
return english(2515);
/* MathXpert cannot decide if this series converges or not. */
}
return NULL;
}
/*________________________________________________________________________________*/
static int series_index(actualop code)
/* return one of the identifiers at the top of this file to identify this series
operation. Code is assumed to be one of the operations on the
series_convergence_tests or series_convergence2 menu. Return -1
if the operation is not found.
*/
{ if( (void *) code == (void *) divergencetest)
return DIVERGENCETEST;
if( (void *) code == (void *) integraltest)
return INTEGRALTEST;
if( (void *) code == (void *) ratiotest)
return RATIOTEST;
if( (void *) code == (void *) roottest)
return ROOTTEST;
if( (void *) code == (void *) comparisontest1)
return COMPARISONTEST1;
if( (void *) code == (void *) comparisontest2)
return COMPARISONTEST2;
if( (void *) code == (void *) limitcomparisontest)
return LIMITCOMPARISONTEST;
if( (void *) code == (void *) condensationtest)
return CONDENSATIONTEST;
if( (void *) code == (void *) finishdivergencetest)
return FINISHDIVERGENCETEST;
if( (void *) code == (void *) finishintegraltest)
return FINISHINTEGRALTEST;
if( (void *) code == (void *) finishratiotest)
return FINISHRATIOTEST;
if( (void *) code == (void *) finishroottest)
return FINISHROOTTEST;
if( (void *) code == (void *) finishcomparisontest1)
return FINISHCOMPARISONTEST1;
if( (void *) code == (void *) finishcomparisontest2)
return FINISHCOMPARISONTEST2;
if( (void *) code == (void *) finishlimitcomparisontest)
return FINISHLIMITCOMPARISONTEST;
if( (void *) code == (void *) finishcondensationtest)
return FINISHCONDENSATIONTEST;
if( (void *) code == (void *) statefinalbound1)
return STATEFINALBOUND1;
if( (void *) code == (void *) statefinalbound2)
return STATEFINALBOUND2;
if( (void *) code == (void *) harmonicseries)
return HARMONICSERIES;
if( (void *) code == (void *) zeta2)
return ZETA2;
if( (void *) code == (void *) addtozeta)
return ADDTOZETA;
return -1;
}
/*________________________________________________________________________________*/
static int closer_index(int index)
/* if index is the identifier of a test-closing operation, return the
index of the corresponding open-test operation. Return -1 otherwise.
*/
{ switch(index)
{ case FINISHDIVERGENCETEST:
return DIVERGENCETEST;
case FINISHINTEGRALTEST:
return INTEGRALTEST;
case FINISHRATIOTEST:
return RATIOTEST;
case FINISHROOTTEST:
return ROOTTEST;
case FINISHCOMPARISONTEST1:
return COMPARISONTEST1;
case FINISHCOMPARISONTEST2:
return COMPARISONTEST2;
case FINISHLIMITCOMPARISONTEST:
return LIMITCOMPARISONTEST;
case FINISHCONDENSATIONTEST:
return CONDENSATIONTEST;
case STATEFINALBOUND1:
return FINISHCOMPARISONTEST1;
case STATEFINALBOUND2:
return FINISHCOMPARISONTEST2;
}
return -1;
}
/*________________________________________________________________________________*/
int used4(actualop op)
/* return the line at which convergence test 'op'
most recently been used, and its corresponding finish operation
has not been used, and every test opened since then has been closed.
Return 0 if there's no such line.
*/
{ int i,m,currentline;
int flags[32];
int index, closer;
controldata cd;
operation *opseq;
actualop code;
get_controldata(&cd);
currentline = get_currentline();
opseq = cd.opseq;
index = series_index(op);
if(index < 0)
return 0; /* assert(0); should not be called in this case */
if(index > NCONVERGENCETESTS)
return 0;
memset(flags,0,32 * sizeof(int));
/* flags will keep track of which convergence tests are open */
for(i=currentline;i>0; i--)
{ if(opseq[i].men != series_convergence_tests &&
opseq[i].men != series_convergence2
)
continue;
code = access_optable(opseq[i].men)[opseq[i].choice-1];
m = series_index(code);
if(m < 0)
assert(0); /* series_index must list all operations on those two menus */
/* m will be one of the identifiers in series.h */
if(flags[m])
{ --flags[m]; /* an open corresponding to a close we passed already */
continue;
}
if(m == index)
return i; /* success */
closer = closer_index(m);
if(closer > 0)
{ /* closing a test */
++flags[closer];
closer = closer_index(closer);
/* for example, finishcomparisontest1 is both an opener for
statefinalbound1 and a closer for comparisontest1. */
if(closer > 0)
++flags[closer];
}
}
return 0;
}
/*________________________________________________________________________________*/
static actualop finisher(actualop op)
{ if(op == comparisontest1) return finishcomparisontest1;
if(op == comparisontest2) return finishcomparisontest2;
if(op == integraltest) return finishintegraltest;
if(op == ratiotest) return finishratiotest;
if(op == roottest) return finishroottest;
if(op == limitcomparisontest) return finishlimitcomparisontest;
if(op == condensationtest) return finishcondensationtest;
else
return NULL; // assert(0);
}
static int used(actualop op)
{ return used3(finisher(op),get_currentline());
}
/*_______________________________________________________________________________ */
int used3(actualop op, int currentline)
/* return the line at which convergence test 'op'
most recently been, starting from the given line 'currentline' and
working backward. Return 0 if the operation hasn't been used.
*/
{ int i;
controldata cd;
operation *opseq;
actualop code;
get_controldata(&cd);
opseq = cd.opseq;
for(i=currentline;i>0; i--)
{ if(opseq[i].men != series_convergence_tests &&
opseq[i].men != series_convergence2
)
continue;
code = access_optable(opseq[i].men)[opseq[i].choice-1];
if((void *) code == (void *) op)
return i;
}
return 0;
}
/*_____________________________________________________________________________*/
void series_toplevel(term t, actualop *o, int *nops)
/* called by postops and by selectops to add operations that apply at toplevel
when the problemtype is TESTCONVERGENCE, but the current
expression has no sums, limits, or integrals.
*/
{ int i = 0;
int err;
term dummy,temp ;
char buffer[MAXREASONSTRING];
SETFUNCTOR(dummy, ILLEGAL,0);
if(used4(integraltest))
{ o[i] = finishintegraltest; ++i;
}
else if(used4(roottest))
{ o[i] = finishroottest; ++i;
}
else if(used4(ratiotest))
{ o[i] = finishratiotest; ++i;
}
else if(used4(comparisontest1))
{ o[i] = finishcomparisontest1; ++i;
}
else if(used4(comparisontest2))
{ o[i] = finishcomparisontest2; ++i;
}
else if(used4(finishcomparisontest1))
{ o[i] = statefinalbound1; ++i;
}
else if(used4(finishcomparisontest2))
{ o[i] = statefinalbound2; ++i;
}
else if(used4(divergencetest))
{ o[i] = finishdivergencetest; ++i;
}
else if(used4(limitcomparisontest))
{ o[i] = finishlimitcomparisontest; ++i;
}
else if(used4(condensationtest))
{ o[i] = finishcondensationtest; ++i;
}
err = polyvalop(t,dummy,&temp,buffer);
if(!err)
{ o[i] = polyvalop; ++i;
}
*nops = i;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists