Sindbad~EG File Manager

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

/*  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
*/

#include <string.h>
#include <assert.h>
#include <math.h>
#define TRIGCALC_DLL
#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 "document.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);
/*____________________________________________________________________*/
MEXPORT_TRIGCALC 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 u = e^(lim ln u)");
   return 0;
}
/*___________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*__________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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 if(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;
}

/*_________________________________________________________________*/

MEXPORT_TRIGCALC int expundefined(term t, term arg, term *next, char *reason)
/* succeed if t is a quotient of limits that evaluate to 0/0 or
� infinity / �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;
    }
  out:  *next = make_fraction(ans1,ans2);
  HIGHLIGHT(*next);
  return 0;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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��,e^x) = e^� */
  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);
  *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==ABS)  /* functor_string doesn't handle ABS */
     { 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;
}
 /*_________________________________________________________________*/
MEXPORT_TRIGCALC 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 u^v =             lim e^(v ln u)");
  return 0;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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)�  */

{ 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC int negexptodenom(term t, term arg, term *next, char *reason)
/* lim u^-n v = lim v/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 = 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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 */
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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�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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_____________________________________________________________*/
MEXPORT_TRIGCALC 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);
}
/*_____________________________________________________________*/
MEXPORT_TRIGCALC 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);
}
/*_____________________________________________________________*/
MEXPORT_TRIGCALC 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);
}
/*_____________________________________________________________*/
MEXPORT_TRIGCALC int liminfcos(term t, term arg, term *next, char *reason)
/*  lim(t->� infinity, cos t) = undefined */
/*  internally the value is bounded_oscillations, which prints out
    as 'undefined'  */
{ return osc_aux(COS,t,next,reason);
}
/*_____________________________________________________________*/
MEXPORT_TRIGCALC int liminfsin(term t, term arg, term *next, char *reason)
/*  lim(t->� infinity, sin t) = undefined */
/*  internally the value is bounded_oscillations, which prints out
    as 'undefined'  */
{ return osc_aux(SIN,t,next,reason);
}
/*_____________________________________________________________*/
MEXPORT_TRIGCALC int liminftan(term t, term arg, term *next, char *reason)
/*  lim(t->� 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->� 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�""��,cos t)       is undefined */
               break;
            case SIN:
               strcpy(reason,english(1907));
               SetShowStepOperation(liminfsin);
               /* lim(t�""��,sin t)       is undefined */
               break;
            case TAN:
               strcpy(reason,english(1908));
               SetShowStepOperation(liminftan);
               /* lim(t�""��,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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC int limpowertimeslnabs(term t, term arg, term *next, char *reason)
/* lim(x->0, x� 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) != ABS && FUNCTOR(v) != ABS)
     return 1;
  if(FUNCTOR(v) != ABS)
     { 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->0,x� 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) ||
           (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) ||
           (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;
}
/*______________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*______________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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� */
      strcat(reason, english(923));  /* 0 as t�      */
      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;
}

/*_________________________________________________________________*/
MEXPORT_TRIGCALC 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