Sindbad~EG File Manager
/* M. Beeson, basic limit operators
Original date 1.14.91
3.23.99 last modified
12.30.99 deleted two lines from limcos2
1.2.00 modified limcontinuous to reject SUM and PRODUCT limitands
2.25.00 corrected limexp2
7.16.00 added SetShowStepOperation calls in osc_aux
9.5.04 deleted superfluous call to vaux in changelimitvariable
2.9.05 modified includes
3.19.23 added return 1 in expundefined
3.11.24 used display math for reason in limexptolog
11.14.24 also in limlog
2.15.25 modified limcontinuous to fail quickly if uofa is seminumerical and has no value.
*/
#include <string.h>
#include <assert.h>
#include <math.h>
#include "globals.h"
#include "ops.h"
#include "trig.h"
#include "calc.h"
#include "prover.h"
#include "order.h"
#include "checkarg.h"
#include "cancel.h"
#include "factor.h"
#include "autosub.h"
#include "algaux.h"
#include "mplimits.h"
#include "deriv.h"
#include "graphstr.h"
#include "mpdoc.h"
#include "automode.h"
#include "deval.h"
#include "symbols.h"
#include "mathmode.h" /* set_substitutionflag */
#include "pvalaux.h" /* is_linear_in */
#include "solvelin.h" /* solve_linear_ineq_for */
#include "probtype.h"
#include "psubst.h"
#include "errbuf.h"
#include "limval2.h"
#include "polynoms.h" /* polyform */
#include "autosimp.h" /* SetShowStepOperation */
#include "maxsub.h"
#include "dispfunc.h"
#include "trigpoly.h" /* algpoly */
#include "mpmem.h"
#include "mstring.h"
static int osc_aux(unsigned short, term, term *, char *);
static int finite_limit(term u, term x, term a);
static int linear_exponent(term u, term x, term *ans);
static int specialcase_sub(term u, term x, term a, term *ans);
static int select_limprod2_arg(int *flag, term t, term *ans);
/*____________________________________________________________________*/
int limlog(term t, term arg, term *next, char *reason)
/* lim u = e^(lim ln u) */
{ term u;
if(FUNCTOR(t) != LIMIT)
return 1;
u = LIMITAND(t);
if(ARITY(t)==2)
*next = make_power(eulere,limit(ARG(0,t),ln1(u)));
else
*next = make_power(eulere,limit3(ARG(0,t),ARG(1,t),ln1(u)));
HIGHLIGHT(*next);
strcpy(reason, "$$lim(t->a,u) = e^(lim(t->a, ln u))$$");
return 0;
}
/*___________________________________________________________*/
int squeezetheorem(term t, term arg, term *next, char *reason)
{ term u;
int err;
if(FUNCTOR(t) != LIMIT)
return 1;
u = LIMITAND(t);
if(FUNCTOR(u) != '*' && FUNCTOR(u) != '/')
return 1;
if(!contains(u,SIN) && !contains(u,COS))
return 1;
err = squeeze_theorem_aux(t,next);
if(err)
{ errbuf(0, english(903));
/* Can't verify hypotheses of squeeze theorem. */
return 1;
}
strcpy(reason, english(904)); /* squeeze theorem */
HIGHLIGHT(*next);
commentbuf(0, english(1994));
/* !provided this limit turns out to be zero. */
return 0;
}
/*__________________________________________________________________*/
int pulloutnonzerolimit(term t, term arg, term *next, char *reason)
/* "factor out nonzero finite limits" */
/* in auto mode, it is selected automatically */
{ unsigned short n;
int err;
term p,q,temp,mid,cancelled;
if(FUNCTOR(t) != LIMIT)
return 1;
n = ARITY(t);
p = LIMITAND(t);
if(FUNCTOR(p) != '/')
return 1;
if(FUNCTOR(arg)==ILLEGAL)
{ err = select_pnzarg(t,&arg,&mid);
if(err)
return 1;
}
else /* arg has been entered by user */
{ if(FUNCTOR(arg)=='/') /* check if the arg entered really divides the limitand */
{ err = cancel(product(ARG(0,p),ARG(1,arg)),product(ARG(1,p),ARG(0,arg)),&cancelled,&mid);
if(err)
return 1;
}
else
{ err = cancel(ARG(0,p),product(ARG(1,p),arg),&cancelled,&mid);
if(err)
return 1;
}
/* ok, it does divide the limitand */
/* Now check if its limit is defined and nonzero */
err = limval( n==2 ? limit(ARG(0,t),arg) : limit3(ARG(0,t),ARG(1,t),arg),&temp);
if(!err && ZERO(temp))
{ errbuf(0, english(905));
/* Suggested limit is zero, so that doesn't work. */
return 1;
}
if(!err && NOTDEFINED(temp))
{ char buffer[128];
strcpy(buffer,english(906));
/* Suggested limit is */
strcat(buffer, ISINFINITE(temp) ? english(907): english(581));
/* infinite : undefined */
errbuf(0,buffer);
return 1;
}
if(err)
{ errbuf(0, english(908));
/* Can't verify suggested limit is defined and not zero. */
return 1;
}
}
if(ONE(mid) || ONE(arg))
return 1; /* else a loop will result */
/* Ok, the arg meets all the conditions */
q = n==2 ? limit(ARG(0,t),mid) : limit3(ARG(0,t),ARG(1,t),mid);
p = n==2 ? limit(ARG(0,t),arg) : limit3(ARG(0,t),ARG(1,t),arg);
*next = product(p,q);
HIGHLIGHT(*next);
strcpy(reason,"lim(uv) = lim u lim v");
return 0;
}
/*_________________________________________________________*/
int defnofe(term t, term arg, term *next, char *reason)
/* lim(x->0,(1+x)^1/x) = e */
/* also accept (x+1)^(1/x) and lim(n->infinity, (1 + 1/n)^n */
/* also evaluate either one-sided limit */
{ term u,x,a,b,temp;
int flag=0,err;
if(FUNCTOR(t) != LIMIT)
return 1;
u = LIMITAND(t);
x = ARG(0,ARG(0,t));
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
err = unify1(make_power(sum(one,var0),make_fraction(one,var0)),u,&a,&flag);
if(err)
err = unify1(make_power(sum(var0,one),make_fraction(one,var0)),u,&a,&flag);
if(err)
return 1;
if(! equals(a,x)) /* try harder, e.g. lim(x->0,(1+x/h)^(1/(x/h))) */
{ int r = psubst(zero,x,a,&temp);
if(r <= 1) /* r = 0 is error, r = 1 is fractional exponent created */
return 1;
polyval(temp,&b);
if(!ZERO(b)) /* then a goes to zero as x does */
return 1;
}
*next = eulere;
HIGHLIGHT(*next);
strcpy(reason, english(909)); /* definition of e */
release(attractlns); /* possibly inhibited by lnofpowerreverse */
return 0;
}
/*_________________________________________________________*/
int changelimitvariable(term t, term arg, term *next, char *reason)
/* lim(x->a,f(g(x))) = lim(u->g(a),f(u)) */
/* u = g(x) is passed in arg */
{ term u,x,w,a,b,New,temp,leftcopy,rightcopy;
unsigned short n,f;
int sign,saveit;
char localbuf[81];
int r,err;
term xrecip;
int nvariables;
approach ll,rr;
if(FUNCTOR(t) != LIMIT)
return 1;
nvariables = get_nvariables();
w = LIMITAND(t);
f = FUNCTOR(w);
n = ARITY(t);
x = ARG(0,ARG(0,t));
a = ARG(1,ARG(0,t)); /* t is lim(x->a,w) */
if(FUNCTOR(arg)==ILLEGAL) /* called in auto mode */
{ /* Example 1, (x-16)/(x^(1/4)-2) where we want u = x^(1/4) */
/* Example 2, e^(1/x)/x where we want u = 1/x */
/* Example 3, lim(x->infinity,cos(e^-x)) where we want u = e^-x */
/* Example 4, lim(h->0,(1+(h/x))^(x/h) which arises in differentiating
ln x from the defn of derivative; we want u = h/x */
/* Example 5, lim(h->0, e^((ln a)h -1)/h), which arises in differentiating
a^x from defn; we want u = (ln a)h */
if(ISINFINITE(a) &&
TRIGFUNCTOR(f) &&
finite_limit(ARG(0,w),x,a)
)
{ u = getnewvar(t,"uvwpqrstxy");
vaux(u);
arg = equation(u,ARG(0,w));
err = changelimitvariable(t,arg,next,reason);
if(err)
{ set_nvariables(nvariables);
return 1;
}
SetShowStepArg(ARG(1,arg));
return 0;
}
if(ISINFINITE(a) && subterm(reciprocal(x),w))
{ u = getnewvar(t,"uvwpqrstxy");
vaux(u);
xrecip = reciprocal(x);
arg = equation(u,xrecip);
err = changelimitvariable(t,arg,next,reason);
if(err)
{ set_nvariables(nvariables);
return 1;
}
SetShowStepArg(xrecip);
return 0;
}
if(!specialcase_sub(w,x,a,&temp))
{ u = getnewvar(t,"uvwpqrstxy");
arg = equation(u,temp);
err = changelimitvariable(t,arg,next,reason);
if(!err)
{ SetShowStepArg(temp);
return 0;
}
}
if(ARITY(t) == 3 &&
contains_etonegpower(w,x) && /* handle Example 2 */
!contains(w,COT) && /* don't use it on (e^(1/x)-1)/cot x */
!contains(w,TAN) &&
!contains(w,SEC) &&
!contains(w,CSC) /* but SIN and COS are OK */
)
{ u = getnewvar(t,"uvwpqrstxy");
vaux(u);
set_valuepointers(&u);
xrecip = reciprocal(x);
arg = equation(u,xrecip);
err = changelimitvariable(t,arg,next,reason);
if(!err)
{ SetShowStepArg(xrecip);
return 0;
}
else
set_nvariables(nvariables);
/* but not 'return 1'. Go on and try autosub. */
}
saveit = get_substitutionflag();
set_substitutionflag(VISIBLESUBS);
err = autosub(w,&arg,&New);
set_substitutionflag(saveit);
if(!err && ISATOM(ARG(0,arg)))
{ err = changelimitvariable(t,arg,next,reason);
if(!err)
{ SetShowStepArg(ARG(1,arg));
return 0;
}
}
set_nvariables(nvariables);
/* Now handle example 5, which autosub misses because there's only
one occurrence of (ln a)h. */
if(!linear_exponent(w,x,&temp) &&
!(NEGATIVE(temp) && equals(ARG(0,temp),x)) &&
/* don't generate u = -x, e.g. in lim(x->infinity, sin(1/x)/e^(-x)) */
!equals(temp,x)
)
{ u = getnewvar(t,"uvwpqrstxy");
vaux(u);
arg = equation(u,temp);
err = changelimitvariable(t,arg,next,reason);
if(!err)
{ SetShowStepArg(temp);
return 0;
}
else
set_nvariables(nvariables);
}
err = maximal_sub(w,&temp);
if(!err &&
( algpoly(temp) || /* otherwise it's too big a step */
(TRIGFUNCTOR(FUNCTOR(temp)) && ispolyin(ARG(0,temp),x))
)
)
{ u = getnewvar(t,"uvwpqrstxy");
vaux(u);
arg = equation(u,temp);
err = changelimitvariable(t,arg,next,reason);
if(!err)
{ SetShowStepArg(temp);
return 0;
}
else
set_nvariables(nvariables);
}
return 1; /* automode could not find a substitution */
}
else /* menu mode */
{ if(FUNCTOR(arg) != '=')
{ if(equals(arg,x))
return 1;
u = getnewvar(t,"uvwpqrstxy");
if(FUNCTOR(u) == ILLEGAL)
{ errbuf(0, english(1448));
/* Too many subscripted variables, can't make more. */
return 1;
}
vaux(u);
set_valuepointers(&u);
arg = equation(u,arg);
err = changelimitvariable(t,arg,next,reason);
if(err)
{ set_nvariables(nvariables);
return 1;
}
return 0;
}
assert(FUNCTOR(arg) == '=' && ISATOM(ARG(0,arg)));
if(equals(ARG(1,arg),x))
return 1;
u = ARG(0,arg);
vaux(u); /* add it to varlist */
set_valuepointers(&u);
r = psubst(u,ARG(1,arg),w,&temp);
err = 0;
if(r==0)
err = 1;
else
{ polyval(temp,&New);
if(contains(New,FUNCTOR(x)))
err = 1;
}
if(err)
{ char buffer[128];
strcpy(buffer, english(692));
/* That substitution won't eliminate */
strcat(buffer,atom_string(x));
strcat(buffer,".");
errbuf(0,buffer);
set_nvariables(nvariables);
return 1;
}
}
if(n==3)
{ /* the substitution must be monotone */
term deriv = derivative(ARG(1,arg),x);
err = infer(lessthan(zero,deriv));
if(!err)
sign = 1;
else
{ err = infer(lessthan(deriv,zero));
if(!err)
sign = -1;
else
sign = 0;
}
if(sign == 0)
{ errbuf(0, english(910));
/* The substitution must be a monotonic function */
set_nvariables(nvariables);
return 1;
}
}
if(ISINFINITE(a))
{ err = limval_aux(0,limit(ARG(0,t),ARG(1,arg)),&b,&ll,&rr);
if(err)
return 1;
if(NOTDEFINED(b) && !ISINFINITE(b))
return 1;
}
else
{ subst(a,x,ARG(1,arg),&temp);
polyval(temp,&b);
}
if(!ISINFINITE(a) &&
(
(FRACTION(b) && ZERO(ARG(1,b))) ||
ISINFINITE(b) ||
infer(domain(b))
)
)
{ /* a new limit at infinity is being created */
if(n == 2)
{ errbuf(0, english(1380));
/* Transforming a two-sided limit to a limit at infinity is illegal. */
set_nvariables(nvariables);
return 1;
}
/* Now it's a one-sided limit, which is legal to transform
to infinity; but should b be infinity or minusinfinity? */
if( (sign < 0 && FUNCTOR(ARG(1,t))==RIGHT) ||
(sign > 0 && FUNCTOR(ARG(1,t))==LEFT)
)
*next = limit(arrow(u,infinity),New);
else
*next = limit(arrow(u,minusinfinity),New);
}
else if(ISINFINITE(a) && !ISINFINITE(b))
{ /* limit at infinity being transformed to finite limit */
/* Is it a right-hand or a left-hand limit? */
if(equals(a,infinity))
{ if(ll == max)
*next = limit3(arrow(u,b),left,New);
else if(ll == min)
*next = limit3(arrow(u,b),right,New);
else
*next = limit(arrow(u,b),New);
}
else if(equals(a,minusinfinity))
{ if(rr == max)
*next = limit3(arrow(u,b),left,New);
else if(rr == min)
*next = limit3(arrow(u,b),right,New);
else
*next = limit(arrow(u,b),New);
}
else
assert(0);
}
else if(n == 2)
*next = limit(arrow(u,b),New);
else if(n == 3 && sign > 0)
*next = limit3(arrow(u,b),ARG(1,t),New);
else if(n == 3 && sign < 0)
*next = limit3(arrow(u,b), equals(ARG(1,t),left)? right : left, New);
else
return 1; /* assert(0) */
HIGHLIGHT(*next);
err = mstring(arg,localbuf);
if(err || strlen(localbuf) > MAXREASONSTRING)
strcpy(reason, english(911)); /* change limit variable */
else
strcpy(reason,localbuf);
permcopy(u,&leftcopy);
permcopy(ARG(1,arg),&rightcopy);
let(leftcopy,rightcopy);
SETDEPENDENT(u);
release(limquotient); /* these may have been inhibited by multnumdenom */
release(pulloutnonzerolimit);
release(lhopital);
release(limlinear);
return 0;
}
/*________________________________________________________________*/
int limlogisloglim(term t, term arg, term *next, char *reason)
{ unsigned short n = ARITY(t);
unsigned short f;
term u,a;
if(FUNCTOR(t) != LIMIT)
return 1;
a = ARG(1,ARG(0,t));
if(NOTDEFINED(a) && !equals(a,infinity))
return 1; /* but it is ok for limits at infinity */
u = LIMITAND(t);
if(FUNCTOR(u) != LN && FUNCTOR(u) != LOG && FUNCTOR(u) != LOGB)
return 1;
f = FUNCTOR(u);
if(f == LN)
*next = n==2 ? ln1(limit(ARG(0,t),ARG(0,u))) : ln1(limit3(ARG(0,t),ARG(1,t),ARG(0,u)));
else if(f == LOG)
*next = n==2 ? log1(limit(ARG(0,t),ARG(0,u))) : log1(limit3(ARG(0,t),ARG(1,t),ARG(0,u)));
else if(f == LOGB)
*next = n==2 ? logb1(ARG(0,u),limit(ARG(0,t),ARG(1,u))) : logb1(ARG(0,u),limit3(ARG(0,t),ARG(1,t),ARG(1,u)));
HIGHLIGHT(*next);
strcpy(reason, f == LN ? "ln" : "log");
strcat(reason, english(912)); /* is continuous */
return 0;
}
/*_________________________________________________________________*/
int limundefined(term t, term arg, term *next, char *reason)
/* recognize of lim(x->a,u) that u isn't defined in a punctured
neighborhood of a and hence the limit is undefined. Similarly
for one-sided limits */
{ term u,x,a;
int err,dir;
short savenextassumption;
if (FUNCTOR(t) != LIMIT)
return 1;
assert(FUNCTOR(ARG(0,t))==ARROW);
x = ARG(0,ARG(0,t));
a = ARG(1,ARG(0,t));
if(ARITY(t) == 3) /* one-sided limit */
{ u = ARG(2,t);
err = refute(domain(u));
if(err)
return 1;
dir = (int) INTDATA(ARG(1,t));
}
/* But if it's a two-sided limit, you must consider both
one-sided limits separately. For example, lim(x->0, sqrt x)
is undefined. To refute(dom(sqrt(x)), that is, refute(x>0),
we must have the binders set for a one-sided limit from the left.
The refutation will fail if we only are set up for a two-sided limit.
True, 'infer(dom(sqrt x))' will fail, but that may be due to the weakness of
the prover in general; we need a refutation.
Generally the binders and locus machinery are not touched by the
mathematical operators in MathXpert. Instead we make an additional
assumption about the limit variable.
*/
else
{
assert(ARITY(t) == 2);
u = ARG(1,t);
dir = CENTERED;
savenextassumption = get_nextassumption();
/* First check the limit from the left */
assume(lessthan(x,a));
err = refute(domain(u));
set_nextassumption(savenextassumption);
if(err)
{ assume(lessthan(a,x)); /* try the right-handed limit */
err = refute(domain(u));
set_nextassumption(savenextassumption);
if(err)
return 1; /* can't refute it on either side */
}
}
/* If we get here, it's undefined on one side or the other */
*next = undefined;
HIGHLIGHT(*next);
strcpy(reason, english(913)); /* function not defined */
if(dir == CENTERED)
strcat(reason, english(914)); /* in neighorhood */
else if (dir == RIGHTDIR)
strcat(reason, english(915)); /* on the right */
else if (dir == LEFTDIR)
strcat(reason, english(916)); /* on the left */
return 0;
}
/*_________________________________________________________________*/
int expundefined(term t, term arg, term *next, char *reason)
/* succeed if t is a quotient of limits that evaluate to 0/0 or
\pm infinity / \pm infinity */
{ term u,v,ans1,ans2;
if(FUNCTOR(t) != '/')
return 1;
u = ARG(0,t);
v = ARG(1,t);
if(FUNCTOR(u)!= LIMIT)
return 1;
if(FUNCTOR(v)!= LIMIT)
return 1;
limval(v,&ans1);
if(equals(v,zero))
{ limval(u,&ans2);
if(!ISZERO(ans2))
{ errbuf(0, english(917));
/* Can't prove that the numerator is zero */
return 1;
}
goto out;
}
if(equals(v,infinity) || equals(v,minusinfinity))
{ limval(u,&ans2);
if(!equals(ans2,infinity) && !equals(ans2,minusinfinity))
{ errbuf(0, english(918));
/* Can't prove that the numerator is infinite */
return 1;
}
goto out;
}
return 1;
out: *next = make_fraction(ans1,ans2);
HIGHLIGHT(*next);
return 0;
}
/*_________________________________________________________________*/
int limsin1(term t, term arg, term *next, char *reason)
/* (sin x)/x ->1 as x->0 */
/* also does x/sin(x) */
{ term x,w,num, denom;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(num) != SIN && FUNCTOR(denom) != SIN)
return 1;
if(FUNCTOR(num) == SIN)
{ if( !equals(x,ARG(0,num)) || !equals(x,denom))
return 1;
}
else /* FUNCTOR(denom) is SIN */
{ if( !equals(x,ARG(0,denom)) || !equals(x,num))
return 1;
}
*next = one;
HIGHLIGHT(*next);
strcpy(reason, "$$lim(x->0,sin(x)/x) = 1$$");
return 0;
}
/*_________________________________________________________________*/
int limtan1(term t, term arg, term *next, char *reason)
/* (tan x)/x ->1 as x->0 */
{ term x,w,num, denom;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(num) != TAN && FUNCTOR(denom) != TAN)
return 1;
if(FUNCTOR(num) == TAN)
{ if( !equals(x,ARG(0,num)) || !equals(x,denom))
return 1;
}
else /* FUNCTOR(denom) is TAN */
{ if( !equals(x,ARG(0,denom)) || !equals(x,num))
return 1;
}
*next = one;
HIGHLIGHT(*next);
strcpy(reason,"$$lim(x->0,tan(x)/x)= 1$$");
return 0;
}
/*_________________________________________________________________*/
int limcos1(term t, term arg, term *next, char *reason)
/* (1-cos x)/x ->0 as x->0 */
/* or (cos x -1)/x ->0 as x->0 */
{ term x,u,w;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(! equals(x,ARG(1,w)))
return 1;
u = ARG(0,w);
if(FUNCTOR(u) != '+')
return 1;
if(ARITY(u) > 2)
return 1;
if(
(ONE(ARG(0,u)) && FUNCTOR(ARG(1,u))=='-' &&
FUNCTOR(ARG(0,ARG(1,u))) == COS && equals(ARG(0,ARG(0,ARG(1,u))),x)
) ||
(ONE(ARG(1,u)) && FUNCTOR(ARG(0,u)) == '-' &&
FUNCTOR(ARG(0,ARG(0,u))) == COS && equals(ARG(0,ARG(0,ARG(0,u))),x)
)
)
{ *next = zero;
HIGHLIGHT(*next);
strcpy(reason,"$$lim(x->0,(1-cos x)/x) = 0$$");
return 0;
}
if(
( FUNCTOR(ARG(0,u)) == COS && equals(ARG(0,ARG(0,u)),x) &&
equals(ARG(1,u),minusone)
) ||
( equals(ARG(0,u),minusone) && FUNCTOR(ARG(1,u)) == COS &&
equals(ARG(0,ARG(1,u)),x)
)
)
{ *next = zero;
HIGHLIGHT(*next);
strcpy(reason,"$$lim(x->0,(cos(x)-1)/x) = 0$$");
return 0;
}
return 1;
}
/*_________________________________________________________________*/
int limcos2(term t, term arg, term *next, char *reason)
/* (1-cos x)/x^2 -> 1/2 as x->0 */
/* or (cos x - 1)/x^2 -> - 1/2 as x->0 */
{ term x,u,w;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(ARG(1,w)) != '^')
return 1;
if( !equals(ARG(1,ARG(1,w)),two))
return 1;
if( !equals(ARG(0,ARG(1,w)),x))
return 1;
u = ARG(0,w);
if(FUNCTOR(u) != '+')
return 1;
if(ARITY(u) > 2)
return 1;
if(
(ONE(ARG(0,u)) && FUNCTOR(ARG(1,u))=='-' &&
FUNCTOR(ARG(0,ARG(1,u))) == COS && equals(ARG(0,ARG(0,ARG(1,u))),x)
) ||
(ONE(ARG(1,u)) && FUNCTOR(ARG(0,u)) == '-' &&
FUNCTOR(ARG(0,ARG(0,u))) == COS && equals(ARG(0,ARG(0,ARG(0,u))),x)
)
)
{ *next = make_fraction(one,two);
goto out;
}
if(
( FUNCTOR(ARG(0,u)) == COS && equals(ARG(0,ARG(0,u)),x) &&
equals(ARG(1,u),minusone)
) ||
( equals(ARG(0,u),minusone) && FUNCTOR(ARG(1,u)) == COS &&
equals(ARG(0,ARG(1,u)),x)
)
)
{ *next = tnegate(make_fraction(one,two));
goto out;
}
return 1;
out:
HIGHLIGHT(*next);
strcpy(reason,"$$lim(x->0,(1-cos x)/x^2) = 1/2$$");
return 0;
}
/*_________________________________________________________________*/
int limcontinuous(term t, term arg, term *next, char *reason)
/* evaluate a limit lim(h->a,u) where u is continuous at a to u[a/h]
*/
{ int err;
double z;
long kk;
int saveit = get_polyvalfunctionflag();
term u,h,a,uofa,testdif,s;
unsigned short f;
if(FUNCTOR(t) != LIMIT)
return 1;
h = ARG(0,ARG(0,t));
a = ARG(1,ARG(0,t));
if(NOTDEFINED(a))
return 1; /* don't use this to say lim(x->infinity,e^x) = e^infinity */
u = LIMITAND(t);
if(ATOMIC(u))
{ if(get_mathmode() == AUTOMODE)
return 1; /* don't work on atoms or numbers in auto mode */
}
subst(a,h,u,&uofa);
if(seminumerical(uofa))
{ double x;
int err = deval(uofa,&x);
if(err || x == BADVAL)
return 1; // operation fails without the prover
}
*next = uofa; /* If there's an answer, this is it */
HIGHLIGHT(*next);
f = FUNCTOR(u);
if(f == SUM || f == PRODUCT)
return 1;
if(!entire(u))
{ err = infer(domain(uofa));
if(err)
{ errbuf(0, english(1413));
errbuf(1, english(1414));
/* Limitand is not defined at the limit point,
and is therefore not continuous at the limit point. */
return 1; /* can't even show the limitand is defined at a */
}
set_polyvalfunctionflag(1);
polyval(sum(u,strongnegate(uofa)),&testdif);
set_polyvalfunctionflag(saveit);
err = stdpartonly(testdif,&s);
if(err)
return 1; /* can't compute standard part */
if(!ZERO(s))
{ if(seminumerical(s)) /* maybe it REALLY is zero, but polyval didn't evaluate it to zero */
{ deval(s,&z);
if(z==BADVAL)
return 1;
if(nearint(z,&kk) && kk == 0)
return 0;
}
else
return 1; /* standard part of u(h)-u(a) isn't zero
means function is not continuous */
}
}
if(ATOMIC(u))
strcpy(reason,atom_string(u));
else if(f==ABSFUNCTOR) /* functor_string doesn't handle ABSFUNCTOR */
{ strcpy(reason, english(926)); /* | */
strcat(reason,atom_string(h));
strcat(reason, english(926)); /* | */
}
else if(f == '^' && equals(ARG(0,u),eulere))
{ strcpy(reason,"e^");
strcat(reason, atom_string(h));
}
else if(f == '^' && equals(h,ARG(0,u)) && !depends(ARG(1,u),h))
{ strcpy(reason,"atom_string(h)");
strcat(reason,"^n");
}
else
functor_string(f,SCREEN,reason);
strcat(reason, english(912)); /* is continuous */
return 0;
}
/*_________________________________________________________________*/
int limexptolog(term t, term arg, term *next, char *reason)
/* lim u^v = lim e^(v ln u) */
{ term u,v,q,w,limv,limv_ans,limu, limu_ans;
int err;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '^')
return 1;
u = ARG(0,w);
v = ARG(1,w);
if(equals(u,eulere))
{ if(get_mathmode() == MENUMODE)
errbuf(0, english(927));
/* That wouldn't help: you already have e to a power. */
return 1;
}
if(POSNUMBER(u) && !ZERO(u))
err = 0;
else if(NEGATIVE(u) && POSNUMBER(ARG(0,u)))
return 1;
else
err = infer(lessthan(zero,u));
if(err)
{ errbuf(0,english(1987));
/* Base of exponent must be positive. */
return 1;
}
if(get_problemtype() == DIFFERENTIATE_FROM_DEFN &&
get_mathmode() == AUTOMODE
)
{ /* check for the case lim u = 1 and lim v undefined and refuse
to execute in that case, since under this problemtype
L'Hopital cannot be used. For example,
on lim(1+h/x)^(x/h) this is useless. */
limv = ARITY(t)==3? limit3(ARG(0,t),ARG(1,t),v) : limit(ARG(0,t),v);
err = limval(limv,&limv_ans);
if(!err && NOTDEFINED(limv_ans))
{ limu = ARITY(t) == 3 ? limit3(ARG(0,t),ARG(1,t),u) : limit(ARG(0,t),u);
err = limval(limu,&limu_ans);
if(!err && ONE(limu_ans))
{ /* refuse to execute */
errbuf(0, english(1290));
return 1;
}
}
}
q = make_power(eulere,product(v,ln1(u)));
if(ARITY(t)==2)
*next = limit(ARG(0,t),q);
else
*next = limit3(ARG(0,t),ARG(1,t),q);
HIGHLIGHT(*next);
strcpy(reason,"$$lim(t->a, u^v) = lim(t-a, e^(v ln u))$$");
return 0;
}
/*_________________________________________________________________*/
int isolateln(term t, term arg, term *next, char *reason)
/* lim u ln v = lim ln v/(1/u) */
/* In term selection mode works on the product u ln v */
{ term u,v,w,uv,cancelled,denom;
int i,err;
unsigned short n;
if(FUNCTOR(t) == LIMIT)
{ uv = LIMITAND(t);
if(FUNCTOR(uv) != '*')
return 1;
err = isolateln(uv,arg,&w,reason);
if(err)
return 1;
n = ARITY(t);
*next = n==2 ? limit(ARG(0,t),w) : limit3(ARG(0,t),ARG(1,t),w);
return 0;
}
if(FUNCTOR(t) != '*')
return 1;
n = ARITY(t);
/* Find the first nonconstant ln or lnpower term in t */
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == LN && !constant(u))
break;
}
if(i==n)
return 1;
if(n == 2)
v = ARG(i ? 0 : 1, t);
else
{ err = cancel(t,u,&cancelled,&v);
assert(!err);
}
if(FUNCTOR(v) == '^')
denom = make_power(ARG(0,v),tnegate(ARG(1,v)));
else
denom = reciprocal(v);
*next = make_fraction(u,denom);
PROTECT(*next); /* save it from automode simplification */
HIGHLIGHT(*next);
strcpy(reason,"lim u ln v = lim ln v/(1/u)");
return 0;
}
/*_________________________________________________________________*/
int isolatelnpower(term t, term arg, term *next, char *reason)
/* lim u ln^k v = lim ln^k v/(1/u) */
/* In term selection mode works on the product u (ln v)^n */
{ term u,v,w,uv,cancelled,denom;
int i,err;
unsigned short n;
if(FUNCTOR(t) == LIMIT)
{ uv = LIMITAND(t);
if(FUNCTOR(uv) != '*')
return 1;
err = isolateln(uv,arg,&w,reason);
if(err)
return 1;
n = ARITY(t);
*next = n==2 ? limit(ARG(0,t),w) : limit3(ARG(0,t),ARG(1,t),w);
return 0;
}
if(FUNCTOR(t) != '*')
return 1;
n = ARITY(t);
/* Find the first nonconstant lnpower term in t */
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == LN && !constant(ARG(0,u)))
break;
}
if(i==n)
return 1;
if(n == 2)
v = ARG(i ? 0 : 1, t);
else
{ err = cancel(t,u,&cancelled,&v);
assert(!err);
}
if(FUNCTOR(v) == '^')
denom = make_power(ARG(0,v),tnegate(ARG(1,v)));
else
denom = reciprocal(v);
*next = make_fraction(u,denom);
PROTECT(*next); /* save it from automode simplification */
HIGHLIGHT(*next);
strcpy(reason,"lim u ln^k v = lim ln^k v/(1/u)");
return 0;
}
/*_________________________________________________________________*/
int negexptodenom(term t, term arg, term *next, char *reason)
/* lim u^-n v = lim v/u^n */
/* In term selection mode works on the product also */
{ term v,w,uv,u,cancelled;
int i,err;
unsigned short n;
if(FUNCTOR(t) == LIMIT)
{ uv = LIMITAND(t);
if(FUNCTOR(uv) != '*')
return 1;
err = negexptodenom(uv,arg,&w,reason);
if(err)
return 1;
n = ARITY(t);
*next = n==2 ? limit(ARG(0,t),w) : limit3(ARG(0,t),ARG(1,t),w);
return 0;
}
if(FUNCTOR(t) != '*')
return 1;
n = ARITY(t);
/* Find the first negative exponent term in u (with constant exponent) */
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '^' && NEGATIVE(ARG(1,u)) &&
constant(ARG(1,u)) && !constant(ARG(0,u))
)
break;
}
if(i==n)
return 1;
if(n == 2)
v = ARG(i ? 0 : 1, t);
else
{ err = cancel(t,u,&cancelled,&v);
assert(!err);
}
*next = make_fraction(v,make_power(ARG(0,u),ARG(0,ARG(1,u))));
HIGHLIGHT(*next);
strcpy(reason,"lim u^-n v = lim v/u^n");
return 0;
}
/*_________________________________________________________________*/
int exptodenom(term t, term arg, term *next, char *reason)
/* lim e^u v = lim v/e^-u */
/* In term selection mode works on the product also */
{ term v,w,uv,u,cancelled;
int i,err;
unsigned short n;
if(FUNCTOR(t) == LIMIT)
{ uv = LIMITAND(t);
if(FUNCTOR(uv) != '*')
return 1;
err = exptodenom(uv,arg,&w,reason);
if(err)
return 1;
n = ARITY(t);
*next = n==2 ? limit(ARG(0,t),w) : limit3(ARG(0,t),ARG(1,t),w);
return 0;
}
if(FUNCTOR(t) != '*')
return 1;
n = ARITY(t);
/* Find the first exponential term in u (with nonconstant exponent) */
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) == '^' &&
equals(ARG(0,u),eulere) &&
!constant(ARG(1,u))
)
break;
}
if(i==n)
return 1;
if(n == 2)
v = ARG(i ? 0 : 1, t);
else
{ err = cancel(t,u,&cancelled,&v);
assert(!err);
}
*next = make_fraction(v,make_power(eulere,tnegate(ARG(1,u))));
if(!NEGATIVE(ARG(1,u)))
/* the answer has a negative exponent in the denom */
PROTECT(*next);
/* else it will be simplified back where it came from,
by eliminating the negative exponent in the denom */
HIGHLIGHT(*next);
strcpy(reason,"lim e^u v = lim v/e^-u");
return 0;
}
/*_________________________________________________________________*/
static void invert_trig(unsigned short g, char *reason)
/* fill in reason with a string justifying moving trig functor
g to the denominator
*/
{ switch(g)
{ case SIN:
strcpy(reason, "sin x = 1/csc x");
break;
case COS:
strcpy(reason, "cos x = 1/sec x");
break;
case TAN:
strcpy(reason,"tan x = 1/cot x");
break;
case SEC:
strcpy(reason, "sec x = 1/cos x");
break;
case CSC:
strcpy(reason, "csc x = 1/sin x");
break;
case COT:
strcpy(reason,"cot x = 1/tan x");
break;
default:
assert(0);
}
}
/*_________________________________________________________________*/
static term trigrecip(term t)
/* get the reciprocal of a trig function, in trig function form;
for example if t is tan u then cot u is returned.
*/
{ unsigned short g = FUNCTOR(t);
term u = ARG(0,t);
switch(g)
{ case SIN:
return csc1(u);
case COS:
return sec1(u);
case TAN:
return cot1(u);
case SEC:
return cos1(u);
case CSC:
return sin1(u);
case COT:
return tan1(u);
default:
assert(0);
}
return zero; /* avoid a compiler warning */
}
/*_________________________________________________________________*/
int trigtodenom(term t, term arg, term *next, char *reason)
/* cot x = 1/tan x etc, but applied to a limit term
with a product for a limitand. In term selection, it is not used
when the product limitand is selected, instead the trig operators
themselves apply to products. When the whole limit term is selected,
however, it does come up, and it is on the menu for L'Hopital's rule;
and it is used in automode on limit terms. Therefore it does not
need to apply to a product directly at all.
*/
{ term u,w,uv,p,cancelled,num,denom,power;
int i,err;
unsigned short n,g,h;
if(FUNCTOR(t) != LIMIT)
return 1;
uv = LIMITAND(t);
if(FUNCTOR(uv) != '*')
return 1;
n = ARITY(uv);
/* Find the first trig factor in u (with nonconstant argument) */
/* Or the first power of a trig function */
for(i=0;i<n;i++)
{ u = ARG(i,uv);
g = FUNCTOR(u);
if(TRIGFUNCTOR(g) && !constant(ARG(0,u)))
{ p = u;
break;
}
if(g == '^')
{ p = ARG(0,u);
h = FUNCTOR(p);
if(TRIGFUNCTOR(h) && constant(ARG(1,u)))
break;
}
}
if(i==n)
return 1;
if(g == '^')
{ g = h;
power = ARG(1,u);
}
else
power = one;
denom = trigrecip(p);
invert_trig(g,reason);
err = cancel(uv,u,&cancelled,&num);
assert(!err);
if(!ONE(power))
denom = make_power(denom,power);
HIGHLIGHT(denom);
w = make_fraction(num,denom);
*next = ARITY(t)==2 ? limit(ARG(0,t),w) : limit3(ARG(0,t),ARG(1,t),w);
return 0;
}
/*_________________________________________________________________*/
int createcompoundfraction(term t, term arg, term *next, char *reason)
/* getarg has already 'checked' that 'arg' is not zero, so there's
no need to duplicate that check here in menumode. */
/* lim uv = lim v/(1/u) where arg == u */
/* Also does lim u^n v = lim v/u^-n, as is appropriate when you're
about to use L'Hopital's rule */
/* select_ccfarg chooses the argument when this is called in automode */
{ term v,w,uv,x,cancelled,denom;
int err;
unsigned short n;
unsigned short h;
unsigned short path[5];
if(FUNCTOR(t) != LIMIT)
return 1;
n = ARITY(t);
uv = LIMITAND(t);
if(FUNCTOR(uv) != '*')
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(arg) == ILLEGAL) /* automode, must select the arg ourselves */
{ err = select_ccfarg(uv,x,&v,&arg);
if(err)
return 1;
}
else /* menu mode */
{ err = cancel(uv,arg,&cancelled,&v);
if(err)
{ errbuf(0, english(928));
/* What you entered does not divide */
errbuf(1, english(929));
/* the expression in the limit. */
return 1;
}
}
h = FUNCTOR(arg);
if(h == '^' && TRIGFUNCTOR(FUNCTOR(ARG(0,arg))))
{ denom = make_power(trigrecip(ARG(0,arg)),ARG(1,arg));
path[0] = LIMIT;
path[1] = n;
path[2] = 0;
set_pathtail(path);
switch(FUNCTOR(ARG(0,arg)))
{ case CSC: SetShowStepOperation(cscrule); break;
case SEC: SetShowStepOperation(secrule); break;
case COT: SetShowStepOperation(cottotan); break;
case SIN: SetShowStepOperation(sintocsc); break;
case COS: SetShowStepOperation(costosec); break;
case TAN: SetShowStepOperation(tantodenom); break;
}
}
else if(h == '^')
denom = make_power(ARG(0,arg),tnegate(ARG(1,arg)));
else if(TRIGFUNCTOR(h))
{ denom = trigrecip(arg);
path[0] = LIMIT;
path[1] = n;
path[2] = 0;
set_pathtail(path);
switch(h)
{ case CSC: SetShowStepOperation(cscrule); break;
case SEC: SetShowStepOperation(secrule); break;
case COT: SetShowStepOperation(cottotan); break;
case SIN: SetShowStepOperation(sintocsc); break;
case COS: SetShowStepOperation(costosec); break;
case TAN: SetShowStepOperation(tantodenom); break;
}
}
else
denom = reciprocal(arg);
w = make_fraction(v,denom);
PROTECT(w); /* save it from automode simplification */
HIGHLIGHT(w);
if(ARITY(t)==2)
*next = limit(ARG(0,t),w);
else
*next = limit3(ARG(0,t),ARG(1,t),w);
/* Select a reason string that suggests the brother
operations isolateln, isolatelnpower, negexptodenom,
exptodenom, or the trig operations used in trigtodenom;
only use uv = v/(1/u) if nothing else will do */
h = FUNCTOR(arg);
if(FUNCTOR(v) == LN)
strcpy(reason,"lim u ln v = lim ln v/(1/u)");
else if(FUNCTOR(v) == '^' && FUNCTOR(ARG(0,v)) == LN)
strcpy(reason,"lim u ln^k v = lim ln^k v/(1/u)");
else if(h == '^' && equals(ARG(0,arg),eulere))
strcpy(reason,"lim e^u v = lim v/e^-u");
else if(h == '^' && NEGATIVE(ARG(1,arg)))
strcpy(reason,"lim u^-n v = lim v/u^n");
else if(TRIGFUNCTOR(h))
invert_trig(h,reason);
else if(h == '^' && TRIGFUNCTOR(FUNCTOR(ARG(0,arg))))
invert_trig(FUNCTOR(ARG(0,arg)),reason);
else
strcpy(reason,"uv = v/(1/u)");
return 0;
}
/*_________________________________________________________________*/
int limln1(term t, term arg, term *next, char *reason)
/* (ln(1+x)/x ->1 as x->0 */
/* also does x/ln(1+x) */
/* also does ln(1-x)/x -> -1 */
{ term x,w,num, denom,u,v;
int signflag = 1;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(num) != LN && FUNCTOR(denom) != LN)
return 1;
if(FUNCTOR(num) == LN && FUNCTOR(denom) == LN)
return 1;
/* Now exactly one of num and denom is a LN term */
if(FUNCTOR(denom) == LN)
{ term temp = num; /* swap num and denom */
num = denom;
denom = temp;
}
if(!equals(x,denom))
return 1;
v = ARG(0,num);
if(FUNCTOR(v) != '+' || ARITY(v) != 2)
return 1;
if(ONE(ARG(0,v)))
u = ARG(1,v);
else if(ONE(ARG(1,v)))
u = ARG(0,v);
else
return 1; /* v doesn't have form 1+u or u+1 */
if(NEGATIVE(u))
{ u = ARG(0,u);
signflag = -1;
}
if(!equals(u,x))
return 1;
/* Now the rule is definitely applicable */
if(signflag == 1)
{ *next = one;
strcpy(reason,"$$lim(x->0,ln(1+x)/x)=1$$");
}
else
{ *next = minusone;
strcpy(reason,"$$lim(x->0,ln(1-x)/x)=-1$$");
}
HIGHLIGHT(*next);
return 0;
}
/*_________________________________________________________________*/
int limexp1(term t, term arg, term *next, char *reason)
/* (e^x-1)/x ->1 as x->0 */
/* also does x/(e^x-1) */
{ term x,w,num, denom;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(!equals(denom,x) && !equals(num,x))
return 1;
if(equals(num,x))
{ term temp;
temp = num; /* swap */
num = denom;
denom = temp;
}
if(FUNCTOR(num) != '+' || ARITY(num) != 2 || !equals(ARG(1,num),minusone)
|| FUNCTOR(ARG(0,num)) != '^' || !equals(ARG(0,ARG(0,num)), eulere)
|| !equals(ARG(1,ARG(0,num)),x)
)
return 1;
/* Now the rule is definitely applicable */
*next = one;
strcpy(reason,"$$lim(t->0,(e^t-1)/t)= 1$$");
HIGHLIGHT(*next);
return 0;
}
/*_________________________________________________________________*/
int limexp2(term t, term arg, term *next, char *reason)
/* (e^(-x)-1)/x ->-1 as x->0 */
/* also does x/(e^(-x)-1) */
{ term x,w,num, denom;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(!equals(denom,x) && !equals(num,x))
return 1;
if(equals(num,x))
{ term temp;
temp = num; /* swap */
num = denom;
denom = temp;
}
if(FUNCTOR(num) != '+' || ARITY(num) != 2 || !equals(ARG(1,num),minusone)
|| FUNCTOR(ARG(0,num)) != '^' || !equals(ARG(0,ARG(0,num)), eulere)
|| !NEGATIVE(ARG(1,ARG(0,num)))
|| !equals(ARG(0,ARG(1,ARG(0,num))),x)
)
return 1;
/* Now the rule is definitely applicable */
*next = minusone;
strcpy(reason,"$$lim(t->0,(e^(-t)-1)/t)=-1$$");
HIGHLIGHT(*next);
return 0;
}
/*_____________________________________________________________*/
int limosccos(term t, term arg, term *next, char *reason)
/* lim(t->0, cos(1/t)) = undefined */
/* internally the value is bounded_oscillations, which prints out
as 'undefined' */
{ return osc_aux(COS,t,next,reason);
}
/*_____________________________________________________________*/
int limoscsin(term t, term arg, term *next, char *reason)
/* lim(t->0, sin(1/t)) = undefined */
/* internally the value is bounded_oscillations, which prints out
as 'undefined' */
{ return osc_aux(SIN,t,next,reason);
}
/*_____________________________________________________________*/
int limosctan(term t, term arg, term *next, char *reason)
/* lim(t->0, sin(1/t)) = undefined */
/* internally the value is unbounded_oscillations, which prints out
as 'undefined' */
{ return osc_aux(TAN,t,next,reason);
}
/*_____________________________________________________________*/
int liminfcos(term t, term arg, term *next, char *reason)
/* lim(t-> \pm infinity, cos t) = undefined */
/* internally the value is bounded_oscillations, which prints out
as 'undefined' */
{ return osc_aux(COS,t,next,reason);
}
/*_____________________________________________________________*/
int liminfsin(term t, term arg, term *next, char *reason)
/* lim(t-> \pm infinity, sin t) = undefined */
/* internally the value is bounded_oscillations, which prints out
as 'undefined' */
{ return osc_aux(SIN,t,next,reason);
}
/*_____________________________________________________________*/
int liminftan(term t, term arg, term *next, char *reason)
/* lim(t-> \pm infinity, tan t) = undefined */
/* internally the value is unbounded_oscillations, which prints out
as 'undefined' */
{ return osc_aux(TAN,t,next,reason);
}
/*_____________________________________________________________*/
static int osc_aux(unsigned short f, term t, term *next, char *reason)
/* Do the work of the above six operators. The functor f is
either COS, SIN, or TAN, and this function fails if the limitand
of t does not have functor f or if the limitand isn't a fraction.
Also works if t is lim(x-> \pm infinity, f(x)).
*/
{ term u,v,x,c,s,a;
if(FUNCTOR(t) != LIMIT)
return 1;
a = ARG(1,ARG(0,t));
u = LIMITAND(t);
x = ARG(0,ARG(0,t));
if(FUNCTOR(u) != f)
return 1;
if(ISINFINITE(a))
{ v = ARG(0,u);
if(FUNCTOR(v) == '*' || FRACTION(v))
twoparts(v,x,&c,&s);
else
s = v;
if(!equals(s,x))
return 1;
if(f == TAN)
*next = unbounded_oscillations;
else
*next = bounded_oscillations;
HIGHLIGHT(*next);
switch(f)
{ case COS:
strcpy(reason,english(1906));
SetShowStepOperation(liminfcos);
/* lim(t\\to \\pm \\infty ,cos t) is undefined */
break;
case SIN:
strcpy(reason,english(1907));
SetShowStepOperation(liminfsin);
/* lim(t\\to \\pm \\infty,sin t) is undefined */
break;
case TAN:
strcpy(reason,english(1908));
SetShowStepOperation(liminftan);
/* lim(t\\to \\pm \infty,tan t) is undefined */
break;
default:
assert(0);
}
return 0;
}
if(!ZERO(a) || !FRACTION(ARG(0,u)))
return 1;
if(depends(ARG(0,ARG(0,u)),x))
return 1; /* numerator must be constant */
v = ARG(1,ARG(0,u)); /* the denominator */
if(FUNCTOR(v) == '*')
{ twoparts(v,x,&c,&s);
v = s;
}
if(!equals(v,x))
return 1;
*next = f== TAN ? unbounded_oscillations : bounded_oscillations;
HIGHLIGHT(*next);
switch(f)
{ case COS:
strcpy(reason,english(900));
break;
case SIN:
strcpy(reason,english(901));
break;
case TAN:
strcpy(reason,english(902));
break;
default:
assert(0);
}
return 0;
}
/*_________________________________________________________________*/
int limpowertimeslnabs(term t, term arg, term *next, char *reason)
/* lim(x->0, x^n ln |x|) = 0 */
{ term x,w,u,v,temp;
unsigned short g;
if(FUNCTOR(t) != LIMIT)
return 1;
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '*' || ARITY(w) != 2)
return 1;
x = ARG(0,ARG(0,t));
u = ARG(0,w);
v = ARG(1,w);
if(FUNCTOR(u) != ABSFUNCTOR && FUNCTOR(v) != ABSFUNCTOR)
return 1;
if(FUNCTOR(v) != ABSFUNCTOR)
{ temp = u;
u = v;
v = temp;
}
v = ARG(0,v);
if(FUNCTOR(v) != LN)
return 1;
if(!equals(ARG(0,v),x))
return 1;
if(equals(u,x))
/* x ln |x| */
goto out;
g = FUNCTOR(u);
if(g == SQRT && equals(ARG(0,u),x))
/* sqrt x ln |x| */
goto out;
if(g == ROOT && equals(ARG(1,u),x) && INTEGERP(ARG(0,u)))
goto out;
if(g != '^' || !equals(ARG(0,u),x))
return 1;
if(POSNUMBER(ARG(1,u)))
goto out;
if(!constant(ARG(1,u)))
return 1;
if(!infer(lessthan(zero,ARG(1,u))))
return 1;
out:
*next = zero;
HIGHLIGHT(*next);
strcpy(reason, "$lim(x\to 0,x^n ln |x|)=0");
return 0;
}
/*______________________________________________________*/
static int finite_limit(term u, term x, term a)
/* a is plus or minus infinity, x is a variable.
If u can be EASILY seen to have a finite limit as x->a,
return 1. If not return 0. Example: u = e^-x should
return 0 when a = infinity.
*/
{ term q;
if(equals(a,infinity))
{ if(FUNCTOR(u) == '^' &&
(equals(ARG(0,u),eulere) ||
equals(ARG(0,u),pi_term) ||
(INTEGERP(ARG(0,u)) && !ONE(ARG(0,u)))
) &&
(NEGATIVE(ARG(1,u)) && equals(ARG(0,ARG(1,u)),x))
)
return 1;
return 0;
}
if(equals(a,minusinfinity))
{ if(FUNCTOR(u) == '^' &&
(equals(ARG(0,u),eulere) ||
equals(ARG(0,u),pi_term) ||
(INTEGERP(ARG(0,u)) && !ONE(ARG(0,u)))
) &&
(equals(ARG(1,u),x) ||
(!polyform(ARG(1,u),x,&q) &&
obviously_positive(ARG(ARITY(q)-1,q))
)
)
)
return 1;
return 0;
}
return 0;
}
/*_________________________________________________________________________*/
static int linear_exponent(term t, term x, term *ans)
/* Return 0 if t contains a term e^v where v is linear in x;
in that case put *ans = v. Return 1 for failure to find
such a subterm.
*/
{ unsigned short n;
int i;
if(ATOMIC(t))
return 1;
if(FUNCTOR(t) == '^' &&
equals(ARG(0,t),eulere) &&
is_linear_in(ARG(1,t),x)
)
{ *ans = ARG(1,t);
return 0;
}
n = ARITY(t);
for(i=0;i<n;i++)
{ if(!linear_exponent(ARG(i,t),x,ans))
return 0;
}
return 1;
}
/*________________________________________________________________________*/
static int specialcase_sub(term u, term x, term a, term *ans)
/* u is a limitand in a limit as x->a. Find a substitution u = *ans
that changelimitvariable will otherwise miss.
Example: sin(x/2)/x as x->0, find u = x/2. Similarly for
sin(x^n/c)/x^m and sin^m(x^n/c)/x^k etc.
ln(1+f(x))/f(x), find u = f(x).
*/
{ term num,denom,v;
if(FRACTION(u) && !ATOMIC(ARG(1,u)))
{ num = ARG(0,u);
denom = ARG(1,u);
if(FUNCTOR(num) == LN && FUNCTOR(ARG(0,num)) == '+' &&
ARITY(ARG(0,num)) == 2 &&
(
(ONE(ARG(0,ARG(0,num))) && equals(ARG(1,ARG(0,num)),denom)) ||
(ONE(ARG(1,ARG(0,num))) && equals(ARG(0,ARG(0,num)),denom)) ||
(ONE(ARG(0,ARG(0,num))) && NEGATIVE(ARG(1,ARG(0,num))) && equals(ARG(0,ARG(1,ARG(0,num))),denom)) ||
(ONE(ARG(1,ARG(0,num))) && NEGATIVE(ARG(0,ARG(0,num))) && equals(ARG(0,ARG(0,ARG(0,num))),denom))
)
)
{ *ans = denom;
return 0;
}
}
/* next clause is for lim(x->0, (1-e^-x)/x) or lim(x->0, 1-e^(cx))/x) */
if(ZERO(a) && FRACTION(u) && equals(ARG(1,u),x) &&
FUNCTOR(ARG(0,u)) == '+' && ARITY(ARG(0,u)) == 2 &&
ONE(ARG(0,ARG(0,u))) && NEGATIVE(ARG(1,ARG(0,u))) &&
FUNCTOR(ARG(0,ARG(1,ARG(0,u)))) == '^' &&
equals(ARG(0,ARG(0,ARG(1,ARG(0,u)))), eulere)
)
{ *ans = ARG(1,ARG(0,ARG(1,ARG(0,u))));
return 0;
}
if(ZERO(a) && FRACTION(u))
{ num = ARG(0,u);
denom = ARG(1,u);
if(FUNCTOR(num) == '^' && INTEGERP(ARG(1,num)))
num = ARG(0,num);
if(FUNCTOR(denom) == '^' && INTEGERP(ARG(1,denom)))
denom = ARG(0,denom);
if(equals(denom,x))
{ denom = num; /* swap */
num = x;
}
if(equals(num,x) && (FUNCTOR(denom) == SIN || FUNCTOR(denom) == TAN))
{ v = ARG(0,denom);
if(FUNCTOR(v) == '^' && INTEGERP(ARG(0,v)))
v= ARG(0,v);
if(FRACTION(v) && equals(ARG(0,v),x) && INTEGERP(ARG(1,v)))
{ *ans = v;
return 0;
}
if(FRACTION(v) && INTEGERP(ARG(1,v)) && FUNCTOR(ARG(0,v)) == '^' &&
INTEGERP(ARG(1,ARG(0,v))) && equals(ARG(0,ARG(0,v)),x)
)
{ *ans = v;
return 0;
}
return 1;
}
return 1;
}
if(equals(a,infinity))
{ /* (1+1/x)^x becomes (1 + u)^(1/u) with u = 1/x */
*ans = reciprocal(x);
if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == '+' &&
ARITY(ARG(0,u)) == 2 && ONE(ARG(0,ARG(0,u))) &&
equals(ARG(1,u),x) &&
(equals(ARG(1,ARG(0,u)),*ans) ||
(NEGATIVE(ARG(1,ARG(0,u))) && equals(ARG(0,ARG(1,ARG(0,u))),*ans))
)
)
return 0;
}
if(equals(a,minusinfinity))
{ /* (1-1/x)^x becomes (1 + u)^(1/u) with u = -1/x */
*ans = reciprocal(x);
if(FUNCTOR(u) == '^' && FUNCTOR(ARG(0,u)) == '+' &&
ARITY(ARG(0,u)) == 2 && ONE(ARG(0,ARG(0,u))) &&
equals(ARG(1,u),x) &&
(equals(ARG(1,ARG(0,u)),*ans) ||
(NEGATIVE(ARG(1,ARG(0,u))) && equals(ARG(0,ARG(1,ARG(0,u))),*ans))
)
)
{ *ans = tnegate(*ans);
return 0;
}
}
return 1;
}
/*______________________________________________________________________*/
int limprod2left(term t, term arg, term *next, char *reason)
/* lim(uv) = lim(u/?) lim(?v) */
{ term u,v,uv,c,d,p,q,cc,dd,temp;
int err,err2;
unsigned n;
if(FUNCTOR(t) != LIMIT)
return 1;
n = ARITY(t);
uv = LIMITAND(t);
if(FUNCTOR(uv) != '*' || ARITY(uv) != 2)
return 1;
if(FUNCTOR(arg) == ILLEGAL) /* automode, select own arg */
return 1; /* only limprod2right is called in auto mode */
u = ARG(0,uv);
v = ARG(1,uv);
HIGHLIGHT(arg);
c = make_fraction(u,arg);
SET_ALREADY(c); /* so it doesn't get 'simplified' before being evaluated */
PROTECT(c);
d = product(arg,v);
if(FUNCTOR(d) == '*' && !rawcollectpowers(d,&temp,1))
d = temp;
if(FUNCTOR(d) == '*')
sortargs(d);
cc = n==2 ? limit(ARG(0,t),c) : limit3(ARG(0,t),ARG(1,t),c);
dd = n==2 ? limit(ARG(0,t),d) : limit3(ARG(0,t),ARG(1,t),d);
err = limval(cc,&p);
if(err)
{ errbuf(0, english(587));
/* MathXpert cannot evaluate the limits that would result. */
return 1;
}
err2 = limval(dd,&q);
if(err2)
{ errbuf(0, english(587));
/* MathXpert cannot evaluate the limit that would result. */
return 1;
}
if( (ZERO(p) && NOTDEFINED(q)) || (NOTDEFINED(p) && ZERO(q)) )
{ errbuf(0, english(1949));
/* One limit would be zero and the other undefined. */
return 1;
}
*next = product(cc,dd);
strcpy(reason, "lim(uv) = lim(u/?)lim(?v)");
return 0;
}
/*______________________________________________________________________*/
int limprod2right(term t, term arg, term *next, char *reason)
/* lim(uv) = lim(?u) lim(v/?) */
{ term v,u,uv,c,d,p,q,cc,dd,temp;
int err,err2,flag;
unsigned n;
if(FUNCTOR(t) != LIMIT)
return 1;
n = ARITY(t);
uv = LIMITAND(t);
if(FUNCTOR(uv) != '*' || ARITY(uv) != 2)
return 1;
if(FUNCTOR(arg) == ILLEGAL) /* automode, select own arg */
{ err = select_limprod2_arg(&flag,t,&arg);
if(err)
return 1;
if(flag == 1)
{ err = limprod2left(t,arg,next,reason);
/* only limprod2right is called in automode */
if(err)
return 1;
SetShowStepOperation(limprod2left);
return 0;
}
}
u = ARG(1,uv);
v = ARG(0,uv);
HIGHLIGHT(arg);
c = make_fraction(u,arg);
SET_ALREADY(c); /* so it doesn't get 'simplified' before being evaluated */
PROTECT(c);
d = product(arg,v);
if(FUNCTOR(d) == '*' && !rawcollectpowers(d,&temp,1))
d = temp;
if(FUNCTOR(d) == '*')
sortargs(d);
cc = n==2 ? limit(ARG(0,t),c) : limit3(ARG(0,t),ARG(1,t),c);
dd = n==2 ? limit(ARG(0,t),d) : limit3(ARG(0,t),ARG(1,t),d);
err = limval(cc,&p);
if(err)
{ errbuf(0, english(587));
/* MathXpert cannot evaluate the limits that would result. */
return 1;
}
err2 = limval(dd,&q);
if(err2)
{ errbuf(0, english(587));
/* MathXpert cannot evaluate the limit that would result. */
return 1;
}
if( (ZERO(p) && NOTDEFINED(q)) || (NOTDEFINED(p) && ZERO(q)) )
{ errbuf(0, english(1949));
/* One limit would be zero and the other undefined. */
return 1;
}
*next = product(dd,cc);
strcpy(reason, "lim(uv) = lim(u/?)lim(?v)");
return 0;
}
/*______________________________________________________________________*/
static int select_limprod2_arg(int *flag, term t, term *ans)
/* t is a limit of a product of arity 2.
Choose the ? term in limprod2left or limprod2right.
Return 0 for success, 1 for failure.
In *flag, return 1 or 2, depending whether u or v in the limitand uv
has to be divided. */
/* example: if t is lim(x->0, x ln(1+x)), choose *ans = x and *flag = 2 */
/* example 2: if t is lim(x->infinity, x ln(1+e^-x)), choose x = e^-x and *flag = 2 */
/* At present such log limits are the only ones that require this operator. */
{ term w,v,uv,p,q,temp;
unsigned short n;
int i,err;
if(FUNCTOR(t) != LIMIT)
return 1;
uv = LIMITAND(t);
n = ARITY(t);
if(FUNCTOR(uv) != '*' || ARITY(uv) != 2)
return 1;
for(i=0;i<2;i++)
{ v = ARG(i,uv);
if(FUNCTOR(v) == LN && FUNCTOR(ARG(0,v)) == '+' && ARITY(ARG(0,v))== 2 &&
(ONE(ARG(0,ARG(0,v))) || ONE(ARG(1,ARG(0,v))))
)
{ w = ARG(0,v);
if(ONE(ARG(0,w)))
p = ARG(1,w);
else if(ONE(ARG(1,w)))
p = ARG(0,w);
q = n == 2 ? limit(ARG(0,t),p) : limit3(ARG(0,t),ARG(1,t),p);
err = limval(q,&temp);
if(!err && ZERO(temp))
{ *ans = p;
*flag = i+1;
return 0;
}
}
}
return 1;
}
/*_________________________________________________________________*/
int limsinh1(term t, term arg, term *next, char *reason)
/* (sinh x)/x ->1 as x->0 */
/* also does x/sinh(x) */
{ term x,w,num, denom;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(num) != SINH && FUNCTOR(denom) != SINH)
return 1;
if(FUNCTOR(num) == SINH)
{ if( !equals(x,ARG(0,num)) || !equals(x,denom))
return 1;
}
else /* FUNCTOR(denom) is SINH */
{ if( !equals(x,ARG(0,denom)) || !equals(x,num))
return 1;
}
*next = one;
HIGHLIGHT(*next);
strcpy(reason, "$$lim(x->0,sinh(x)/x) = 1$$");
return 0;
}
/*_________________________________________________________________*/
int limtanh1(term t, term arg, term *next, char *reason)
/* (tan x)/x ->1 as x->0 */
{ term x,w,num, denom;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
num = ARG(0,w);
denom = ARG(1,w);
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(num) != TANH && FUNCTOR(denom) != TANH)
return 1;
if(FUNCTOR(num) == TANH)
{ if( !equals(x,ARG(0,num)) || !equals(x,denom))
return 1;
}
else /* FUNCTOR(denom) is TANH */
{ if( !equals(x,ARG(0,denom)) || !equals(x,num))
return 1;
}
*next = one;
HIGHLIGHT(*next);
strcpy(reason, "$$lim(x->0,tanh(x)/x) = 1$$");
return 0;
}
/*_________________________________________________________________*/
int limcosh1(term t, term arg, term *next, char *reason)
/* (cosh x -1)/x ->0 as x->0 */
/* or (1- cosh x)/x ->0 as x->0 */
{ term x,u,w;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(! equals(x,ARG(1,w)))
return 1;
u = ARG(0,w);
if(FUNCTOR(u) != '+')
return 1;
if(ARITY(u) > 2)
return 1;
if(
(ONE(ARG(0,u)) && FUNCTOR(ARG(1,u))=='-' &&
FUNCTOR(ARG(0,ARG(1,u))) == COSH && equals(ARG(0,ARG(0,ARG(1,u))),x)
) ||
(ONE(ARG(1,u)) && FUNCTOR(ARG(0,u)) == '-' &&
FUNCTOR(ARG(0,ARG(0,u))) == COSH && equals(ARG(0,ARG(0,ARG(0,u))),x)
)
)
{ *next = zero;
HIGHLIGHT(*next);
strcpy(reason, english(2000)); /* (1 - cosh t)/x \\to */
strcat(reason, english(923)); /* 0 as t\\to */
strcat(reason,"0");
return 0;
}
if(
( FUNCTOR(ARG(0,u)) == COSH && equals(ARG(0,ARG(0,u)),x) &&
equals(ARG(1,u),minusone)
) ||
( equals(ARG(0,u),minusone) && FUNCTOR(ARG(1,u)) == COSH &&
equals(ARG(0,ARG(1,u)),x)
)
)
{ *next = zero;
HIGHLIGHT(*next);
strcpy(reason, "$$lim(x->0,(cosh(x)-1)/x) = 0$$");
return 0;
}
return 1;
}
/*_________________________________________________________________*/
int limcosh2(term t, term arg, term *next, char *reason)
/* (1-cosh x)/x^2 -> 1/2 as x->0 */
/* or (cosh x - 1)/x^2 -> - 1/2 as x->0 */
{ term x,u,w;
if(FUNCTOR(t) != LIMIT)
return 1;
w = LIMITAND(t);
if(FUNCTOR(w) != '/')
return 1;
if(!ZERO(ARG(1,ARG(0,t))))
return 1;
x = ARG(0,ARG(0,t));
if(FUNCTOR(ARG(1,w)) != '^')
return 1;
if( !equals(ARG(1,ARG(1,w)),two))
return 1;
if( !equals(ARG(0,ARG(1,w)),x))
return 1;
if(! equals(x,ARG(1,w)))
return 1;
u = ARG(0,w);
if(FUNCTOR(u) != '+')
return 1;
if(ARITY(u) > 2)
return 1;
if(
(ONE(ARG(0,u)) && FUNCTOR(ARG(1,u))=='-' &&
FUNCTOR(ARG(0,ARG(1,u))) == COSH && equals(ARG(0,ARG(0,ARG(1,u))),x)
) ||
(ONE(ARG(1,u)) && FUNCTOR(ARG(0,u)) == '-' &&
FUNCTOR(ARG(0,ARG(0,u))) == COSH && equals(ARG(0,ARG(0,ARG(0,u))),x)
)
)
{ *next = make_fraction(one,two);
goto out;
}
if(
( FUNCTOR(ARG(0,u)) == COSH && equals(ARG(0,ARG(0,u)),x) &&
equals(ARG(1,u),minusone)
) ||
( equals(ARG(0,u),minusone) && FUNCTOR(ARG(1,u)) == COSH &&
equals(ARG(0,ARG(1,u)),x)
)
)
{ *next = tnegate(make_fraction(one,two));
goto out;
}
return 1;
out:
HIGHLIGHT(*next);
strcpy(reason, "$$lim(x->0,(cosh(x)-1)/x^2) = 1/2$$");
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists