Sindbad~EG File Manager

Current Path : /usr/home/beeson/Otter-Lambda/yyy/trigcalc/
Upload File :
Current File : /usr/home/beeson/Otter-Lambda/yyy/trigcalc/limquo.c

/*
limit of quotient menu operators for MathXpert
M. Beeson
1.14.91  Original date
8.24.98 last modified
1.13.00  added "ARITY(t) == 2" in limquotient,  and made other changes
to the block where the denominator of the limitand is tan(x).
2.9.05 modified includes
*/

#include <string.h>
#include <assert.h>
#include <math.h>
#define TRIGCALC_DLL
#include "globals.h"
#include "ops.h"
#include "calc.h"
#include "prover.h"
#include "order.h"
#include "checkarg.h"
#include "cancel.h"
#include "factor.h"
#include "autosub.h"
#include "symbols.h"
#include "algaux.h"
#include "mplimits.h"
#include "polynoms.h"
#include "graphstr.h"
#include "document.h"
#include "automode.h"   /* setlocus       */
#include "symbols.h"
#include "pvalaux.h"    /* content_factor */
#include "mathmode.h"     /* get_mathmode   */
#include "trig.h"       /* TRIGFUNCTOR    */
#include "probtype.h"
#include "errbuf.h"
#include "deriv.h"      /* derivative     */
#include "trigsimp.h"   /* trigsimp       */
#include "autosimp.h"   /* set_pathtail   */
#include "binders.h"    /* get_binders    */
#include "deval.h"  

static int indeterminate_form(term);
static int determine_sign(term t);
/*_________________________________________________________________*/
MEXPORT_TRIGCALC int limrecip(term t, term arg, term *next, char *reason)
/* lim (1/v) = 1/(lim v) or lim(c/v)=c/(lim v)     (c constant) */
{ term u,v,w,mid,temp;
  int err,sign=3;  /* 3 is neither -1, 0, or 1 */
  if(FUNCTOR(t) != LIMIT)
     return 1;
  w = LIMITAND(t);
  if(FUNCTOR(w) != '/')
     return 1;
  u = ARG(0,w);
  v = ARG(1,w);
  if(depends(u,ARG(0,ARG(0,t))))
     { errbuf(0, english(576)); /* Numerator isn't constant */
       errbuf(1, english(577)); /* so that operator can't be used */
       return 1;
     }
  mid = ARITY(t)==2 ? limit(ARG(0,t),v) : limit3(ARG(0,t),ARG(1,t),v);
  err = limval(mid,&temp);  /* calculate limit of denominator invisibly */
  /* If the limit of the denominator comes out infinite or one of the
     undefined values, we still allow this operator to be used.  In
     automode, limquoinfinite will be tried first, so most such cases
     won't come here.  */
  if(err)
     /* limval can't compute the limit of the denominator.  That means
        the student can't do it either, so the computation is doomed.
        Provisionally we assume the denominator has a nonzero limit */

     { if(get_mathmode() == AUTOMODE)
          return 1;    /* don't proceed in auto mode if limval
                        can't calculate the limits of num and denom. */
       if(err == 1)  /* denominator limit couldn't be calculated */
          { temp = ne(mid,zero);
            SETPROVISIONAL(temp);
            SET_ALREADY(ARG(0,temp));  /* so lpt doesn't try to compute the limval again */
            assume(temp);
            commentbuf(0,english(948));
            /* MathXpert can't calculate the limit of the denominator */
            commentbuf(1,english(1983));
            /* MathXpert will assume it is defined and nonzero. */
            commentbuf(2, english(1982));
            /* If not, you may get a wrong answer, so be careful. */
          }
     }
  if(ZERO(temp))
     { sign = determine_sign(mid);
       inhibit(polyvalop);
       inhibit(arithmetic);
     }
  if(equals(temp,undefined))
    /* example, lim (x->pi/2, 1/tan x)  */
    { errbuf(0, english(1924));
       /* Limit of denominator is undefined */
       return 1;
     }
  *next = make_fraction(u, ARITY(t)==2 ? limit(ARG(0,t),v) : limit3(ARG(0,t),ARG(1,t),v));
  if(sign > 0)
     SETPOSITIVE(*next);
  else if (sign < 0)
     SETNEGATIVE(*next);
  else if (sign == 0)
     SETINFINITESIMAL(*next);
  HIGHLIGHT(*next);
  if(ONE(u))
     strcpy(reason,"lim (1/v)=1/(lim v)");
  else
     strcpy(reason,"lim (c/v)=c/(lim v)");
  release(quotienttopower);  /* perhaps inhibited by quotientofpowers */
  return 0;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC int limquotient(term t, term arg, term *next, char *reason)
/* lim (u/v)=(lim u)/(lim v) */
/* Fail if both lim u and lim v are zero, but succeed if either one
is nonzero;  it's ok if the denom is zero and the num is not, because
this will go to plus or minus infinity or undefined.  But 0/0 is not ok.
Also fails if both limits are undefined (or infinite) */

{ term u,v,w,mid,temp,mid2,numval,denomval,a,x;
  int err,err2,s,sign;
  int zerodenomflag, zeronumflag,failure=0;
  unsigned short path[7];
  if(FUNCTOR(t) != LIMIT)
     return 1;
  w = LIMITAND(t);
  if(FUNCTOR(w) != '/')
     return 1;
  u = ARG(0,w);
  v = ARG(1,w);
  a = ARG(1,ARG(0,t));
  x = ARG(0,ARG(0,t));
  /* the following code traps lim(x->pi/2, c/tan x) and applies
     tan x = sin x/ cos x, which otherwise doesn't get used,
     and must be used to get zero for the answer, since lim(x->pi/2,tan x)
     is 'undefined'. */
  if( ARITY(t) == 2 &&
     (equals(a,piover2) || (NEGATIVE(a) && equals(ARG(0,a),piover2))) &&
     FUNCTOR(ARG(1,w)) == TAN && equals(ARG(0,ARG(1,w)),x)
    )
     { /* but we don't want to do thus if the limit of the numerator is 
          infinite anyway, only if it is zero or finite */
       if(!numerical(u))
          { mid2 = ARITY(t)==2 ? limit(ARG(0,t),u) : limit3(ARG(0,t),ARG(1,t),u);
            err2 = limval(mid2,&numval);
            if(err2 || NOTDEFINED(numval))
              { failure = 2;
                goto fail;
              }
          }
       v = make_fraction(sin1(x),cos1(x));
       HIGHLIGHT(v);
       mid = make_fraction(ARG(0,w),v);
       *next = ARITY(t) == 2  ? limit(ARG(0,t),mid) : limit3(ARG(0,t),ARG(1,t),mid);
       strcpy(reason, "tan x = (sin x)/cos x");
       SetShowStepOperation(tanrule);
       path[0] = LIMIT;
       path[1] = ARITY(t) == 2 ? 2 : 3;
       path[2] = '/';
       path[3] = 2;
       path[4] = 0;
       set_pathtail(path);
       return 0;
     }
  s = status(limquotient);
  if(s > LEARNING && !ONE(v) && !depends(v,ARG(0,ARG(0,t))))
      /* !ONE(v) because then limlinear may fail, and we don't
          want limquotient to fail on lim(u/1) if some user
          tries to use it. */
      return limlinear(t,arg,next,reason);
  if(s > LEARNING && !depends(v,ARG(0,ARG(0,t))))
      return limrecip(t,arg,next,reason);
  mid = ARITY(t)==2 ? limit(ARG(0,t),v) : limit3(ARG(0,t),ARG(1,t),v);
  err = limval(mid,&denomval);
  if(err)
     { failure = 1;
       goto fail;
     }
  /* Now determine if denomval is zero or undefined or finite. */
  if(ZERO(denomval))
     zerodenomflag = 1;
  else if(NOTDEFINED(denomval))
     { if(!ISINFINITE(denomval))
          { errbuf(0,english(1909));
                /* Limit of the denominator is not defined and not plus or minus infinity. */
            /* Note, if students had a symbol for "unbounded oscillations", etc.
            you COULD calculate with "unbounded_oscillations/0", etc. */
            return 1;
          }
       zerodenomflag = -1;
     }
  else if(ATOMIC(denomval))
     zerodenomflag = 0;
  else
     zerodenomflag = check(nonzero(denomval));
  if(!zerodenomflag)
     { err = check(domain(denomval));
       if(err)
          zerodenomflag = -1;  /* undefined limit */
     }
  if(zerodenomflag)
     /* undefined or zero denominator */
     { mid2 = ARITY(t)==2 ? limit(ARG(0,t),u) : limit3(ARG(0,t),ARG(1,t),u);
       err2 = limval(mid2,&numval);
       if(err2)
          { failure = 2;
            goto fail;
          }
       /* Now determine if numval is zero or undefined or finite */
       if(ZERO(numval))
          zeronumflag = 1;
       else if(NOTDEFINED(numval))
          { if(!ISINFINITE(numval))
               { errbuf(0, english(1910));
                 /* Limit of the numerator is not defined and not plus or minus infinity. */
                 return 1;
               }
            zeronumflag = -1;
          }
       else if(ATOMIC(numval))
          zeronumflag = 0;
       else
          zeronumflag = check(nonzero(numval));
       if(!zeronumflag)
          { err = check(domain(numval));
            if(err)
               zeronumflag = -1;  /* undefined limit */
          }

       if(zeronumflag < 0 && zerodenomflag < 0)
          /* undefined numerator and denominator, fail */
          { char buffer[128];
            strcpy(buffer, english(579)); /* Limit of denominator is  */
            strcat(buffer,
                   equals(denomval,infinity) ? "$�$" :
                       equals(denomval,minusinfinity) ? "$-�$" :
                       english(581) /* undefined */
                  );
            errbuf(0,buffer);
            errbuf(1, english(870));
               /* and the limit of the numerator is also not finite, */
            errbuf(2, english(577));
               /* so that operator can't be used */
            return 1;
          }
       if(zeronumflag > 0 && zerodenomflag > 0)
          { /* both num and denom are zero, fail */
            errbuf(0, english(944));
               /* Both numerator and denominator approach 0, */
            errbuf(1, english(577));
               /* so that operator can't be used. */
            return 1;
          }
     }
  if(zerodenomflag > 0)
     sign = determine_sign(mid);
  else
     sign = 0;

  fail:  /* get here if limval failed on num or denom */
  if(failure)
     { if(get_mathmode() == AUTOMODE)
          return 1;    /* don't proceed in auto mode if limval
                        can't calculate the limits of num and denom. */
       if(failure == 1)  /* denominator limit couldn't be calculated */
          { temp = ne(mid,zero);
            SETPROVISIONAL(temp);
            SET_ALREADY(ARG(0,temp));   /* to prevent lpt trying limval again */
            assume(temp);
            commentbuf(0,english(948));
            /* MathXpert can't calculate the limit of the denominator */
            commentbuf(1,english(1983));
            /* MathXpert will assume it is defined and nonzero. */
            commentbuf(2, english(1982));
            /* If not, you may get a wrong answer, so be careful. */
          }
       if(failure == 2)
          { /* we only get here if the denominator has a zero or
               undefined limit */
            errbuf(0, english(951));
            /* MathXpert can't calculate the limit of the numerator. */
            return 1;
          }
     }
  if(ARITY(t)==2)
     *next = make_fraction(limit(ARG(0,t),u),limit(ARG(0,t),v));
  else
     *next = make_fraction(limit3(ARG(0,t),ARG(1,t),u),limit3(ARG(0,t),ARG(1,t),v));
  if(sign > 0)
     SETPOSITIVE(*next);
  else if (sign < 0)
     SETNEGATIVE(*next);
  else if (sign == 0)
     SETINFINITESIMAL(*next);
  HIGHLIGHT(*next);
  strcpy(reason,"lim (u/v) =          (lim u)/(lim v)");
  release(quotienttopower);  /* perhaps inhibited by quotientofpowers */
  return 0;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC int limrationalfunction(term t, term arg, term *next, char *reason)
{ term u,x;
  int err;
  if(FUNCTOR(t) != LIMIT)
     return 1;
  u = LIMITAND(t);
  x = ARG(0,ARG(0,t));
  if(
      (FUNCTOR(u) == '/' && poly2(ARG(0,u),x) && poly2(ARG(1,u),x)) ||
      poly2(u,x)
    )
     { err = limval(t,next);
       assert(!err);
     }
  else
     return 1;
  strcpy(reason,english(578)); /* calculate limit of     rational function */
  HIGHLIGHT(*next);
  return 0;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC int quotientofpowers(term t, term arg, term *next, char *reason)
{ term u,v,n,temp;
  int err;
  unsigned f=FUNCTOR(t);
  unsigned short path[5];
  if(f == LIMIT)
    { err = quotientofpowers(LIMITAND(t),arg,&temp,reason);
      if(err)
         return 1;
      inhibit(quotienttopower);
      if(ARITY(t)==2)
         { *next = limit(ARG(0,t),temp);
           path[0] = LIMIT;
           path[1] = 2;
           path[2] = 0;
         }
      else
         { *next = limit3(ARG(0,t),ARG(1,t),temp);
           path[0] = LIMIT;
           path[1] = 3;
           path[2] = 0;
         }
      set_pathtail(path);
      return 0;
    }
  else if (f == '/')
    { if(FUNCTOR(ARG(0,t)) != '^')
         return 1;
      n = ARG(1,ARG(0,t));
      if(FUNCTOR(ARG(1,t)) != '^')
         return 1;
      if(!equals(ARG(1,ARG(1,t)),n))
         return 1;
      u = ARG(0,ARG(0,t));
      v = ARG(0,ARG(1,t));
      *next = make_power(make_fraction(u,v),n);
      HIGHLIGHT(*next);
      strcpy(reason,"$a�/b� = (a/b)�$");
      SetShowStepOperation(poweroutoffraction);
      /* this operation does the same thing but it is
      poweroutoffraction that will go on the ShowStep menu */
      return 0;
    }
  else
     return 1;
}

/*___________________________________________________________________*/
static int select_multnumdenomarg(term x,term num, term denom, term *arg)
/* given num and denom of a limitand, where x is the limit variable,
   select an approriate arg to multiply num and denom by, such as
   3 in case of tan(3x)/x.  Return 0 for success.
*/

{ term v,c,trash,d,s;
  int err;
  if(ATOMIC(num))
     return 1;
  v = ARG(0,num);
  if(equals(v,denom))
     return 1;  /* this happens when this operator is called the
                        second time after it has worked once */
  if(FUNCTOR(v) != '*')
     return 1;
  twoparts(v,x,&c,&s);  /* v = cs where c doesn't depend on x */
  if(ONE(c))
     return 1;
  err = cancel(denom,s,&trash,&d);   /* u  = f(cs)/ds */
  if(err || depends(d,x))
     return 1;
  HIGHLIGHT(c);
  *arg = make_fraction(c,d);        /*  u = (c/d) f(cs)/cs   */
  inhibit(limquotient);  /* these will be released by changelimitvariable */
  inhibit(pulloutnonzerolimit);
  inhibit(lhopital);
  inhibit(limlinear);
  return 0;
}
/*___________________________________________________________________*/
MEXPORT_TRIGCALC int multnumdenom(term t, term arg, term *next, char *reason)
/* multiply num and denom in a limit of quotients by arg */
/* This operator is needed to derive the derivative of ln x
   directly from the definition-- you have to multiply num and denom by x.
   It is called in auto mode on limits of quotients when problemtype
   is DIFFERENTIATE_FROM_DEFN. */

/* It is also needed to do examples like  lim(x->0, sin(3x)/x) */
/* It should also handle lim(x->0, x/sin(3x))                  */
/* If arg is constant, as it always is in auto mode,
   it pulls the constant out of the numerator e.g.
   lim(x->0, sin(3x)/x) =>   3 lim(x->0, sin(3x)/3x)  */

{ int err, whichway=0,saveit,i;
  term newlimitand,c,d,u,v,x,num,denom,newnum,newdenom,temp;
  unsigned f;
  int inhibitflag = 0;
  unsigned n = ARITY(t);
  char localbuf[DIMREASONBUFFER];
  if(FUNCTOR(t) != LIMIT)
     return 1;
  u = LIMITAND(t);
  x = ARG(0,ARG(0,t));
  if(FUNCTOR(u) != '/')
     { errbuf(0, english(589));
         /* That only works on limits of quotients. */
       return 1;
     }
  num = ARG(0,u);
  denom = ARG(1,u);
  if(ATOMIC(num) || mvpoly(num))
     { whichway = 1;
       /* as in  x/tan(3x), role of num and denom will be interchanged */
       temp = num;  /* swap num and denom */
       num = denom;
       denom = temp;
     }
  f = FUNCTOR(num);
  if(FUNCTOR(arg)== ILLEGAL &&
     !TRIGFUNCTOR(f) &&
     !((f == LN || f == LOG || f == LOGB) && get_problemtype() == DIFFERENTIATE_FROM_DEFN)
      /* You have to use this operator to get d/dx ln x from defn */
    )
     return 1;
  if(FUNCTOR(arg) == ILLEGAL)  /* automode, select own arg */
     { err = select_multnumdenomarg(x,num,denom,&arg);
       if(err)
          return 1;
     }
  /* Now arg can be a fraction; it can have a constant and a
     non-constant part; and whichway has to be considered, so there
     are lots of different cases to handle.  We write arg = cu/(dv)  where
     c and d are constant and any of these terms can be one:  */
  if(FRACTION(arg))
     {  twoparts(ARG(0,arg),x,&c,&u);
        twoparts(ARG(1,arg),x,&d,&v);
     }
  else
     { d = v = one;
       twoparts(arg,x,&c,&u);
     }
  /*  lim  num/denom =>  (c/d)  lim  (u num)/v  / lim(c u denom/dv)  */
  if(!ONE(u) || !ONE(v))  /* non-constant arg */
     { saveit = get_polyvalfractexpflag();   /* don't change �x to x^(1/2) */
       set_polyvalfractexpflag(0);
       polyval(make_fraction(product3(c,u,denom),product(d,v)),&temp);
       if(FRACTION(temp) && FUNCTOR(ARG(0,temp)) == '+')
          { err = apartandcancel(temp,zero,&newdenom,localbuf);
            if(err)
                err = apart(temp,zero,&newdenom,localbuf);
            if(err)
               newdenom = temp;
            else
              { SET_ALREADY(newdenom);  /* limval calls polyval; without this
                   it may undo what apartandcancel did, and cause a loop */
                ++inhibitflag;
              }
          }
       else
          newdenom = temp;
       SET_ALREADY(newdenom);  /* so polyval won't touch it again */
       polyval(make_fraction(product(u,num),v),&temp);
       if(FRACTION(temp) && FUNCTOR(ARG(0,temp)) == '+')
          { err = apartandcancel(temp,zero,&newnum,localbuf);
            if(err)
               err = apart(temp,zero,&newnum,localbuf);
            if(err)
               newnum = temp;
            else
              { SET_ALREADY(newnum);  /* limval calls polyval; without this it
                    will undo what apartandcancel did, and cause a loop */
                ++inhibitflag;
              }
          }
       else
          newnum = temp;
       set_polyvalfractexpflag(saveit);
       if(err)
          newnum = temp;
       newlimitand = whichway ? make_fraction(newdenom, newnum) :
                                make_fraction(newnum,newdenom);

     }
  else   /* lim num/denom => (c/d) lim(num/lim(c denom /d)   */
     { saveit = get_polyvalfractexpflag();
       set_polyvalfractexpflag(0);
       polyval(make_fraction(product(c,denom),product(d,v)),&temp);
       set_polyvalfractexpflag(saveit);
       err = apartandcancel(temp,zero,&newdenom,localbuf);;
       if(err)
          err = apart(temp,zero,&newdenom,localbuf);
       if(err)
          newdenom = temp;
       else
          { SET_ALREADY(newdenom);  /* limval calls polyval; without this it will
                            undo what apartandcancel did, and cause a loop;
                            it may do this anyway, because polyval calls
                            UNSET_ALREADY in fractions, so as to ALWAYS use
                            common denominators.  */
            ++inhibitflag;
          }
       newlimitand = whichway ? make_fraction(newdenom,num): make_fraction(num, newdenom);
     }
  if(FUNCTOR(newlimitand) == '/')
     { if(!equals(ARG(0,newlimitand),num))
           HIGHLIGHT(ARG(0,newlimitand));
       if(!equals(ARG(1,newlimitand),denom))
           HIGHLIGHT(ARG(1,newlimitand));;
     }
  else
     HIGHLIGHT(newlimitand);
  *next = product(whichway ? make_fraction(d,c) : make_fraction(c,d),
                  n==2 ? limit(ARG(0,t),newlimitand) :
                         limit3(ARG(0,t),ARG(1,t),newlimitand)
                 );
  HIGHLIGHT(ARG(0,*next));
  strcpy(reason, english(590));  /* mult num and denom */
          /* this reason lets the student see how they could
             have accomplished this */
  for(i=0;i<inhibitflag;i++)
     { /* inhibit all common denominator operators */
       /* More than once if there are sums in both num and denom!
          They'll only get fully released after limsum has been used
          on both num and denom then.  */

       inhibit(commondenom);
       inhibit(commondenomandsimp);
       inhibit(commondenom2);
       inhibit(commondenomandsimp2);
       inhibit(polyvalop);
     }
  return 0;
}
/*___________________________________________________________________*/
MEXPORT_TRIGCALC int divnumdenom2(term t, term arg, term *next, char *reason)
/* divide num and denom in a limit of quotients by arg, but
don't use limquo after that; example:  lim(h->0, ln(1+h/x)/h, we
have to divide num and denom by x.  */
{ int err;
  term temp = reciprocal(arg);
  err = multnumdenom(t,temp,next,reason);
  if(err)
     { if(FUNCTOR(temp) == '/')
          RELEASE(temp);  /* created by reciprocal above */
       return 1;
     }
  strcpy(reason, english(481));   /*  divide num and denom */
  return 0;
}
/*___________________________________________________________________*/
MEXPORT_TRIGCALC int divnumdenom(term t, term arg, term *next, char *reason)
/* divide num and denom in a limit of quotients by arg  and THEN
use limquo, so *next is a quotient of well-defined limits.  In
general then arg will NOT be a constant, as it often is in
multnumdenom. */

/* It is also needed to do examples like
   lim(x->0, tan(2x)/tan(3x))  => lim(x->0, tan(2x)/x) / lim(x->0, tan(3x)/x)
   but not lim(x->0, sin(3x)/x) which is handled by multnumdenom
   or changelimitvariable
*/

{ term u,v,c,d,newnum,newdenom,cc,dd,trash,x;
  char localbuf[DIMREASONBUFFER];
  int i,err,err2;
  int inhibitflag = 0;
  unsigned n;
  if(FUNCTOR(t) != LIMIT)
     return 1;
  n = ARITY(t);
  u = LIMITAND(t);
  x = ARG(0,ARG(0,t));  /* the limit variable */
  if(FUNCTOR(arg) == ILLEGAL && !FRACTION(u))
     return 1;
  if(FUNCTOR(arg) == ILLEGAL)  /* automode, select own arg */
     { err = select_divnumdenom_arg(t,&arg);
       if(err)
          return 1;
     }
  HIGHLIGHT(arg);
  c = make_fraction(ARG(0,u),arg);
  if(FUNCTOR(ARG(0,u)) == '+')
     { err = apartandcancel(c,zero,&cc,localbuf);
       if(err)   /* example:  (�x + 3)/x => �x/x + 3/x  */
           err = apart(c,zero,&cc,localbuf);
     }
  else
     err = cancel(ARG(0,u),arg,&trash,&cc);
  if(err)
     cc = c;
  else
     { ++inhibitflag;
       SET_ALREADY(cc);  /* limval calls polyval; without this it will
                            undo what apartandcancel did, and cause a loop */
     }
  if(equals(arg,ARG(1,u)) && !err) /* example  u = (5+x)/x */
     { strcpy(reason,localbuf);
       *next =  n==2 ? limit(ARG(0,t),cc) : limit3(ARG(0,t),ARG(1,t),cc);
       return 0;
     }
  d = make_fraction(ARG(1,u),arg);
  if(FUNCTOR(ARG(1,u)) == '+')
     { err = apartandcancel(d,zero,&dd,localbuf);
       if(err)
           err = apart(d,zero,&dd,localbuf);
     }
  else
     err = cancel(ARG(1,u),arg,&trash,&dd);
  if(err)
     dd = d;
  else
     { ++inhibitflag;
       SET_ALREADY(dd);  /* limval calls polyval; without this it will
                            undo what apartandcancel did, and cause a loop.
                            Actually, this is ineffective, as polyval calls
                            UNSET_ALREADY and uses common denoms anyway,
                            when working on fractions.
                          */
     }
  for(i=0;i<inhibitflag;i++)
     { /* inhibit all common denominator operators */
       inhibit(commondenom);
       inhibit(commondenomandsimp);
       inhibit(commondenom2);
       inhibit(commondenomandsimp2);
       inhibit(polyvalop);
     }
  newnum = n==2 ? limit(ARG(0,t),cc) : limit3(ARG(0,t),ARG(1,t),cc);
  newdenom = n==2 ? limit(ARG(0,t),dd) : limit3(ARG(0,t),ARG(1,t),dd);
  err = limval(newnum,&u);
  if(err)
     { errbuf(0, english(587));
       /* MathXpert cannot evaluate the limit that would result. */
       goto fail;
     }
  err2 = limval(newdenom,&v);
  if(err2)
     { errbuf(0, english(587));
       /* MathXpert cannot evaluate the limit that would result. */
       goto fail;
     }
  if(NOTDEFINED(u) && NOTDEFINED(v))
     { errbuf(0, english(1945));
       /* The resulting numerator and denominator would both be undefined. */
       goto fail;
     }
  if(ZERO(u) && ZERO(v))
     { errbuf(0, english(1946));
       /* The resulting numerator and denominator would both be zero. */
       goto fail;
     }
  if(ZERO(v))
     { /* if the denominator goes to zero, you still might be able to
          use this if the sign of the denominator is known.
          Example:  lim(x->0, sin(x)/x^3), after dividing num and denom
          by x, we get 1/lim(x->0,x^2) = infinity since x^2 >= 0. */
       err = infer(lessthan(zero,newdenom));
       if(err)
          { err = infer(lessthan(newdenom,zero));
            if(err)
               { errbuf(0, english(1947));
                 /* The resulting denominator would be zero,
                     and MathXpert can't determine its sign. */
                 return 1;
               }
          }
     }
  *next = make_fraction(newnum,newdenom);
  strcpy(reason, "lim (u/v) =          lim(u/w) / lim(v/w)");
  if(!depends(arg,x))
     { inhibit(limquotient);  /* these will be released by changelimitvariable */
       inhibit(pulloutnonzerolimit);
       inhibit(lhopital);
       inhibit(limlinear);
     }
  HIGHLIGHT(c);
  return 0;
  fail:
  for(i=0;i<inhibitflag;i++)
     { /* release all common denominator operators */
       release(commondenom);
       release(commondenomandsimp);
       release(commondenom2);
       release(commondenomandsimp2);
       release(polyvalop);
     }
  return 1;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC int lhopital(term t, term arg, term *next, char *reason)
{ term u,v,x,w,val,test,r,a;
  double z,aval,oldval;
  int err;
  void  *savenode;
  if(FUNCTOR(t) != LIMIT)
     return 1;
  w = LIMITAND(t);
  if(FUNCTOR(w) != '/')
     return 1;
  u = ARG(0,w);
  v = ARG(1,w);
  x = ARG(0,ARG(0,t));
  a = ARG(1,ARG(0,t));
  savenode = heapmax();
  err = indeterminate_form(t);
  reset_heap(savenode);
  if(err==2)
    { errbuf(0, english(584));
         /* This limit is not an indeterminate form. */
      return 1;
    }
  if(err)
     { errbuf(0, english(585));
       errbuf(1, english(586));
       /* MathXpert cannot determine whether this is an indeterminate form or not */
       /* Sorry, but under those circumstances the rule can't be applied. */
       return 1;
     }
  polyval(make_fraction(derivative(u,x),derivative(v,x)),&r);
  /* Try to avoid needing to compute the limit of r;  if r is
  defined at x = a  then (unless r is very unusual) the limit
  will exist.  If we apply L'Hopital in such an unusual situation
  no harm is done anyway as the limit won't be wrongly evaluated.
  */
  if(seminumerical(a) && !deval(a,&aval) && aval != BADVAL)
     { oldval = VALUE(x);
       SETVALUE(x,aval);
       if(NEGATIVE(r))
          r = ARG(0,r);
       if(FRACTION(r))
          { deval(ARG(1,r),&z);
            SETVALUE(x,oldval);
            if(z != BADVAL && fabs(z) > VERYSMALL)
               goto success;
          }
       else
          { deval(r,&z);
            SETVALUE(x,oldval);
            if(z != BADVAL)
               goto success;
          }
     }
  if(contains_trig(r))
     r = trigsimp(r,x); /* example, r = sin x cos x/ (sec x tan x) */
  if(ARITY(t)==2)
    test = limit(ARG(0,t),r);
  else
    test = limit3(ARG(0,t),ARG(1,t),r);
  err = limval(test,&val);
  if(err)
     goto fail1;
  if(equals(val,undefined))

     { /* It's still OK if both one-sided limits are plus or minus infinity. */
       test = limit3(ARG(0,t),left,r);
       err = limval(test,&val);
       if(err)
          goto fail1;
       if(NOTDEFINED(val) && !ISINFINITE(val))
          goto fail2;
       test = limit3(ARG(0,t),right,r);
       err = limval(test,&val);
       if(err)
          goto fail1;
       if(NOTDEFINED(val) && !ISINFINITE(val))
          goto fail2;
       /* even if the two limits are different, it's still legal to
       apply L'Hopital! */
     }
  if(NOTDEFINED(val) && !ISINFINITE(val))
      goto fail2;
  success:
  if(ARITY(t)==2)
    *next = limit(ARG(0,t),make_fraction(diff(u,x),diff(v,x)));
  else
    *next = limit3(ARG(0,t),ARG(1,t),make_fraction(diff(u,x),diff(v,x)));
  HIGHLIGHT(*next);
  strcpy(reason, english(588)); /* L'H�pital's rule */
  return 0;
  fail1:
      { errbuf(0, english(587));
       errbuf(1, english(586));
       /* MathXpert cannot internally evaluate the limit that would result.
          Sorry, but under those circumstances the rule can't be applied.
       */
       return 1;
     }
  fail2:
     { errbuf(0, english(1407));
       errbuf(1, english(1408));
       errbuf(2, english(1409));
       /* The resulting limit would not be finite or infinite, but
          would be undefined in some more complicated way.  Under those
          circumstances L'H\^opital's rule cannot be correctly applied.
       */
       return 1;
     }
}
/*_________________________________________________________________*/
static int indeterminate_form(term t)
/* t must be a limit term.  Use limval to see if it's OK
to proceed with L'H�pital's rule.  Return values:
   0 means successfully verified that t is an indeterminate form
   2 means successfully showed it is NOT an indeterminate form
   1 means couldn't tell.
*/

{ term v,w,limv[2],limv1[2],limv2[2];
  int i,err;
  unsigned n;
  if(FUNCTOR(t) != LIMIT)
     return 1;
  n = ARITY(t);
  w = LIMITAND(t);
  if(FUNCTOR(w) != '/')
     return 2;
  for(i=0;i<2;i++)
    { v = ARG(i,w);
      if(n==2)
         err = limval(limit(ARG(0,t),v),&limv[i]);
      else
        err = limval(limit3(ARG(0,t),ARG(1,t),v),&limv[i]);
      if(err && n ==3)
         return 1;
      if(err)
         return 1;
      if(equals(limv[i],undefined))
           /* compute the one-sided limits.  It's still an indeterminate form
              if the limit on one side is infinity and on the other is minus infinity */
         { err = limval(limit3(ARG(0,t),left,v),&limv1[i]);
           if(err)
              return 1;
           if(!equals(limv1[i],zero)&& !equals(limv1[i],infinity) && !equals(limv1[i],minusinfinity))
              return 2;
           err = limval(limit3(ARG(0,t),right,v),&limv2[i]);
           if(err)
              return 1;
           if(!equals(limv2[i],zero)&& !equals(limv2[i],infinity) && !equals(limv2[i],minusinfinity))
              return 2;
           if(ISINFINITE(limv1[i]) && ISINFINITE(limv2[i]))
             limv[i] = limv1[i];
         }
     if(!equals(limv[i],zero)&& !equals(limv[i],infinity) && !equals(limv[i],minusinfinity))
         return 2;
    }
  if(equals(limv[0],limv[1]))
     return 0;
  if(ISINFINITE(limv[0]) && ISINFINITE(limv[1]))
     return 0;
  return 2;
}
/*______________________________________________________________*/
static int determine_sign(term t)
/* t is a limit term, known to have value zero.  Determine the sign
of the limitand in the relevant neighborhoods, and return 1 if positive,
-1 if negative, 0 if both signs or can't determine.
   fill_binders has already been called, but just in case, it checks
for nonzero binders and calls fill_binders if binders is NULL.
*/

{ term u;
  int ans,flag;
  flag = get_binders() ? 1 : 0;
  u = LIMITAND(t);
  if(!flag)
     fillbinders(t);
  if(!infer(le(zero,u)))
     ans = 1;
  else if(!infer(le(u,zero)))
     ans = -1;
  else
     ans = 0;
  if(!flag)
     releasebinders();
  return ans;
}

#if 0  // obsolete code
{ term val;
  approach l,r;
  unsigned n = ARITY(t);
  int err = limval_aux(0,t,&val,&l,&r);
  if(err)
     return 0;
  if( (n==3 && equals(ARG(1,t),right)) ||
      (n==2 && equals(ARG(1,ARG(0,t)),minusinfinity))
    )
     { if(r == min)
          return 1;
       if(r == max)
          return -1;
       else
          return 0;
     }
  if( (n==3 && equals(ARG(1,t),left)) ||
      (n==2 && equals(ARG(1,ARG(0,t)),infinity))
    )
     { if(l == min)
          return 1;
       if(l == max)
          return -1;
       else
          return 0;
     }
  assert(n==2);
  if(l==min && r == min)
     return 1;
  if(l==max && r == max)
     return 1;
  return 0;
}
#endif
/*___________________________________________________________*/
MEXPORT_TRIGCALC int factorunderlimit(term t, term arg, term *next, char *reason)
/* factor out (x-a)^n in limit as x->a */
{ term u,v,w,p,q,u1,v1,u2,v2,x,a;
  unsigned n,m,k;
  int err,err2;
  if(FUNCTOR(t) != LIMIT)
     return 1;
  n = ARITY(t);
  w = LIMITAND(t);
  if(FUNCTOR(w) != '/')
     return 1;
  u = ARG(0,w);
  v = ARG(1,w);
  x = ARG(0,ARG(0,t));
  a = ARG(1,ARG(0,t));  /* lim(x->a,u/v)  */
  if(ZERO(a))
     { err = content_factor(u,&p,&u1);
       if(err || contains(p,FUNCTOR(x)))
          { u1 = u;
            err = 1;
          }
       else
          u1 = product(p,u1);
       err2 = content_factor(v,&q,&v1);
       if(err2 || !contains(q,FUNCTOR(x)))
          { v1 = v;
            err2 = 1;
          }
       else
          v1 = product(q,v1);
       if(err && err2)
          return 1;
       q = make_fraction(u1,v1);
       HIGHLIGHT(q);
       *next = n==3 ? limit3(ARG(0,t),ARG(1,t),q) : limit(ARG(0,t),q);
       strcpy(reason, english(1374)); /* factor out in limit*/
       return 0;
     }
  p = sum(x,tnegate(a));
  if(FUNCTOR(u) != '+')
     err = 3;
  else
     err = divideoutpowers(x,p,u,&m,&u1);
  if(err)
     { u2 = u;
       m = 0;
       u1 = one;
     }
  else
     { u2 = m == 1 ? product(p,u1) : product(make_power(p,make_int(m)),u1);
       PROTECT(u2);
     }
  if(FUNCTOR(v) != '+')
     err2 = 3;
  else
     err2 = divideoutpowers(x,p,v,&k,&v1);
  if(err2)
     { v2 = v;
       v1 = one;
       k = 0;
     }
  else
     { v2 = k == 1 ? product(p,v1) : product(make_power(p,make_int(k)),v1);
       PROTECT(v2);
     }
  if(err && err2)
     { if(err == 2 || err2 == 2)
          errbuf(0, english(1372));
         /* That operation only factors a polynomial numerator or denominator. */
       else if(err == 3 || err2 == 3)
          errbuf(0, english(1373));
         /* The numerator or denominator must be a sum of monomials. */
       return 1;
     }
  q = make_fraction(u2,v2);
  if(equals(q,w) || (ONE(u1) && ONE(v1)))
     return 1;  /* No net change, e.g. x/(x-6) with a = 6 */
             /* but don't just test equals(q,w) as that misses x/(2+x) = x/(x+2) */
  HIGHLIGHT(q);
  *next = n==3 ? limit3(ARG(0,t),ARG(1,t),q) : limit(ARG(0,t),q);
  strcpy(reason, english(1374)); /* factor out in limit */
  return 0;
}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists