Sindbad~EG File Manager

Current Path : /usr/home/beeson/Otter-Lambda/yyy/automode/
Upload File :
Current File : /usr/home/beeson/Otter-Lambda/yyy/automode/postops.c

/* post_ops: post-associated operators, used together with pre_ops
   to describe the automode simplification algorithm of Mathpert.
M. Beeson
Original date 1.7.91
Last modified 3.30.99
1.4.00 added tanhi and cothi;
1.4.00 added code to get sinhdef, tanhdef, cothdef, coshdef used
       when the argument is a natural log.
1.5.00 added oksqrt and oksqrt_aux
1.13.00 added code to get sin((arctan(8)-pi)/2) simplified by apart
1.13.00 added secrecip and cscrecip
1.15.00 modified conditions for rectangulartopolar and complexexponential
2.27.00 added missing 'break' at line 1429
2.27.00 added statefinalbound1 and statefinalbound2
3.2.00  added call to series_ops
4.2.00  added intervaltoabs1 and intervaltoabs2
12.01.00 modified sqrtexp_conditions
6.17.04 modified post_ops so sqrtsimp isn't used on complex problems
6.18.04 modified conditions for complexexponential in postops so it's not used inside SQR or ROOT.
        put in complexsqrt and complexroot 
        removed the modification made yesterday about sqrtsimp.
6.21.04 modified conditions for rectangulartopolar        
        Added !is_complex at line 1985
8.20.04 Added pathlength==0 etc. at line 1755 to stop expanding (x^2+y^2)^3 at toplevel        
1.23.06 Added sumtodifofsums0 
1.25.06 added rejectpoint under MATRIX
1.28.06 don't use introducelninexponent on a power that's a summand, see line 2063.
        Modified logbases,  so that it won't count terms of the form logb(b,b^z).
*/

#define AUTOMODE_DLL
#include <assert.h>
#include <string.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "document.h"
#include "tdefn.h"
#include "checkarg.h" /* for operator typedef */
#include "ops.h"  /* for prototypes of operators */
#include "operator.h"
#include "trig.h"
#include "calc.h"
#include "series.h"
#include "probtype.h"  /* set_control_flags needs values of problemtype */
#include "prover.h"
#include "polynoms.h"
#include "exec.h"
#include "algaux.h"
#include "order.h"
#include "automode.h"
#include "symbols.h"
#include "pvalaux.h"   /* complex_number     */
#include "cflags.h"    /* get_currenttopic   */
#include "cancel.h"    /* contains_undefined */
#include "autosimp.h"  /* get_whichpass etc  */
#include "ssolve.h"    /* solved             */
#include "maxsub.h"    /* locate             */
#include "nfactor.h"   /* small_prime        */
#include "eqn.h"       /* econstant          */
#include "trigpoly.h"  /* stricttrigpoly     */
#include "preops.h"    /* get_trigpolyflag, algebraic_identity */
#include "islinear.h"  /* is_linear          */
#include "autosum.h"   /* contains_arctrigs  */
#include "sqrts.h"     /* fractexps          */
#include "deval.h"     /* deval              */
#include "relrates.h"  /* used               */
#include "surdsimp.h"  /* canonical          */
#include "binders.h"   /* fill_binders       */
#include "domain.h"    /* contains_defined_variables */
#include "scontrol.h"  /* series_ops         */

static int fractional_factor(term);
static int sqrt_condition(term,term);
static int contains_integer_sqrt(term);
static int contains_integer_root(term,term);
static int in_sum(void);
static int unsolved_lineqns(term t);
static int elimfractexp_conditions(unsigned pathlength, unsigned short *path);
static int ok_complexexp(unsigned short *path, int pathlength);
static int stop_logofproduct(term t);
static int stop_changebase(void);
static int rationalizable_cubic(term denom);
static int insumfract(void);
static int sg_ok(void);
static int solved_cubic(term t);
static int oksqrt(term t);
static void autoexp(term t,actualop *o, int *nops);
static void autoquo(term t,actualop *o, int *nops);
/*_____________________________________________________________________*/
static int hflag;
/* set in preops under '=' when the identity contains exponentials AND
cosh or sinh, or on topic _hyperfunctions1, where you are supposed to
convert hyperbolic functions to exponentials.
   If nonzero, coshdef and sinhdef can be used when whichpass == 0.
If zero, they have to wait until whichpass > 1.  Before hflag existed,
they always waited till whichpass > 1, which means common denoms sneak in
first, making ugly solutions.
*/
void set_hflag(int n)
{ hflag = n;
}

int get_hflag(void)
{ return hflag;
}
/*_____________________________________________________________________*/

void post_ops(term t, term *arg, actualop *o, int *nops)
/* fill in operators o[i] that might work on t;  use  *arg to supply
one argument for such an operator.  *arg is used only in automode solving
of equations and linear equations and when it used to return an arg, the
operator in question must certainly work.  */


{ unsigned short f = FUNCTOR(t);
  term u;
  unsigned short g;
  int i=0;
  double z;
  int whichpass = get_whichpass();
  const int intflag = get_intflag();
  const int indexflag = get_indexsumflag();
  const int seriesflag = get_seriesflag();
  int currenttopic = get_currenttopic();
  int problemtype = get_problemtype();
  int pathlength = get_pathlength();
  unsigned short  *path = get_path();
  int trigexpandflag, factorflag, expandflag, logcollectflag,radicalflag;
  int trigpolyflag = get_trigpolyflag();
  term cancelled, trash,v,w;
  if(ISINTEGER(t))
     { if(pathlength >= 2 && path[pathlength-2] == LOGB && path[pathlength-1] == 2)
          { o[i] = writeintegeraspower; ++i;  /* log(2,16) => log(2,2^4) */
          }
     }
  if(ATOMIC(t))
     { if(currenttopic == _minmax && equals(t,false))
          { o[i] = addendpoints; ++i;
            o[i] = addundefinedpoints; ++i;
            *nops = i;
            return;
          }
       else if(problemtype == INEQUALITIES && equals(t, true))
          { o[i] = explicitdomain; ++i;
            *nops = i;
            return;
          }
       else if(equals(t,complexi) &&
               pathlength <= 5 &&
               currenttopic == _polar_form &&
               !in_sum() &&
               !(pathlength >=2 && path[0] == '^' && path[1] == 2)
               /* don't do it on e^( pi i), or e^((pi/2) i), etc. */
              )
          { o[i] = rectangulartopolar; ++i;
            *nops = i;
            return;
          }
       goto finish;
     }
  if(is_complex(t) && pathlength >= 3 && 
     (path[pathlength-3] == SQRT || path[pathlength-3] == ROOT) &&
     (path[pathlength-3] == ROOT || complexparts(ARG(0,t),&w,&v) != 0)  &&
      /* sqrt(a+bi) is NOT computed by polar form, so only proceed if can't compute u = a+bi  */
      (
        (ATOMIC(t) || f == '*' || f == '^' || f == '/' ) ||
        (f == '-' && 
           ( ATOMIC(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '*' || 
             FUNCTOR(ARG(0,t)) == '^' || FUNCTOR(ARG(0,t)) == '/' 
           )
        )
      )
     )
      { o[i] = rectangulartopolar; ++i;  /* sqrt(u) with u complex; bring u to polar form */
      } 
       
  trigexpandflag = get_trigexpandflag();
  expandflag = get_expandflag();
  factorflag = get_factorflag();
  logcollectflag = get_logcollectflag();
  radicalflag = get_radicalflag();
  if(whichpass == 5 &&   /* don't do this till the very end */
     ARITY(t) == 1 && OBJECT(ARG(0,t)) && TYPE(ARG(0,t)) == DOUBLE &&
     f != SQRT &&  /* computeroot will be used */
     f != '-' && f != DEG
    )
     { o[i] = computefunction; ++i;
       goto finish;
     }
  if(TRIGFUNCTOR2(f)  &&  /* trig and arctrig functors */
     (! ((trigexpandflag & 2) && (!trigexpandflag & 1))
            /* bit 1 set means only one trig function occurs, and bit 0
               zero means all trig functions have the same arg,
               in which case it's a purely algebraic problem */
      || seminumerical(ARG(0,t)) /* You still need to evaluate trig functions
                                  even if only one occurs */
     )
    )
     { autotrig(t,o,nops);
       i = *nops;
       goto finish;
     }
  switch(f)
    { case '-' :
         u = ARG(0,t);
         if(currenttopic == _polar_form && pathlength <= 1  &&
            ( equals(u,complexi) || ATOMIC(u) || (FUNCTOR(u) == '*' && iscomplex(u)))
           )
            { if(ATOMIC(u))
                 { o[i] = minustopolar; ++i;
                 }
              else
                 { o[i] = rectangulartopolar; ++i;
                 }
            }
         if(currenttopic == _polar_form && pathlength <= 5 &&
            !inpowerflag && !get_infractionflag() &&
            !in_sum()
           )
            { o[i] = minustopolar; ++i;
            }
         else if(FRACTION(u) &&
            FUNCTOR(ARG(0,u)) == '+' &&
            NEGATIVE(ARG(1,ARG(0,u)))
           )
            { o[i] = minusintonum; ++i;
            }
         else if(FUNCTOR(u) == '*' &&
                 FRACTION(ARG(0,u)) &&
                 FUNCTOR(ARG(0,ARG(0,u))) == '+' &&
                 NEGATIVE(ARG(1,ARG(0,ARG(0,u))))
                )
            { o[i] = minusintonum; ++i;
              /* example:  -((e^-x - e^x)/2)i */
            }
         else if(FRACTION(u) && pathlength >= 3 &&
            path[pathlength-3] == SQRT && FUNCTOR(ARG(0,u)) == '+'
           )
            { o[i] = minusintonum; ++i;
            }

         if(FRACTION(u) && pathlength >= 3 &&
            path[pathlength-3] == SQRT && FUNCTOR(ARG(1,u)) == '+'
           )
            { o[i] = minusintodenom; ++i;
            }
         if(FRACTION(u))
            { o[i] = minusoutfromdenom33; ++i;   /* -a/(b-c) = a/(c-b)  */
              o[i] = minusoutfromdenom22; ++i;   /* -a/(-b-c) = a/(b+c) */
              o[i] = minusoutfromnum22; ++i;     /* -(-a-b)/c = (a+b)/c */
            }
         break;
      case '+' :
         autosum(t,o,&i);
         break;
      case '*' :
         autoproduct(t,o,&i);
         break;
      case '^' :
         autoexp(t,o,&i);
         break;
      case '/' :
         autoquo(t,o,&i);
         break;
      case MATRIX:
         if(problemtype == MINMAX)
            { o[i] = rejectpoint; ++i;
              o[i] = addlimits; ++i;
              o[i] = selectmax; ++i;
              o[i] = selectmin; ++i;
              break;
            }
      case MATRIXINVERSE:
         if(numerical(t))
             { o[i] = exactmatrixinverse; ++i;
               o[i] = decimalmatrixinverse; ++i;
             }
         break;
      case SQRT:
         u = ARG(0,t);
         if(OBJECT(u) && TYPE(u) == DOUBLE && whichpass == 5)
            { o[i] = computeroot; ++i;
              goto finish;
            }
         if(INTEGERP(u) || FUNCTOR(u) == '*')
            { o[i] = factorundersqrt; ++i;
            }
         if(OBJECT(u))
            { o[i] = evaltorational; ++i;
            }
         if(NOTDEFINED(u))
            { o[i] = sqrtinfinity; ++i;
            }
         if((!get_complex() || seminumerical(u)) && sqrtexp_conditions(u))
            { o[i] = sqrtexp; ++i;
              break;
            }
         if(ISATOM(u))
            { if(equals(u,complexi))
                 { o[i] = sqrtofi; ++i;
                 }
              break;
            }
         g = FUNCTOR(u);
         switch(g)
            { case '*' :
                 o[i] = sqrtsimp; ++ i;  /* sqrt (x^2 y) = x sqrt y */
                 if(is_complex(u))
                    { o[i] = sqrtofi; ++i;
                      o[i] = complexsqrt; ++i;  /* sqrt( a e^it) = sqrt(a) e^(it/2) */
                    }
                 if(
                     !factorflag ||
                     (innumflag && !(factorflag & 101)) ||
                     (indenomflag && !(factorflag & 0x110))
                   )
                   /* sqrt((a-b)(a+b)) = sqrt(a^2-b^2)  */
                    { o[i] = multiplyoutundersqrt; ++i;
                    }
                 break;
              case '^' :
                 o[i] = sqrtsimp; ++ i;  /* sqrt(x^2y) = x sqrt y */
                 if(is_complex(u))
                    { o[i] = complexsqrt; ++i;  /* sqrt( a e^it) = sqrt(a) e^(it/2) */
                    }
                 break;
              case '-' :
                 /* sqrtofminus1 and sqrtofneg are in preops */
                 if(is_complex(t))
                    { o[i] = sqrtofminusi; ++i;
                    }
                 break;
              case '/' :
                 if((!get_complex() || seminumerical(t)) && 
                    sqrt_condition(history(get_currentline()),t) && 
                    !limitflag
                   )
                    { o[i] = sqrtofquotient2; ++i;
                      /* This introduces absolute value signs rather than
                         make assumptions about the signs of the new sqrts */
                    }
                 break;
/* Example:   sqrt(2/3) is left alone but sqrt(2/3) + 5 is
   rewritten sqrt 2/sqrt 3 + 5 = (sqrt 2 + 5 sqrt 3)/sqrt 3 = (sqrt 6 + 15) /3.
   Conditions for sqrtofquotient here must mesh with
   those for quotientofsqrts in pre_ops to avoid a loop. */

              case '+' :
                 o[i] = factorpolyundersqrt; ++i;
                 if(intflag) /* but not indexflag or seriesflag */
                    { o[i] = completethesquare1; ++i;
                    }
                 if(is_complex(t))
                    { o[i] = sqrtofaminusbi; ++i;
                      o[i] = sqrtofaplusbi; ++i;
                    }
                 break;
              case SQRT:
                 o[i] = sqrtofsqrt; ++i;
                 break;
              case ROOT:
                 o[i] = sqrtofroot; ++i;
                 break;
            }
         break;
      case ROOT:
         u = ARG(1,t);
         if(OBJECT(u) && TYPE(u) == DOUBLE && whichpass == 5)
            { o[i] = computeroot; ++i;
              goto finish;
            }
         if(INTEGERP(u) || FUNCTOR(u) == '*')
            { o[i] = factorunderroot; ++i;
            }
         if(NOTDEFINED(u))
            { o[i] = rootinfinity; ++i;
              goto finish;
            }
         if((radicalflag < 0 && !algebraic_number(ARG(1,t))) ||
            iscomplex(ARG(1,t)) ||
            ( get_complex()  &&
              NEGATIVE(ARG(1,t)) && obviously_nonnegative(ARG(0,ARG(1,t)))
              /* whether odd or even, when computing complex roots of
                 negative numbers use fractional exponents. */
            )
           )
            { if(FUNCTOR(ARG(1,t)) == '^')
                 { o[i] = rootpowerexp; ++i;
                 }
              else
                 { o[i] = rootexp; ++i;
                 }
              break;
            }
         if(ISATOM(u))
            { i = 0;
              goto finish;
            }
         if(OBJECT(u))
            { o[i] = evaltorational; ++i;
            }
         if(equals(ARG(0,t),two))
            { o[i] = roottosqrt; ++i;
              break;
            }
         if(seminumerical(u) && !NEGATIVE(u) && !get_complex())
            { o[i] = rootofminus; ++i;
              /* Needed in solving cubic equations, to simplify e.g.
                 root(3, 1/2 - (1/18)sqrt 93).  But when complex
                 numbers are on, we use complexrootminus instead,
                 which is in preops to preempt rootexp. */
            }
         g = FUNCTOR(u);
         switch(g)
            { case '-' :
                 if(!seminumerical(u))
                    { o[i] = rootofminus; ++i;
                    }
                 else if(!deval(u,&z) && z != BADVAL && z < 0.0)
                    { o[i] = rootofminus; ++i;
                    }
                 /* but don't use rootofminus on e.g. root(3,-2(1-sqrt(3)));
                    keep a positive quantity under the root. */
                 break;
              case '/' :
                 if( !limitflag &&
                     (  !equals(t,history(get_currentline()))
                        || (INTEGERP(ARG(0,u)) && !rootfree(ARG(0,u),(unsigned) INTDATA(ARG(0,t))))
                        || (INTEGERP(ARG(1,ARG(0,t))) && !rootfree(ARG(1,ARG(0,t)),(unsigned) INTDATA(ARG(0,t)) ))
                                /* indices of roots never exceed 0xffff */
                        || contains(ARG(0,u),'^')
                        || contains(ARG(1,u),'^')
                     )
                   )
                    { o[i] = rootofquotient; ++i;
                    }
                 break;
              case '*' :
                 /* rootsimp is in preops.  It need not also be here */
                 if(!factorflag ||
                    (innumflag && !(factorflag & 1)) ||
                    (indenomflag && !(factorflag & 0x10))
                   )
                   /* ��((a-b)(a+b)) = ��(a^2-b^2)  */
                    { o[i] = multiplyoutunderroot; ++i;
                    }
                 if(is_complex(u))
                    { o[i] = complexroot; ++i;
                    }
                 break;
              case '^' :
                 if(radicalflag == -1)
                    break;
                 o[i] = rootofpower2; ++i;
                 o[i] = rootofpower; ++i;
                 if(is_complex(u))
                    { o[i] = complexroot; ++i;
                    }
                 break;
              case '+' :
                 if(NEGATIVE(ARG(0,u)))
                    { o[i] = rootofminus; ++i;
                    }
                 o[i] = factorpolyunderroot; ++i;
                 break;
              case SQRT:
                 o[i] = rootofsqrt; ++i;
                 break;
              case ROOT:
                 o[i] = rootofroot; ++i;
                 break;
            }
         break;
      case LOG:
         u = ARG(0,t);
         if(FUNCTOR(u) == ABS)
            u = ARG(0,u);
         if(NOTDEFINED(u))
            { o[i] = lninfinity; ++i;
              break;
            }
         if(OBJECT(u))
            { o[i] = logofone; ++i;
              o[i] = logoften; ++i;
              if(!inpowerflag && !logcollectflag)
                 { o[i] = factoroutbase10 ; ++i;
                   /* Note, pathlength is even when OBJECT(u), since objects
                      are NOT tacked onto the end of the path */
                   if((pathlength >= 2 && path[pathlength-2] == '+') ||
                      (pathlength >= 4 && path[pathlength-2] == '-' && path[pathlength-4] == '+') ||
                      currenttopic == _logarithms
                     )
                      /* No point in factoring inside a log
                         unless the factored and expanded log
                         has something to combine with. */
                      { o[i] = factornumberinlog; ++i;
                      }
                 }
            }
         g = FUNCTOR(u);
         if(g == '^' && equals(ARG(0,u),ten))
            { o[i] = logofpowerof10; ++i;
            }
         if( (inpowerflag || logcollectflag) &&
              /* leave LOG alone in exponents or when collecting logs */
             (g != '*' || SOLVETYPE(problemtype)) &&
              /* except logs of products with only one
                 nonconstant factor can still be expanded, but not when solve equations. */
             problemtype < LIMITS
           )
            break;
         if(contains(history(get_currentline()),LN) ||
            problemtype >= LIMITS
           )
            { o[i] = logtoln; ++i;
            }
         switch(g)
            { case '^':
                 /* logofpower is in preops */
                 break;
              case '*':
                 if(limitflag)
                    break;
                 if(inpowerflag || problemtype >= LIMITS)
                    break;
                 if(!logcollectflag  && !stop_logofproduct(t))
                    { o[i] = logofproduct; ++i;
                    }
                  break;
               case '/':
                  if(limitflag)
                     break;
                  o[i] = logofreciprocal; ++i;
                  if(!(pathlength == 1 && !is_linear(ARG(0,u)) &&
                       !constant(ARG(0,u)) && !is_linear(ARG(1,u))
                       /* is_linear returns 0 on linear arguments */
                      )
                    )
                     { o[i] = logofquotient; ++i;
                     }
                  break;
             }
         break;
      case LN:
         u = ARG(0,t);
         if(FUNCTOR(u) == ABS)
            u = ARG(0,u);
         if(ZERO(u))
            { o[i] = lnzero; ++i;
              break;
            }
         if(NOTDEFINED(u))
            { o[i] = lninfinity; ++i;
              break;
            }
         if(equals(u,eulere))
           { o[i] = lnofe; ++i;
           }
         if(OBJECT(u))
            { o[i] = lnofone; ++i;
              if(!inpowerflag && !logcollectflag)
                 { o[i] = factornumberinlog; ++i;
                 }
            }
         if(iscomplex(u))
            { if(equals(u,complexi))
                 { o[i] = lnofi; ++i;
                 }
              else
                 { o[i] = complexln; ++i;
                   o[i] = complexlntopolarform; ++i;
                 }
            }
         g = FUNCTOR(u);
         if(g == '-')
            { o[i] = lnofnegative; ++i;
            }
         if(g == '^' && equals(ARG(0,u),eulere))
            { o[i] = lnofpowerofe; ++i;
            }
         else if(g == '+' &&
                 limitflag && !inpowerflag &&
                 ONE(ARG(0,u)) &&
                 problemtype == DIFFERENTIATE_FROM_DEFN
                 /* We need this to differentiate ln x from defn,
                 but all other times it makes trouble */
                )
            { /* ln(1+b) = b ln((1+b)^(1/b)),
                 but only inside a limit and NOT in a power,
                 because if done in a power it screws up the
                 results of limexptolog, e.g. on lim(x->0, (1-2x)^x)
                 and only if b is going to zero.
                 The operator itself checks the latter condition.
                 This operator is needed to diff ln x from defn.
              */
              o[i] = lnofpowerreverse; ++i;
            }
         if(inpowerflag || logcollectflag)
            /* leave LN alone in exponents or when collecting logs */
            /* Exception:  ln x = a + ln x^2 for example; this kind of
               equation doesn't yield to attract-collect-isolate.
               These examples are handled in auto mode by an operator
               that works on equations, called unitelogargs  */
            break;
         switch(g)
            { case '^':
                 /* lnofpower is in preops  */
                 break;
              case '*':
                 if(limitflag)
                    break;
                 if(!logcollectflag)
                    { o[i] = lnofproduct; ++i;
                    }
                 break;
              case '/':
                 if(limitflag || (problemtype >=LIMITS && !intflag && !difflag))
                    break;
                  if(!(pathlength == 1 && !is_linear(ARG(0,u)) &&
                       !constant(ARG(0,u)) && !is_linear(ARG(1,u))
                       /* is_linear returns 0 on linear arguments */
                      )
                    )
                    { o[i] = lnofquotient; ++i;
                    }
                 break;

            }
         break;
      case LOGB:
         u = ARG(1,t);
         if(FUNCTOR(u) == ABS)
            u = ARG(0,u);
         if(equals(ARG(0,t),u))
            { o[i] = logbofb; ++i;
            }
         if(ONE(u))
            { o[i] = logbofone; ++i;
            }
         if(equals(ARG(0,t),eulere))
            { o[i] = logbtoln; ++i;   /* trap case of base e */
            }
         if(equals(ARG(0,t),ten))
            { o[i] = logbtolog; ++i;
            }
         if(pathlength >= 3 && path[pathlength-3] == DIFF)
            { o[i] = logbtolns; ++i;
            }
         g = FUNCTOR(u);
         if(currenttopic == _change_log_base)
            { if(g == '^' && equals(ARG(0,u),eulere))
                 { o[i] = logbtolns; ++i;
                 }
              else if(g == '^' && equals(ARG(0,u),ten))
                 { o[i] = logbtologs; ++i;
                 }
              /* and in other cases, e.g. log(3,7), should we change to log
                 or to ln?  Let's go for ln. */
              else
                 { o[i] = logbtolns; ++i;
                 }
            }
         if(OBJECT(ARG(0,t)) && !stop_changebase())
            { o[i] = factorlogbase; ++i;  /* e.g. log(36,6) = log(6^2,6) */
            }
         if(OBJECT(u))
            { o[i] = logpowerofbofb; ++i;
              if(!inpowerflag && !logcollectflag)
                 { o[i] = factoroutbase; ++i;
                   o[i] = factornumberinlog; ++i;
                 }
            }
         if(FUNCTOR(ARG(0,t))=='^')
            { o[i] = logpowerofbofb; ++i;
            }
         if(FRACTION(ARG(0,t)))
            { o[i] = changebase; ++i;  /* in log(1/5,x), change base to 5 */
              break;
            }
         if(inpowerflag)
            { term lastbase = get_lastbase();
              if(ATOMIC(lastbase) && !equals(lastbase,ARG(0,t)) &&
                 /* don't changebase in a compound fraction, simplify the
                    compound fraction first.  The following code only catches
                    the simplest kinds of compound fractions. */
                 (pathlength < 5 || ! (path[pathlength-5] == '/' && path[pathlength-3] == '/')) &&
                 (pathlength < 7 || ! (path[pathlength-7] == '/' && path[pathlength-5] == '/'))
                )
                 { o[i] = changebase; ++i;
                 }
            }
         if(logcollectflag &&
            get_whichpass() > 0 &&  /* don't use it on log(8,80)-log(8,5) */
            currenttopic != _change_log_base
            /* for that topic it's already been thrown in */
           )
            { if(!(pathlength >= 3 && INEQUALITY(path[pathlength-3])))
                 /* log(b,x) = a will go to x = b^a */
                 { o[i] = logbtolns; ++i;
                 }
              break;
            }
         switch(g)
            { case '^':
                 o[i] = logbofpowerofb; ++i;
                 o[i] = logbofpower; ++i;
                 break;
              case '*':
                 if(!logcollectflag)
                    { o[i] = logbofproduct; ++i;
                    }
                 break;
              case '/':
                 o[i] = logbofreciprocal; ++i;
                 if(!(pathlength == 1 && !is_linear(ARG(0,u)) &&
                      !constant(ARG(0,u)) && !is_linear(ARG(1,u))
                      /* is_linear returns 0 on linear arguments */
                     )
                   )
                    { o[i] = logbofquotient; ++i;
                    }
                 break;
            }
         break;
      case COSH:
         u = ARG(0,t);
         if(currenttopic == _hyperfunctions || FUNCTOR(u) == LN)
            { o[i] = coshdef; ++i;
              break;
            }
         if(NOTDEFINED(u))
             { o[i] = coshinfinity; ++i;
             }
         if(ZERO(u))
            { o[i]= cosh0; ++i;
            }
         if(NEGATIVE(u))
            { o[i] = cosheven; ++i;    /* cosh(-u) = cosh u       */
            }
         if(iscomplex(u) &&
            (FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
           )
            { o[i] = coshi; ++i;
            }

         if(!intflag && !indexflag && !seriesflag && !difflag && FUNCTOR(u) == '+' &&
            (!CALCULUS_TOPIC(currenttopic) || contains(u,FUNCTOR(get_eigenvariable()))) &&
            (problemtype != TRIG_IDENTITY || pathlength <= 3 ||
             path[pathlength-3] == '=' || path[pathlength-3] == '/'
            )
            /* in automode, only apply this law to one side of an equality.
               If cosh(u+v) occurs as a subterm, it's almost always better
               to expand in exponentials. */
           )
            { o[i] = coshsum; ++i;
            }
         if(!intflag && !indexflag && !seriesflag && !difflag && !limitflag &&
            (problemtype != TRIG_IDENTITY || pathlength <= 3 ||
             path[pathlength-3] == '=' || path[pathlength-3] == '/'
            ) &&
            !cancel(u,two,&cancelled,&trash)
           )
            { o[i] = doublecosh; ++i;
            }
         if(
            (problemtype == TRIG_IDENTITY || limitflag) &&
            (hflag || whichpass > 1)
           )
            { o[i] = coshdef; ++i;
            }
         if(problemtype == SIMPLIFY && INTEGERP(ARG(0,t)))
            { o[i] = coshdef; ++i;
            }

         break;
      case SINH:
         u = ARG(0,t);
         if(currenttopic == _hyperfunctions || FUNCTOR(u) == LN)
            { o[i] = sinhdef; ++i;
              break;
            }
         if(NOTDEFINED(u))
            { o[i] = sinhinfinity; ++i;
            }
         if(NEGATIVE(u))
            { o[i] = sinhodd; ++i;     /* sinh(-u) = -sinh u      */
            }
         if(iscomplex(u) &&
            (FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
           )
            { o[i] = sinhi; ++i;
            }
         if(!intflag && !indexflag && !seriesflag &&
            !difflag && FUNCTOR(u) == '+' &&
            /* but definitely use this in limits, e.g. to
               differentiate sinh from defn; but only when
               the sinh term contains the limit variable!
               Not only inside limits, but after the limit has been
               evaluated, there's still no use expanding cosh(3x-1) for example. */
            (!CALCULUS_TOPIC(currenttopic) || contains(u,FUNCTOR(get_eigenvariable()))) &&
            (problemtype != TRIG_IDENTITY || pathlength <= 3 ||
             path[pathlength-3] == '=' || path[pathlength-3] == '/'
            )
            /* in automode, only apply this law to one side of an equality.
               If sinh(u+v) occurs as a subterm, it's almost always better
               to expand in exponentials. */
           )
            { o[i] = sinhsum; ++i;
            }
         if(ZERO(u))
            { o[i] = sinh0; ++i;
            }
         if(!intflag && !indexflag && !seriesflag && !difflag && !limitflag &&
            !cancel(u,two,&cancelled,&trash) &&
            (problemtype != TRIG_IDENTITY || pathlength <= 3 ||
             path[pathlength-3] == '=' || path[pathlength-3] == '/'
            )
           )
            { o[i] = doublesinh; ++i;
            }
         if((problemtype == TRIG_IDENTITY || limitflag) &&
            (hflag || whichpass > 1)
           )
            { o[i] = sinhdef; ++i;
            }
         if(problemtype == SIMPLIFY && INTEGERP(ARG(0,t)))
           /* simplify sinh(1) but leave sinh(3/2) alone */
            { o[i] = sinhdef; ++i;
            }
         break;
      case SECH:
         u = ARG(0,t);
         if(seminumerical(u))
            { o[i] = sechdef; ++i;
            }
         if(
             (problemtype == TRIG_IDENTITY || limitflag ) &&
             (hflag || whichpass > 1)
           )
            { o[i] = sechdef; ++i;
            }
         break;
      case TANH:
         u = ARG(0,t);
         if(NOTDEFINED(u))
            { o[i] = sinhinfinity; ++i;
            }
         if(seminumerical(u) || FUNCTOR(u) == LN)
            { o[i] = tanhdef; ++i;
            }
         if(iscomplex(u) &&
            (FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
           )
            { o[i] = tanhi; ++i;
            }
         if(
            (problemtype == TRIG_IDENTITY || limitflag) &&
            (hflag || whichpass > 1)
           )

            { o[i] = tanhdef; ++i;
            }
         if(currenttopic == _hyperfunctions)
            break;
         if(FUNCTOR(u) == LN)
            { o[i] = tanhln; ++i;
            }
         break;
      case CSCH:
         u = ARG(0,t);
         if(seminumerical(u) || FUNCTOR(u) == LN)
            { o[i] = cschdef; ++i;
            }
         if(
            (problemtype == TRIG_IDENTITY || limitflag) &&
            (hflag || whichpass > 1)
           )
            { o[i] = cschdef; ++i;
            }
         break;
      case COTH:
         u = ARG(0,t);
         if(seminumerical(u) || FUNCTOR(u) == LN)
            { o[i] = cothdef; ++i;
            }
         if(iscomplex(u) &&
            (FUNCTOR(u) == '*' || FRACTION(u) || equals(u,complexi))
           )
            { o[i] = cothi; ++i;
            }
         if(
            (problemtype == TRIG_IDENTITY || limitflag) &&
            (hflag || whichpass > 1)
           )
            { o[i] = cothdef; ++i;
            }
         break;
      case ASINH:
         if(!difflag)
            { o[i] = asinhtoln; ++i;
            }
         break;
      case ATANH:
         if(!difflag)
            { o[i] = atanhtoln; ++i;
            }
         break;
      case ACOSH:
         if(!difflag)
            { o[i] = acoshtoln; ++i;
            }
         break;

      case SUM:
         if(!equals(ARG(3,t),infinity) && !equals(ARG(2,t),minusinfinity))
            { /* a finite summation */
              g = FUNCTOR(ARG(0,t));
              if(OBJECT(ARG(0,t)))
                 { o[i] = sigmaconstant; ++i;
                   goto finish;
                 }
              o[i] = sigmaconstant; ++i;
              if(equals(ARG(0,t),ARG(1,t)))
                 { if(!ZERO(ARG(2,t)) && !ONE(ARG(2,t)))
			          { o[i] = sumtodifofsums0;++i;
					  }
				   else
			          { o[i] = sumofi; ++i;
					  }
                 }
              if(g == '^' &&
                 equals(ARG(0,ARG(0,t)),ARG(1,t)) &&
                 ISINTEGER(ARG(1,ARG(0,t)))
                )
                 { long kk = INTDATA(ARG(1,ARG(0,t)));
			       if(!ZERO(ARG(2,t))&& !ONE(ARG(2,t)))
			          { o[i] = sumtodifofsums0;++i;
					  }
				   else if(kk == 2)
                      { o[i] = sumofisquared; ++i;
                      }
                   else if(kk == 3)
                      { o[i] = sumoficubed; ++i;
                      }
                   else if(kk == 4)
                      { o[i] = sumofitothefourth; ++i;
                      }
                  }
              if(g == '^' && equals(ARG(1,ARG(0,t)),ARG(1,t)))
                 { o[i] = sumofallpowers; ++i;
                 }
              if(EXPANDFLAG || problemtype == SIGMA_NOTATION)
                 { if(status(sigmatosum) >= KNOWN)
                      { int nfree = numerical_sum(t);
                        if(nfree == 2)
                           { o[i] = evaluatesigmatorational; ++i;
                           }
                        else if(nfree == 1)
                           { o[i] = evalpuresigmatorational; ++i;
                           }
                      }
                   o[i] = sigmatosum; ++i;
                 }
              if(problemtype == INDUCTION)
                 { term hi = ARG(3,t);
                   o[i] = evaluatesigmatorational; ++i;
                   if(FUNCTOR(hi) == '+' && ONE(ARG(1,hi))
                      && equals(get_eigenvariable(),ARG(0,hi))
                     )
                      { o[i] = splitofflastterm; ++i;
                      }
                 }
              switch(g)
                 { case '-' :
                      o[i] = minusoutofsigma; ++i;
                      break;
                   case '+' :
                      o[i] = telescopingsum; ++i;
                      o[i] = sigmasum; ++i;
                      o[i] = sigmapoly; ++i;
                      break;
                   case '*' : /* fall-through */
                   case '/' :
                      o[i] = constantoutofsigma; ++i;
                      o[i] = sigmapoly; ++i;
                      break;
                 }
            }
         else
            { int k;
              series_ops(t,o+i,&k);
              i += k;
            }
         break;
      case FACTORIAL:
         if(expandflag & 0x0100)
            { o[i] = evaluatefactorial; ++i;
            }
         break;
      case BINOMIAL:
         if(expandflag & 0x0100)
            { o[i] = evaluatebinomialcoef; ++i;
            }
         break;
      case AND:
         if(problemtype == RELATED_RATES)
            { o[i] = eliminatederivative; ++i;
              if(used(DIFEQN))
                 { o[i] = substforvar; ++i;
                 }
              break;
            }
         if(problemtype == LINEAR_EQUATIONS &&
            unsolved_lineqns(t)
           )
            { if(currenttopic == _eqns_by_substitution)
                  solve_by_substitution(t,arg,i,o,nops);
              else
                  solve_linear_equations(t,arg,i,o,nops);
              return;
            }
         if(interval_as_and(t))
            { unsigned short ff = FUNCTOR(ARG(0,t));
              unsigned short gg = FUNCTOR(ARG(1,t));
              term a = ARG(0,ARG(0,t));
              term b = ARG(1,ARG(1,t));
              term mid = ARG(1,ARG(0,t));
              if(pathlength <= 3 &&
                 solved(t,get_eigenvariable()) &&
                 !contains_defined_variables(t)
                )
                 { o[i] = explicitdomain; ++i;
                 }
              if(obviously_nonnegative(mid))
                 { if(ff == '<' &&
                      obviously_positive(strongnegate(a))
                     )
                      { o[i] = gg == '<' ? sqrtinterval3 : sqrtinterval5; ++i;
                      }
                   else if(ff == LE &&
                           obviously_nonnegative(strongnegate(a))
                          )
                      { o[i] = gg == '<' ? sqrtinterval4 : sqrtinterval6; ++i;
                      }
                 }
              if(FUNCTOR(mid) == '^')
                 { if(equals(ARG(1,mid),two))
                      { if(FUNCTOR(ARG(0,t)) == '<' && FUNCTOR(ARG(1,t)) == '<')
                           { o[i] = sqrtinterval1; ++i;
                           }
                        else
                           { o[i] = sqrtinterval2; ++i;
                           }
                      }
                   else if(iseven(ARG(1,mid)))
                      { if(FUNCTOR(ARG(0,t)) == '<' && FUNCTOR(ARG(1,t)) == '<')
                           { o[i] = rootinterval1; ++i;
                           }
                        else
                           { o[i] = rootinterval2; ++i;
                           }
                      }
                 }
              if(FRACTION(mid) &&
                 econstant(a) && econstant(b) &&
                 econstant(ARG(0,mid)) &&
                 !econstant(ARG(1,mid))
                )
                 { /* reciprocal interval operators */
                   int signa, signb;
                   if(obviously_positive(a))
                      signa = 1;
                   else if(obviously_negative(a))
                      signa = -1;
                   else signa = 0;
                   if(obviously_positive(b))
                      signb = 1;
                   else if(obviously_negative(b))
                      signb = -1;
                   else signb = 0;
                   if(signa == 1 && signb == 1)
                      {  o[i] = ff == '<' ?
                                      (gg == '<' ? recipinterval11 : recipinterval21):
                                      (gg == '<' ? recipinterval12 : recipinterval22);
                         ++i;
                      }
                   if(signa == -1 && signb == -1)
                      {  o[i] = ff == '<' ?
                                      (gg == '<' ? recipinterval31 : recipinterval41):
                                      (gg == '<' ? recipinterval31 : recipinterval42);
                         ++i;
                      }
                   if(signa == -1 && signb == 1)
                      {  o[i] = ff == '<' ?
                                      (gg == '<' ? recipinterval51 : recipinterval61):
                                      (gg == '<' ? recipinterval52 : recipinterval62);
                         ++i;
                      }
                 }
              if(SERIES_TOPIC(currenttopic))
                 { o[i] = intervaltoabs1; ++i;
                   o[i] = intervaltoabs2; ++i;
                 }
            }
         break;
      case OR:
         if(problemtype == RELATED_RATES && pathlength <= 1)
            { *nops = 0;
              return;
            }
         o[i] = combineintervals; ++i;
         if(get_checksolutionsflag())
            { o[i] = checkroot; ++i;
              if(contains(t,PI_ATOM))
                { o[i] = periodicform; ++i;
                }
            }

         if(problemtype == MINMAX)
            { /* addcriticalpoints, addendpoints, addundefinedpoints
                 are in preops */
              o[i] = rejectpoint; ++i;
              o[i] = tabulate; ++i;
            }
         if(currenttopic == _complex_cubics &&
            contains_existentials(t) &&
            ARITY(t) == 2 && solved_cubic(ARG(0,t)) &&
            solved_cubic(ARG(1,t))
           )
            { o[i] = translatevar; ++i;
            }
         if(get_complex() && pathlength == 1 &&
            get_whichpass() > 0 &&
            contains_existentials(t) &&
            !contains_defined_variables(t)
           )
            { o[i] = explicitparams; ++i;
            }
         break;
      case SG:
         if(ZERO(ARG(0,t)))
             { o[i] = sgzero; ++i;
               *nops = i;
               return;
             }
         o[i] = sgpos; ++i;
         o[i] = sgneg; ++i;
         if(NEGATIVE(ARG(0,t)))
            { o[i] = sgodd; ++i;
            }
         break;
      case ABS:
         o[i] = abspos; ++i;
         o[i] = absneg; ++i;
                     /* don't use absdef in auto mode */
         if(intflag && !get_binders() && sg_ok() && !contains(t,SG))
            /* !get_binders so we don't introduce SG in a definite integral */
            { o[i] = abssg; ++i;  /* abs(x) = x sg(x) */
            }
         g = FUNCTOR(ARG(0,t));
         switch(g)
            { case '^' :
                 o[i] = abspower; ++i;
                 break;
              case '*' :
                 o[i] = abslinear; ++i;
                 break;
              case '/' :
                 o[i] = abslinearquo; ++i;
                 if(!contains_defined_variables(ARG(0,t)))
                    /* don't break up abs(a/b) until after
                       defined variables have been unwound,
                       e.g. in the answer to an integral,
                       because a/b may permit cancellations
                       after the variables are unwound
                     */
                    { o[i] = absoffraction; ++i;
                    }
                 break;
              case ROOT:
                 o[i] = absroot; ++i;
                 break;
              case SQRT:
                 o[i] = abssqrt; ++i;
                 break;
            }
         break;
      case ATAN:
         o[i] = evalarctan; ++i;
         break;
      case ACOS:
         o[i] = evalarccos; ++i;
         break;
      case ASIN:
         o[i] = evalarcsin; ++i;
         break;
      case ASEC:
         if(seminumerical(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '/')
            { o[i] = evalarcsec; ++i;
            }
         break;
      case ACSC:
         if(seminumerical(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '/')
            { o[i] = evalarccsc; ++i;
            }
         break;
      case ACOT:
         if(seminumerical(ARG(0,t)) || FUNCTOR(ARG(0,t)) == '/')
            { o[i] = evalarccot; ++i;
            }
         break;
      case '=':
         if(problemtype==RELATED_RATES && !contains(t,DIFF) && !used(DIFEQN))
           /* don't differentiate more than once */
            { o[i] = difeqn; ++i;
            }
         if(problemtype == TRIG_IDENTITY && !algebraic_identity(t))
            { unsigned short localtrigflag=0,bf;
              o[i] = checknumerically; ++i;
              if(whichpass >= 4 && (SIGNEDFRACTION(ARG(0,t)) || SIGNEDFRACTION(ARG(1,t))))
                 { o[i] = crossmultiply; ++i;
                 }
              if(FUNCTOR(ARG(0,t)) == '^' && iseven(ARG(1,ARG(0,t))) &&
                 TRIGFUNCTOR(FUNCTOR(ARG(0,ARG(0,t))))
                )
                 { set_trigflag(ARG(1,t),&localtrigflag);
                   bf = FUNCTOR(ARG(0,ARG(0,t))); /* base functor */
                   if(localtrigflag == (1 << TRIGINDEX(SIN)) && bf == COS)
                      { o[i] = cossqtosinsq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(COS)) && bf == SIN)
                      { o[i] = sinsqtocossq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(TAN)) && bf == SEC)
                      { o[i] = secsqtotansq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(SEC)) && bf == TAN)
                      { o[i] = tansqtosecsq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(COT)) && bf == CSC)
                      { o[i] = cscsqtocotsq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(CSC)) && bf == COT)
                      { o[i] = cotsqtocscsq; ++i;
                      }
                 }
              if(FUNCTOR(ARG(1,t)) == '^' && iseven(ARG(1,ARG(1,t))) &&
                 TRIGFUNCTOR(FUNCTOR(ARG(0,ARG(1,t))))
                )
                 { unsigned short localtrigflag = 0,bf;
                   set_trigflag(ARG(0,t),&localtrigflag);
                   bf = FUNCTOR(ARG(0,ARG(1,t))); /* base functor */
                   if(localtrigflag == (1 << TRIGINDEX(SIN)) && bf == COS &&
                      !trigpolyflag == SIN
                     )
                      { o[i] = cossqtosinsq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(COS)) && bf == SIN &&
                      !trigpolyflag == COS
                     )
                      { o[i] = sinsqtocossq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(TAN)) && bf == SEC &&
                      !trigpolyflag == TAN
                     )
                      { o[i] = secsqtotansq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(SEC)) && bf == TAN &&
                      !trigpolyflag == SEC
                     )
                      { o[i] = tansqtosecsq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(COT)) && bf == CSC &&
                      !trigpolyflag == COT
                     )
                      { o[i] = cscsqtocotsq; ++i;
                      }
                   if(localtrigflag == (1 << TRIGINDEX(CSC)) && bf == COT &&
                      !trigpolyflag == CSC
                     )
                      { o[i] = cotsqtocscsq; ++i;
                      }
                 }
              g = contains_sqrt(t);
              if(g == ABS || g == SQRT)
                 { o[i] = squareeqn; ++i;
                 }
              else if (g == ROOT)
                 { o[i] = powereqn; ++i;
                 }
              else if(
                      (FUNCTOR(ARG(1,t)) == '^' && RATIONALP(ARG(1,ARG(1,t))) && equals(ARG(1,ARG(1,ARG(1,t))),two)) ||
                      (FUNCTOR(ARG(0,t)) == '^' && RATIONALP(ARG(1,ARG(0,t))) && equals(ARG(1,ARG(1,ARG(0,t))),two))
                     )
                 { o[i] = squareeqn; ++i;
                   /* squareeqn will only work if the other side can
                      be checked nonnegative. */
                 }
              else if(
                      (FUNCTOR(ARG(1,t)) == '^' && RATIONALP(ARG(1,ARG(1,t)))) ||
                      (FUNCTOR(ARG(0,t)) == '^' && RATIONALP(ARG(1,ARG(0,t))))
                     )
                  { o[i] = powereqn; ++i;
                  }
            }
         if(problemtype == LINEAR_EQUATIONS && pathlength < 2 &&
            !contains(t,MATRIX) && !contains(t,VECTOR)
           )
            /* a system of linear equations has degenerated to a
               single equation, e.g. because one reduced to an identity */
            { o[i] = regardvarasconst; ++i;
              if(FUNCTOR(ARG(0,t)) == '+')
                 { o[i] = transfereqn; ++i;
                 }
              if(FUNCTOR(ARG(0,t)) == '*')
                 { o[i] = diveqn; ++i;
                 }
            }
         if(currenttopic == _higher_order_diff
            && !contains(ARG(1,t),DIFF)
            && (
                ( FUNCTOR(ARG(0,t)) == DIFF && ARITY(ARG(0,t)) == 2)
                || (FUNCTOR(ARG(0,t)) == PR && ONE(ARG(1,ARG(0,t))))
                   /* so far we've taken only one derivative */
               )
            )
             { o[i] = difeqn; ++i;
             }
         if(SOLVETYPE(problemtype) ||  /* which includes MINMAX */
            (problemtype == LINEAR_EQUATIONS && pathlength<=2 && !contains(t,MATRIX) && !contains(t,VECTOR))
           )
            { if(problemtype == IMPLICIT_DIFF)
                 { o[i] = transfereqn; ++i;
                 }
                 /* for other solvetypes, transfereqn is pre-associated;
                    it's not needed for RELATED_RATES as ssolve is used.
                 */
              if(problemtype != RELATED_RATES)
                 { if(status(ssolveop) >= KNOWN)
                      { o[i] = ssolveop; ++i;
                      }
                   else
                      { solve_equation(t,i,o,nops);
                        if(problemtype != MINMAX && currenttopic != _related_rates)
                           return;
                        else
                           i = *nops;
                      }
                 }
            }
         if(problemtype==MINMAX)
            { o[i] = addcriticalpoints; ++i;
              o[i] = addendpoints; ++i;  /* could need this if there was
                                            just one critical point */
              o[i] = addundefinedpoints; ++i;
              o[i] = rejectpoint; ++i;
              o[i] = tabulate; ++i;  /* could need this if one endpoint
                                             and no critical points */
              *nops = i;
              return;
            }
         if(problemtype==INDUCTION)
            { /* selectinductionvariable and useinductionhyp are in pre_ops */
              o[i] = basiscase; ++i;
              o[i] = inductionstep; ++i;
              o[i] = thereforeasdesired; ++i;
              *nops = i;
              return;
            }
         if(currenttopic == _related_rates)
            { // o[i] =  usenumbers; ++i;
              //  o[i] =  usederivs; ++i;
              /* these operators change the problemtype to IMPLICIT_DIFF,
                 but the topic remains _related_rates */
              if(whichpass > 0)
                 { o[i] = ssolveop; ++i;
                 }
                 /* solve for the derivative anyway, even if you
                    can't use or don't have specific information */
            }
         if(FUNCTOR(ARG(0,t)) == VECTOR && FUNCTOR(ARG(1,t)) == VECTOR)
            { o[i] = impossibleeqns; ++i;
              o[i] = dropzerorow; ++i;
              o[i] = convertmatrixeqn; ++i;
              *nops = i;
              return;
            }
         if(FUNCTOR(ARG(0,t))== '*' &&
            ARITY(ARG(0,t)) == 2 &&
            FUNCTOR(ARG(0,ARG(0,t))) == MATRIX
            /* The right side can be a vector, a scalar, or a matrix
               product.  We don't check its form here.  */
           )
            { solve_matrix_equation(t,arg,i,o,nops);
              return;
            }
         if(FUNCTOR(ARG(0,t)) == VECTOR &&
            FUNCTOR(ARG(1,t)) == '*' &&
            FUNCTOR(ARG(0,ARG(1,t))) == MATRIX &&
            FUNCTOR(ARG(1,ARG(1,t))) == VECTOR
           )
            { o[i] = multiplymatrices; ++i;
              /* This arises in _gauss_jordan when in term selection mode
                 the user has multiplied out on the left to get { x,y }
                 and on the right we still have a matrix product.  In
                 auto mode the two matrix multiplications are done at once.
              */
            }

         if(FUNCTOR(ARG(0,t))==INTEGRAL)
            { o[i] = transferintegral; ++i;
              break;
            }
         if(contains(ARG(0,t),INTEGRAL) && FUNCTOR(ARG(0,t)) == '*')
            { o[i] = muleqn; ++i;  /* needed if coef. is a fraction */
              o[i] = diveqn; ++i;
              break;
            }
         if(get_pending() != NULL)
            { o[i] = showcallingproblem; ++i;
              break;
            }
         break;
      case '>' :  /* fall through */
      case GE  :  /* fall through */
      case '<' :  /* fall through */
      case NE  :
      case LE  :
         if(problemtype==INDUCTION)
            { /* selectinductionvariable and useinductionhyp are in pre_ops */
              o[i] = basiscase; ++i;
              o[i] = inductionstep; ++i;
              o[i] = thereforeasdesired; ++i;
              *nops = i;
            }
         else
            solve_ineq(t,i,o,nops);
         if(problemtype != TESTCONVERGENCE)
            return;
         else
            i = *nops;
         break;  /* and continue to throw in finishcomparisontest1 etc. */
      case MULTIPLICITY:
         o[i] = collectmultiplesolns; ++i;
         o[i] = rejecteqn; ++i;
         break;
      case PR:
         if(PREDEFINED_FUNCTOR(FUNCTOR(ARG(0,t))))
            { o[i] = primerule; ++i;
            }
            /* but leave f'(x), f''(x) etc. alone when f is user-defined */
         break;
      case DIFF:
         if(status(derivop) > LEARNING &&
            problemtype != DIFFERENTIATE_FROM_DEFN &&
            problemtype != DIFFERENTIATE
           )
            { o[i] = derivop; ++i;
              *nops = i;
              return;
            }
         autodif(t,i,o,nops);
         return;
      case INTEGRAL:
         if(CANTFACTOR(t))  /* this integral has already been tried */
            { *nops =i;
              return;
            }
         if(pathlength == 5 && path[0] == '-' && path[2] == '-' &&
            equals(t,history(0))
           )
            { *nops = i;
              return;  // give up
            }
         autoint(t,arg,i,o,nops);
         return;
      case LIMIT:
         autolimit(t,i,o,nops);
         return;
    } /* close big switch */
  if(pathlength == 1 &&      /* toplevel */
     contains_fractional_exponents(t) &&
     (contains(t,SQRT) || contains(t,ROOT))
    )
     { /* Don't leave expressions with both fractional exponents and roots. */
       term fractexp[3];
       int nfracts;
       if(radicalflag != -1)
          { nfracts = fractexps(t,fractexp);
            if(nfracts == 1 && ONEHALF(fractexp[0]))
               { o[i] = backtosqrts; ++i;
               }
            else if(nfracts < 3)
               { o[i] = backtoroots; ++i;
                 /* if only one or two distinct root indices are involved */
               }
          }
       if(contains(t,ROOT) || !oksqrt(t))
          /* if backtosqrts and backtoroots fail, then change all roots
          to fractional exponents. */
          {   o[i] = eliminatesqrts; ++i;
          }
     }
  else if(pathlength == 1 &&
          (contains(t,SQRT) || contains(t,ROOT)) &&
          radicalflag == -1
         )
     { /* t has no fractional exponents, but does have roots.
          In some cases we still want to go to fractional exponents
       */
       o[i] = eliminatesqrts; ++i;
     }
  if(currenttopic == _polar_form && pathlength == 1 &&
     seminumerical(t)  /* example, 1-sqrt(3)  */
    )
     { o[i] = minustopolar; ++i;
     }
  finish:
  if(problemtype == TESTCONVERGENCE && pathlength <= 1)
     { if(!contains_calc(t) && !contains(t,SUM) && get_whichpass() > 0)
          { int k;
            series_toplevel(t,o+i,&k);
            i+= k;
          }
     }
  *nops = i;  /* functor isn't trapped above */
  return;
}  /* close function */
/*__________________________________________________________________*/
static void autoexp(term t,actualop *o, int *nops)
/* do the work of post_ops when t is a power */
{ unsigned short g;
  int i=0;
  const int intflag = get_intflag();
  const int indexflag = get_indexsumflag();
  const int seriesflag = get_seriesflag();
  int expandflag = get_expandflag();
  int radicalflag = get_radicalflag();
  int trigexpandflag = get_trigexpandflag();
  int pathlength = get_pathlength();
  int trigpolyflag = get_trigpolyflag();
  unsigned short *path = get_path();
  term u = ARG(0,t);
  term v = ARG(1,t);
  term cancelled, trash;
  int problemtype = get_problemtype();
  int currenttopic = get_currenttopic();
  int whichpass = get_whichpass();
  if(INTEGERP(u) && RATIONALP(v) &&
     !nsquarefree(u) &&
     !(FRACTION(v) && ONE(ARG(0,v)) && equals(ARG(1,v),two))
     /* 16^(1/2) = 2^2 looks too mysterious */
    )
    /* examples:  36^(1/5)  becomes 6^2/5;   4^(1/4)  becomes 2^(1/2)  */
     { o[i] = powertopower; ++i;
     }
  if(ATOMIC(u))
     { if(equals(u,infinity))
          { o[i] = powerofinfinity; ++i;
            *nops = i;
            return;
          }
       if(INTEGERP(u) && NEGATIVE(v) && INTEGERP(ARG(1,v)))
          { if(ONE(ARG(1,v)))
               { o[i] = eliminatenegexp1; ++i;
               }
            else
               { o[i] = eliminatenegexp; ++i;
               }
            *nops = i;
            return;
          }
       if(get_complex() && equals(u,complexi))

          /* without 'complex' here this will apply to an index
             variable in a sum.  The variable i really means complexi
             only when the 'complex' variable is nonzero */

          { /* defnofi, powersofi0 etc are in preops */
             if(iscomplex(v))
                { o[i] = introducelninexponent; ++i;
                  /* We need this to do i^i, but the clause iscomplex(v)
                     prevents it being used on i^(3/2), which should go
                     to (e^(pi/2))^(3/2) and hence to e^(3pi i/4)
                  */
                }
             else
                { o[i] = rectangulartopolar; ++i;
                }
            *nops = i;
            return;
          }
       else if(equals(u,eulere))
          { if(FUNCTOR(v) == LN)
               { o[i] = lninexponent; ++i;
               }
            if(FUNCTOR(v) == '*')
               { term c,s;
                 ratpart2(v,&c,&s);
                 if(FUNCTOR(s) == LN ||  /* example, e^(2 ln x) */
                    (FUNCTOR(c) == LN && !contains(s,LN))  /* example, e^(1/x) ln 2 */
                   )
                    { o[i] = lninexponent; ++i;
                    }
               }
            if(NEGATIVE(v) &&
               !difflag &&
               !intflag &&
               !limitflag &&
               !indexflag &&
               !seriesflag &&
               (FUNCTOR(ARG(0,v)) == LN ||
                (FUNCTOR(ARG(0,v)) == '*' && contains_at_toplevel(ARG(0,v),LN))
               )
              )
               { o[i] = eliminatenegexp; ++i;
               }
            if(
               iscomplex(v) &&
               (FUNCTOR(v) == '+' ||
                complex_number(v) ||
                (FUNCTOR(v) == '*' && ARITY(v) == 2 && equals(ARG(1,v),complexi) && FUNCTOR(ARG(0,v)) == '+')
               ) &&
               /* must work on e^(x+iy) but NOT on e^iz where z is a complex variable */
               !locate(path,path+pathlength-1,'^') && /* not inside another power */
               !locate(path,path+pathlength-1, LN) && /* not inside a logarithm,
                              cf ln(xi) = ln(xe^(i pi/2)) which should NOT
                              go to ln(x(cos(pi/2) + i sin(pi/2))) */
               !locate(path,path+pathlength-1,SQRT) &&
               !locate(path,path+pathlength-1,ROOT) &&
               get_currenttopic() != _polar_form &&
               !(indenomflag && NEGATIVE(v))&& /* wait for introducenegexp */
               (pathlength < 3 || path[pathlength-3] != '*' || ok_complexexp(path,pathlength))
              )
               { o[i] = complexexponential; ++i;
                 o[i] = complexexponential2; ++i;
               }
            if(FUNCTOR(v) == '+' && contains(v,LN))
             /* e^(a + ln b) => e^a e^ln b   */
             /* reversecollectpowers will inhibit(collectpowers) if
                one of the powers created contains_monomially a LN term;
                it will be released by lninexponent */
               { o[i] = reversecollectpowers2; ++i;
                 o[i] = reversecollectpowers; ++i;
               }
          }
       else if(equals(u,ten) && FUNCTOR(v) == LOG)
          { o[i] = loginexponent; ++i;
          }
       else if(equals(u,ten) && FUNCTOR(v) == '+' && contains(v,LOG))
          /* 10^(a + log b) => 10^a 10^log b  */
          { o[i] = reversecollectpowers; ++i;
          }
       else if(equals(u,eulere) && FUNCTOR(v) == '+' && contains(v,LN))
          { o[i] = reversecollectpowers; ++i;
          }
       else if(FUNCTOR(v) == '+' && contains(v,LOGB) && ATOMIC(u))
          { o[i] = reversecollectpowers; ++i;
          }
       else if(FUNCTOR(v) == LOGB && equals(ARG(0,v),u))
          { o[i] = logbinexponent; ++i;
          }
       else if(NEGATIVE(v) && FUNCTOR(ARG(0,v)) == LOGB &&
               ( equals(ARG(0,ARG(0,v)),u) || !cancel(u,ARG(0,ARG(0,v)),&cancelled,&trash))
                 /* 9^(-log(3,x))  for example of the need to cancel */
              )
          { o[i] = eliminatenegexp; ++i;
          }
       else if(NEGATIVE(v) && (intflag || seriesflag || indexflag) && indenomflag)
          { if(ONE(ARG(0,v)))
               { o[i] = eliminatenegexp1; ++i;
               }
            else
               { o[i] = eliminatenegexp; ++i;
               }
            /* example:  integral(1/(u(1+2u -u^-1)),u);
              we don't use the distributive law in the denom, but
              if we eliminate the negative exponent, common denom
              and cancellation will follow. */
          }
       else if(INTEGERP(u) && !limitflag && !difflag
               && (problemtype < LIMITS || NUMBER(v)) &&
               FUNCTOR(v) != '+'  /* Don't use it on 10^(log a - log b) */
              )
                /* don't factor 10 when differentiating 10^x */
          {  /* on known roots like 8^(1/3), don't use factorbase */
            if(FRACTION(v) && ONE(ARG(0,v)) &&
               ISINTEGER(ARG(1,v)) && INTDATA(ARG(1,v)) < 4 &&
               ISINTEGER(u)
              )
               { if(INTDATA(ARG(1,v)) == 2)
                    { long k = INTDATA(u);
                      if(k==4 || k == 9 || k ==16 || k == 25 || k == 36
                         || k ==49 || k==64 || k==81 || k == 100 || k == 121
                         || k == 144 || k == 169
                        )
                         { o[i] = exponenttosqrt; ++i;
                           *nops = i;
                           return;
                         }
                    }
                 else if(INTDATA(ARG(1,v)) == 3)
                    { long k = INTDATA(u);
                      if(k==8 || k == 27 || k == 64 || k == 125)
                         { o[i] = exponenttoroot; ++i;
                           *nops = i;
                           return;
                         }
                    }
               }
            o[i] = factorbase; ++i;
          }
     }
  if(pathlength >= 3 && path[pathlength-3] == DIFF &&
     currenttopic == _diff_polynomial && ISINTEGER(v) &&
     FUNCTOR(u) == '+' && ARITY(u) == 2
    )
     { if(equals(v,two))
          { o[i] = NEGATIVE(ARG(1,u)) ? squareofdif : squareofsum; ++i;
          }
       else if(equals(v,three))
          { o[i] = NEGATIVE(ARG(1,u)) ? cubeofdif : cubeofsum; ++i;
          }
     }
  if(NOTDEFINED(v))
     { o[i] = toinfinity1; ++i;
       o[i] = toinfinity0; ++i;
       o[i] = tominusinfinity1; ++i;
       o[i] = tominusinfinity0; ++i;
       *nops = i;
       return;
     }
  if(!ATOMIC(u))
     { g = FUNCTOR(u);
       switch(g)
          { case '+':
               if(intflag)
                  { if(FRACTION(v) && equals(ARG(1,v),two))
                       { term x = get_eigenvariable();
                         if(ispolyin(u,x) && contains(u,'^'))
                             { o[i] = completethesquare1; ++i;
                             }
                       }

                    break;  /* don't expand the integrand;
                             expandintegrand will do it later.  If we
                             do it now, we screw up some integrals that
                             should be done by substitution, such as
                             (1+�x)^9/�x for example */
                  }
               if(pathlength >= 3 && path[pathlength-3] == SUM)
                  { if(equals(v,two))
                       { if(ARITY(u) == 2 &&
                            (
                              (NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
                              (NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
                            )
                           )
                            { o[i] = squareofdif; ++i;
                            }
                         else
                            { o[i] = squareofsum; ++i;
                            }
                       }
                    else if(equals(v,three) && ARITY(u) == 2 && NEGATIVE(ARG(1,u)))
                       { o[i] = cubeofdif; ++i;
                       }
                    else if(equals(v,three))
                       { o[i] = cubeofsum; ++i;
                       }
                    else if(ISINTEGER(v) && INTDATA(v) <= 4)
                       { o[i] = binomialtheorem; ++i;
                       }
                  }
               if( (EXPANDFLAG && !in_sum() && 
                    !(pathlength==1 && problemtype == SIMPLIFY && get_currentline() > 0)
                   ) ||
                   currenttopic == _binomial_theorem ||
                   (get_complex() && ispolyin(u,complexi))
                 )
                  { if(equals(v,two))
                       { if(ARITY(u) == 2 &&
                            (
                              (NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
                              (NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
                            )
                           )
                            { o[i] = squareofdif; ++i;
                            }
                         else
                            { o[i] = squareofsum; ++i;
                            }
                       }
                    else if(equals(v,three) && ARITY(u) == 2 && NEGATIVE(ARG(1,u)))
                       { o[i] = cubeofdif; ++i;
                       }
                    else if(equals(v,three))
                       { o[i] = cubeofsum; ++i;
                       }
                    else
                       { o[i] = binomialtheorem; ++i;
                       }
                  }
               else if(!in_sum())
                  { if(equals(v,two) && contains_sqrt(u) == SQRT)
                       { if(ARITY(u) == 2 &&
                            (
                              (NEGATIVE(ARG(0,u)) && !NEGATIVE(ARG(1,u))) ||
                              (NEGATIVE(ARG(1,u)) && !NEGATIVE(ARG(0,u)))
                            )
                           )
                            { o[i] = squareofdif; ++i;
                            }
                         else
                            { o[i] = squareofsum; ++i;
                            }
                       }

                    if(equals(v,three) && contains_sqrt(u) == ROOT)
                       { if(ARITY(u) == 2 && NEGATIVE(ARG(1,u)))
                            { o[i] = cubeofdif; ++i;
                            }
                         else
                           { o[i] = cubeofsum; ++i;
                           }
                       }
                  }
               break;
            case '-':
               o[i] = minustopower; ++i;
               break;
            case '/':
               if(!difflag  && /* better to use power rule first when differentiating */
                  !contains(v, LIMIT) &&
                  !contains(v, SUM) &&
                  !contains(v, DIFF) &&
                  !contains(v, INTEGRAL) &&
                  (!get_polyvaldomainflag() ||
                   (!contains_undefined(v) && !check(defined(v)))
                   /* don't do this:  (a/b)^infinity = a^infinity/b^infinity.
                      You don't have to check this if polyvaldomainflag is 0,
                      and even if it is, first make a fast check for
                      a NOTDEFINED atom in the exponent, before calling
                      on the prover to check the definedness of the exponent
                   */
                  )
                 )
                  { if(!limitflag || whichpass > 0 || limfractflag == -1)
                       /* Don't use (u/v)^n = u^n/v^n in a limitand unless
                          it is in a compound fraction (limfractflag == -1)
                          or you failed to solve it completely without
                          this law (whichpass > 0).  For example,
                          to simplify a difference of two powers of
                          fractions, where each term individually goes
                          to infinity, such as
                          lim(x->0, ((x+1)/x^2)^2 - ((x-1)/x^2)^2)) =
                          lim(x->0, ((x+1)^2-(x-1)^2)/x^4) =
                          lim(x->0, 4x/x^4) = infinity
                       */
                       { o[i] = quotienttopower; ++i;
                       }
                  }
               break;
            case '*':
               if(radicalflag == 1 && FRACTION(v))
                  break;   /*  (5�7)^(1/3), don't make it 5^(1/3)7^(1/3)
                            if you're going to convert to radicals anyway */
               /* don't convert (ab)^-n to a^-n b^-n unless the directions are
                  to express in negative exponents, or it's in a sum in a
                  fraction. */
               if(!NEGATIVE(v) ||
                  get_polyvalnegexpflag() == 1 ||
                  !get_infractionflag() ||
                  insumfract()
                 )
                  { o[i] = producttopower; ++i;
                  }
               break;
            case SQRT:
               if(problemtype < LIMITS)
                  /* In calculus, use fractional exponents instead */
                  { o[i] = powerofsqrt2; ++i;  /* (sqrt(x))^(2n+1) = x^n sqrt x */
                                               /* powerofsqrt is in preops */
                  }
               break;
            case ROOT:
               if((radicalflag < 0 && !algebraic_number(ARG(1,u))) ||
                  iscomplex(ARG(1,u)) ||
                  ( get_complex()  &&
                    NEGATIVE(ARG(1,u)) && obviously_nonnegative(ARG(0,ARG(1,u)))
                    /* whether odd or even, when computing complex roots of
                       negative numbers use fractional exponents. */
                  )
                 )
                  { o[i] = rootpowerexp; ++i;
                  }
               if(!SOLVETYPE(problemtype))
                  { o[i] = powerofroot2; ++i;  /*  (��a)^m = ��(a^m))  */
                    o[i] = powerofroot3; ++i;   /* (��a)^(qn+r) = a^q ��(a^r)  */
                  }
               break;
            case SG:
               o[i] = sgoddpower; ++i;
               o[i] = sgevenpower; ++i;
               break;
            case ABS:
               if(get_complex() && contains(u,'i'))
                  { o[i] = squareofabs; ++i;
                  }
               o[i] = absevenpower; ++i;
               break;
            case SIN:
               if((trigexpandflag & 1) || contains_arctrigs(u))
                  { o[i] = sinsqhalf; ++i;
                  }
               if(!ATOMIC(ARG(0,u)))
                  { o[i] = sinsqperiodic; ++i;
                  }
               if(trigpolyflag == SIN)
                  { if(iseven(v))
                       { o[i] = sinsqtocossq; ++i;
                       }
                    else
                       { o[i] = sinoddpower; ++i;
                       }
                  }
               break;
            case COS:
               if((trigexpandflag & 1) || contains_arctrigs(u))
                  { o[i] = cossqhalf; ++i;
                  }
               if(!ATOMIC(ARG(0,u)))
                  { o[i] = cossqperiodic; ++i;
                  }
               if(trigpolyflag == COS)
                  { if(iseven(v))
                       { o[i] = cossqtosinsq; ++i;
                       }
                    else
                       { o[i] = cosoddpower; ++i;
                       }
                  }
               break;
            case COT:
               if((trigexpandflag & 0x0004) &&  /* only cot and tan */
                  !difflag && !indenomflag
                 )
                  { o[i] = cottotan; ++i;
                    break;
                  }
               if(!(trigexpandflag & 0x100) && limfractflag != -1 && !difflag)
                  { o[i] = cottosincos; ++i;
                    break;
                  }
               if(trigpolyflag == COT)
                  { if(iseven(v))
                       { o[i] = cotsqtocscsq; ++i;
                       }
                    else
                       { o[i] = cotoddpower; ++i;
                       }
                  }
            case SEC:
               if(!(trigexpandflag & 0x10) && limfractflag != -1 &&
                  (pathlength < 3 || !TRIGFUNCTOR(path[pathlength-3]))
                 )
                  { o[i] = secrule; ++i;  /* may be inhibited if inside a sum
                                           containing only sec and tan */
                  }
               /* see comments below near cscrule */
               if(!ATOMIC(ARG(0,u)))
                  { o[i] = secsqperiodic; ++i;
                  }
               if(trigpolyflag == SEC)
                  { if(iseven(v))
                       { o[i] = secsqtotansq; ++i;
                       }
                    else
                       { o[i] = secoddpower; ++i;
                       }
                  }
               break;
            case TAN:
               if(trigpolyflag == TAN)
                  { if(iseven(v))
                       { o[i] = tansqtosecsq; ++i;
                       }
                    else
                       { o[i] = tanoddpower; ++i;
                       }
                  }
               break;
            case CSC:
               if(!(trigexpandflag & 0x100) &&
                  limfractflag != -1 &&
                  (pathlength < 3 || !TRIGFUNCTOR(path[pathlength-3]))
                 )
                  { o[i] = cscrule; ++i;
                    /* this is here instead of in autotrig so that we will
                        get  csc^2 => 1/sin^2  instead of (1/sin)^2.
                       'path' is used to suppress it in autotrig. */
                  }
               if(!ATOMIC(ARG(0,u)))
                  { o[i] = cscsqperiodic; ++i;
                  }
               if(trigpolyflag == CSC)
                  { if(iseven(v))
                       { o[i] = cscsqtocotsq; ++i;
                       }
                    else
                       { o[i] = cscoddpower; ++i;
                       }
                  }
               break;
          }
     }
  /* Don't leave a negative exponent under a root or sqrt,
     as in root(2,y^-2) or sqrt(xy^-2).  The following
     code takes care of this:
  */
  if(NEGATIVE(v) && !is_complex(v) && 
     (
      (pathlength >= 3 &&
       (path[pathlength-3] == ROOT || path[pathlength-3] == SQRT)
      ) ||
      (pathlength >= 5 &&
       path[pathlength-3] == '*' &&
       (path[pathlength-5] == ROOT || path[pathlength-5] == SQRT)
      )
     )
    )
     { if(ONE(ARG(0,v)))
          { o[i] = eliminatenegexp1; ++i;
          }
       else
          { o[i] = eliminatenegexp; ++i;
          }
     }
  if(radicalflag == 1 && FRACTION(v) && elimfractexp_conditions(pathlength,path))
     { if(FUNCTOR(ARG(0,v)) == '*')
          { /* (ab^n)^(1/2) = a^(1/2)b^(n/2)  before introducing sqrt */
            unsigned short m = ARITY(ARG(0,v));
            int j;
            for(j=0;j<m;j++)
               { if(FUNCTOR(ARG(j,ARG(0,v)))== '^')
                    break;
               }
            if(j<m)
               { o[i] = producttopower; ++i;
               }
          }
       if(equals(ARG(1,v),two))
          { o[i] = exponenttosqrt; ++i;
          }
       else
          { o[i] = exponenttoroot; ++i;
          }
     }
  if( SOLVETYPE(problemtype))
     { if( !inpowerflag &&
           contains(v,FUNCTOR(get_eigenvariable())) &&
           pathlength >=3 &&
           path[pathlength-3] != LN &&
           path[pathlength-3] != LOG &&
           path[pathlength-3] != LOGB &&
           path[pathlength-3] != '+'  &&
           !equals(u,ten) &&
           !equals(u,eulere)
         )
               /* don't rewrite ln(a^b) => ln(e^b ln a) as
                  this leads to an infinite regress
                    => ln(e^ln(a^b))
                    => ln(e^ln(e^(b ln a)))  etc.
                  And don't use this in a power to stop the
                  second step in this and similar regresses.
                  And don't use it inside a sum, as it won't do any good there
                  and can block further progress.
               */
          { o[i] = introduceloginexponent; ++i; /* x^n = 10^(n log x) */
                /* but in automode it requires log to appear in n */
            o[i] = introducelninexponent; ++i;  /* x^n = e^(n ln x) */
                /* so if log doesn't already appear in n, ln will be used */
          }
     }
  if(problemtype == DIFFERENTIATE_FROM_DEFN &&
     limitflag && !difflag  &&  /* definition of derivative has been used already */
     ISATOM(v)  /* whether x or h in 2^(x+h)-2^x  */
    )
     { /* you need introducelninexponent to handle f(x) = a^x */
       o[i] = introducelninexponent; ++i;
     }
  if(
      (intflag || limitflag) &&
      ATOMIC(u) && !equals(u,eulere) &&
      !NOTDEFINED(u) && !constant(v) && constant(u) &&
      !ATOMIC(v)
    )
       /* convert 2^f(x) to e^((ln 2) f(x)) in an integral or limit */
       /* also pi^f(x) or c^f(x) but not x^2 and not 2^x, since we
          have integration and differentiation formulas for 2^x directly.
       */
     { o[i] = introducelninexponent; ++i;
     }
  /* and don't use introducelninexponent at all unless
     we're solving equations or inequalities, or differentiating
     from the definition of derivative, or in the case of atom^v in an
     integral or limit.
     Just leave a^c alone, e.g. in a^c/b^c.
 */

  if(whichpass == 5 &&   /* don't do this till the very end */
     (
       (OBJECT(u) && TYPE(u) == DOUBLE && NUMBER(v)) ||
       (NUMBER(u) && OBJECT(v) && TYPE(v) == DOUBLE) ||
       (NUMBER(u) && NEGATIVE(v) && OBJECT(ARG(0,v)) && TYPE(ARG(0,v))==DOUBLE)
     )
    )
     { o[i] = computepower; ++i;
     }
  *nops = i;
}

/*__________________________________________________________________*/
static void autoquo(term t,actualop *o, int *nops)
/* do the work of post_ops when t is an quotient */

{ term num = ARG(0,t);
  term denom = ARG(1,t);
  term x;
  const int intflag = get_intflag();
  const int indexflag = get_indexsumflag();
  const int seriesflag = get_seriesflag();
  unsigned short g = FUNCTOR(num);
  unsigned short h = FUNCTOR(denom);
  unsigned short f;
  int apartandcancelflag = 0;
  int problemtype = get_problemtype();
  int topic = get_currenttopic();
  int comdenomflag = get_comdenomflag();
  int whichpass = get_whichpass();
  int pathlength = get_pathlength();
  unsigned short  *path = get_path();
  int i=0;
  if(whichpass == 5 && seminumerical(t) && contains_double(t))
     { o[i] = devalop; ++i;
       *nops = i;
       return;
     }
  if(g=='/' && h != '/')
     { o[i] = compoundfractions2; ++i;  /* (a/b)/c = a/(bc) */
     }
  if(h == '/')
     { if(ONE(num))
          { o[i] = invertandmultiply2; ++i;
          }
       else
          { o[i] = invertandmultiply; ++i;
          }
     }
  if( (get_ringflag() & RATRING) && pulloutrational_aux(num,denom))
     { o[i] = pulloutrational; ++i;
          /* careful that this doesn't loop with multiplyfractions */
          /* Therefore we don't do it just for an integer denom;
             in automode pulloutrational is associated mainly to sums,
             but is called here for special situations involving roots */

       *nops = i;
       return;
     }
  if(equals(denom,two) && FUNCTOR(num) == '+' && ARITY(num) == 2 &&
     FUNCTOR(ARG(0,num)) == '^' && equals(ARG(0,ARG(0,num)),eulere) &&
     get_currenttopic() != _hyperfunctions &&
     /* if it's _hyperfunctions we're supposed to express things in
        terms of exponentials. */
     !iscomplex(num)   /* don't use this on complex arguments */
    )
     { o[i] = coshdefrev; ++i;
       o[i] = sinhdefrev; ++i;
     }
  if(topic == _polar_form && iscomplex(num) && !iscomplex(denom) && !inpowerflag)
     /* example, e^(it)/sqrt 2 = (1/sqrt 2) e^it; but don't do it in the
        exponent, or you get e^((1/2) pi i) instead of e^(pi i/2) */
     { o[i] = pulloutreal; ++i;
     }
  if(NOTDEFINED(denom))
     { o[i] = nonzerooverinfinity; ++i;
       *nops = i;
       return;
     }
  if(NOTDEFINED(num))
     { o[i] = infinityovernonzero; ++i;
       *nops = i;
       return;
     }
  if(iscomplex(denom) &&
     h != '^'            /* don't use it if denom is e^ix */
    )
     { if(FUNCTOR(denom) == '*')
          { term c,s;
            o[i] = recipofi3; ++i;   /* a/bi = -ai/b */
            ratpart2(denom,&c,&s);
            /* don't use it if denom is c e^ix either */
            if(FUNCTOR(s) != '^')
               { o[i] = cleardenomofi; ++i;
               }
          }
       else
          { o[i] = cleardenomofi; ++i;
          }
     }
  if(intflag && FUNCTOR(ARG(1,t)) == '^' && constant(ARG(0,ARG(1,t))))
     /* example, integral(sin(x)/e^x,x) should become
        integral(e^-x sin x,x)  */
     { o[i] = introducenegexp; ++i;
     }
      /* eliminatenegexpdenom is in pre_ops */
  if(get_polyvalnegexpflag() == 1  &&
     !INTEGERP(ARG(1,t)) &&  /* don't make 1/2 into  2^(-1) */
     (pathlength < 3 || path[pathlength-3] != '/')  && /* simplify compound fractions first */
     (pathlength < 3 || path[pathlength-3] != '^') && /* in (a/b)^k, wait for a^k/b^k to happen
                                                         rather than creating (ab^-1)^k */
     (pathlength < 5 || path[pathlength-3] != '-' || path[pathlength-5] != '^')
                                                      /* similarly with (-a/b)^k */

    )  /* else eliminateconstnegexpnum is called in preops */
     { o[i] = introducenegexp; ++i;
       o[i] = introducenegexp1; ++i;
     }
  else if(get_polyvalnegexpflag() == 2 &&
          iscomplex(denom)
         )
     { o[i] = introducenegexp; ++i;
       o[i] = introducenegexp1; ++i;
     }
  else if(intflag   /*  handle integral(1/x^(1/2),x) => integral(x^(-1/2),x) */
          && !get_infractionflag()   /* don't do this in compound fractions */
          && FUNCTOR(denom) == '^'
          && FRACTION(ARG(1,denom))
          && ISATOM(ARG(0,denom))
          && constant(num)
          && !inlogflag
         )
     { o[i] = introducenegexp; ++i;
     }
  else if(difflag &&    /* d/dx( u/(x-1)^2 = d/dx u (x-1)^(-2) */
          !inlogflag &&  /* don't introduce neg exps inside a log in a deriv */
          ! (constant(num) && FUNCTOR(denom) == '^' &&
             equals(ARG(0,denom),get_eigenvariable())
            ) && /* e.g. on d/dx ( c/x^2);  inversepower will be used
                    instead of introducenegexp */
          !use_logdif(t,get_eigenvariable())
          /* don't screw up logarithmic differentiation problems */
         )
     { if(FUNCTOR(num) == '+')
          { o[i] = apartandcancel; ++i;
            apartandcancelflag = 1;  /* don't try it again below */
          }
       o[i] = introducenegexp; ++i;
       o[i] = sqrtexpdenom; ++i;
       o[i] = rootexpdenom; ++i;
     }
        /* next line deals with e.g.  (a/b + c)/d => (a/b)/d + c/d; */
  if(g=='+' &&
     (pathlength < 3 || path[pathlength-3] != '^') &&
     /* don't use apart just inside a power, wait for the power to be
        pushed into the fraction.  In particular when there are fractional
        exponents in a fraction inside a power, comdenom is used, and we
        don't want to loop e.g. on (x^1/2 + 1/x^1/2)^2. */
     (pathlength < 3 || (((f = path[pathlength-3]) != 0) && !TRIGFUNCTOR(f))) &&
     /* don't use apart just inside a trig or arctrig functor, to avoid changing
        sin((u+v)/2) to sin(u/2 + v/2) which will be attacked by sinsum instead
        of half-angle formulas. */
     !contains_sqrt(ARG(1,t)) && /* rationalizedenom should be used first */
     !(numerical(t) && contains(ARG(1,t),SQRT)) /* don't thrash on algebraic expressions */
    )
     { int k;
       term sum,arg;
       unsigned short mm;
       sum = ARG(0,t);
       mm = ARITY(sum);
       for(k=0; k<mm; k++)
          { arg = ARG(k,sum);
            if(FUNCTOR(arg)=='-')
               arg = ARG(0,arg);
            if(fractional_factor(arg))
               break;
          }
       if(k < mm)
          { o[i] = apart; ++i;
          }
     }
  if(
     (intflag || indexflag || seriesflag) &&
     !get_infractionflag() && !comdenomflag &&
     !insqrtflag && !inrootflag  && !infractexpflag &&
      /* don't attack integral(sqrt((1+x)/(1-x)),x) by polynomial division */
     !stop_apart(t)
    )
     { x = get_eigenvariable();
       if(rational_function(t,x))
          { if(FRACTION(t) && FUNCTOR(ARG(0,t)) == '+' &&
               ispolyin(ARG(0,t),x) &&        /* num is a polynomial in x */
               (
                equals(ARG(1,t),x) ||         /* demon is x or  */
                (FUNCTOR(ARG(1,t)) == '^' &&  /* a power of x */
                 equals(ARG(0,ARG(1,t)),x) &&
                 INTEGERP(ARG(1,ARG(1,t)))
                )
               )
              )
               { o[i] = apart; ++i;
               }
            else
               { o[i] = polydivop; ++i;
                 o[i] = factordenominator; ++i;
                 o[i] = partialfractionsop; ++i;
                 o[i] = completethesquare1; ++i;
                 o[i] = apart; ++i;
               }
          }
       if(pathlength >= 3 && (path[pathlength-3]==INTEGRAL || path[pathlength-3] == SUM))
          { o[i] = apart; ++i;
          }
     }
  /* The following code gets sin((arctan(8)-pi)/2) simplified
     by applying apart to the argument of sin
  */
  if(g == '+' && pathlength >= 3 &&
     TRIGFUNCTOR(path[pathlength-3]) && ARITY(ARG(0,t)) == 2 &&
     equals(ARG(1,t),two) &&
     ( ARCTRIGFUNCTOR(FUNCTOR(ARG(0,ARG(0,t)))) ||
       (FUNCTOR(ARG(0,ARG(0,t))) == '*' && ARCTRIGFUNCTOR(FUNCTOR(ARG(1,ARG(0,ARG(0,t))))) && ISINTEGER(ARG(0,ARG(0,ARG(0,t)))) && ARITY(ARG(0,ARG(0,t))) == 2)
     ) &&
     ( equals(ARG(1,ARG(0,t)),pi) ||
       (NEGATIVE(ARG(1,ARG(0,t))) && equals(ARG(0,ARG(1,ARG(0,t))),pi))
     )
    )
    { o[i] = apart; ++i;
    }



  if(
     (problemtype == SIMPLIFY || problemtype == ROOTS) &&
     (pathlength < 3 || path[pathlength-3] != '*') &&
      /* if the fraction is in a product, multiply it out first */
     (
       (h == '+' && ARITY(denom) == 2) ||
       (h == SQRT && alg_numerical(t)) ||
       rationalizable_cubic(denom)
     ) &&
     (!constant(num) || seminumerical(t)) &&
     contains_sqrt(denom) &&
     !(
       h == SQRT && NUMBER(num) &&  /* leave sin(1/sqrt 2) alone */
       (
         (pathlength >= 3 && TRIGFUNCTOR(path[pathlength-3])) ||
         (pathlength >= 5 && TRIGFUNCTOR(path[pathlength-4]) && path[pathlength-3]=='-')
       )
      ) &&
     !get_infractionflag()  /* don't rationalize denom in a compound fraction */
    )
     { o[i] = rationalizedenom; ++i;
     }
  else if(seminumerical(t) && rationalizable_cubic(denom))
     /* e.g. in cubic equations involving a preliminary substitution to
        get rid of the quadratic term, the problemtype doesn't revert to
        SIMPLIFY until after all the defined variables are eliminated,
        but we need to simplify the solutions after the cubic is solved. */
     { o[i] = rationalizedenom; ++i;
     }
  if(!get_infractionflag() &&
     !comdenomflag && g == '+' &&
     !SOLVETYPE(problemtype) &&
     (problemtype != TRIG_IDENTITY ||
      (pathlength >= 3 && path[pathlength-3] != TAN && path[pathlength-3] != COT) &&
      (pathlength < 5 || path[pathlength-5] != '^')
     ) &&
     /* apart makes trouble in trig identities; generally we want to bring
        both sides to fractional form and then, if all else fails,
        cross multiply.  But, we DO need to simplify sin((t + pi)/2),
        but NOT sin^2((t+pi)2)...*/
     !(numerical(t) && contains(ARG(1,t),SQRT)) &&
     (!contains_sqrt(ARG(1,t)) || iscomplex(ARG(0,t))) &&
     /* rationalize the denominator instead of using apart,
        but not if the numerator is complex, e.g. (a + bi)/ sqrt(2) which
        must become a/sqrt(2) + (b/sqrt(2)) i
     */
     !contains_defined_variables(t) &&
        /* don't use apart until defined variables have been eliminated,
           for example  (1-u^2)/u with u = sin t; the numerator
           will simplify to cos^2 t, but not if apart is used. */
     (!limitflag || inlogflag)
        /* inlogflag permits us to use apartandcancel
           on ln((x+h)/x) inside a limit, as required in doing
           diff(ln x,x) from the definition of derivative;
           but we don't want it when doing limits of rational functions. */
    )
     { /* use apart on  (3+2i)/5  for example  */
       /* This is not done by 'apart' but by 'complexapart' */
       if(get_complex())
          { if(FUNCTOR(ARG(0,t)) == '+' && ARITY(ARG(0,t)) == 2 &&
               iscomplex(ARG(1,t)) && !iscomplex(ARG(0,t)) &&
               !contains(ARG(0,t),'+') && !contains(ARG(1,t),'+')
              )
               { o[i] = complexapart; ++i;
               }
            else
               { o[i] = complexform; ++i;
               }
          }
       if(problemtype != TRIG_IDENTITY && !apartandcancelflag)
          { o[i] = apartandcancel; ++i;
          }
       if(problemtype == LINEAR_EQUATIONS && !constant(t))
          { o[i] = apart; ++i;
          }
       else if(pathlength >= 3 && TRIGFUNCTOR(path[pathlength-3]))
          /* Example:  sin((2t + pi)/2) = sin(t + pi/2)  */
          /* In automode, apartandcancel will not work if only a constant cancels,
             as in this example.  We have to use apart.  But we really want
             to use it ONLY if there is going to be a cancellation, because
             in general we don't want sin((u+v)/2) to go to sin(u/2 + v/2).
          */
          { char buffer[DIMREASONBUFFER];
            term w,q,a,b;
            int k,err;
            if(equals(denom,two) && ARITY(num) == 2 && equals(ARG(0,num),pi) && NEGATIVE(ARG(1,num)))
               { o[i] = apart; ++i;
               }
            else
               { err = apart(t,zero,&w,buffer);
                 if(!err && FUNCTOR(w) == '+')
                    { for(k=0;k<ARITY(w);k++)
                         { q = ARG(k,w);
                           if(NEGATIVE(q))
                              q = ARG(0,q);
                           if(FRACTION(q) && !cancel(q,zero,&a,&b))
                              break;
                         }
                      if(k < ARITY(w))
                         { o[i] = apart; ++i;
                         }
                    }
               }
          }
     }
/* apart is (a+b)/c = a/c + b/c
 apart_and_cancel is apart followed by successful cancelling of at least
 one term of the sum.
 'Apart' loops with commondenom and  with addfractions (a/c + b/c = (a+b)/c)
 We use add_fractions to get rid of compound fractions and to
 hope (a+b) may cancel with c.  Using apartandcancel prevents that loop.
*/
  if(g == '+' && h == '+' && !comdenomflag &&
     !get_infractionflag() && problemtype < LIMITS &&
     !insqrtflag && !inrootflag  && !infractexpflag
      /* don't attack integral(sqrt((1+x)/(1-x)),x) by polynomial division */
    )
     { o[i] = polydivop; ++i;  /* polydiv with remainder */
     }
       /*  rationalizefraction is called from autocalc.c
           as it takes functor LIMIT */
  if( (intflag || seriesflag || indexflag || problemtype == SIMPLIFY) &&
      h == '+' && ARITY(denom) == 2 && contains(denom,SQRT) &&
      !get_infractionflag()
    )
     { o[i] = rationalizedenom; ++i;
       /* example, integral(1/(sqrt(x+1) + sqrt x),x)   */
     }
  if( (intflag || seriesflag || indexflag) && contains_sqrt(num) && contains_sqrt(denom))
     { o[i] = rationalizedenom; ++i;
       /* example, integral(sqrt(1-x)/sqrt(1+x),x)
          This will get us to an integral with only one square root,
          in the numerator.
       */
     }
  if(g == SQRT &&
     (
      (pathlength >= 3 && TRIGFUNCTOR(path[pathlength-3])) ||
      (pathlength >= 5 && TRIGFUNCTOR(path[pathlength-5]) && path[pathlength-3] == '-')
     )
    )
     { o[i] = cancelsqrt2; ++i;  /* sin(1/sqrt 2), not sin(sqrt(2)/2) */
     }
  if(contains_sqrt(num) || contains_sqrt(denom))
     /* since num and/or denom could contain several of ROOT, SQRT, ABS,
        you can't rely on the return value of contains_sqrt; better
        try all three. */

     { o[i] = cancelsqrt3; ++i;
       o[i] = cancelabs3;   ++i;
       o[i] = cancelroot3; ++i;
       o[i] = cancelsqrtgcd; ++i;
       o[i] = cancelrootgcd; ++i;
       o[i] = cancelabsgcd; ++i;
     }
  if(g == '*' || g == '^' || h == '^' || h == '*')
     { o[i] = powerstonum; ++i;
       o[i] = powerstodenom; ++i;
     }
  if(g == '-')
     { o[i] = minusoutfromnum; ++i;
     }
  if(h == '-')
     { o[i] = minusoutfromdenom; ++i;
     }
  if(g == '*' && INTEGERP(denom) &&
     numerical(ARG(0,num)) &&
     !numerical(ARG(1,num)) &&   /* don't use it on (3 sqrt(13) x)/13
                                    because polyvalop will just undo it */
     (get_ringflag() & RATRING) &&
     !canonical(0,t) &&
     !limitflag    /* it's a waste of time in limits to write 3x/2 as (3/2) x */
    )
     { o[i] = breakfraction2; ++i;
       /* convert sqrt(3) x /2  to (sqrt(3)/2) x */
     }
  if(contains(t,SIN) && contains(t,COS))
      /* example:   (1-cos x)/sin^2x => (1-cos x)/(1-cos^2 x)  */
     { o[i] = sinsqtocossq; ++i;
       o[i] = cossqtosinsq; ++i;
     }
  if(h == SG || (h == '*' && contains_at_toplevel(denom,SG)))
     { o[i] = sgrecip; ++i;  /* 1/sg(x) = sg(x) */
     }
  if(g == '^' && h == '^' &&
     FUNCTOR(ARG(0,num)) == TAN && FUNCTOR(ARG(0,denom)) == COS &&
     (difflag || pathlength == 1 || (pathlength == 3 && path[0] == '=')) &&
     equals(ARG(0,ARG(0,num)),ARG(0,ARG(0,denom)))
    )
     { o[i] = tanrule2; ++i;
     }
  if(g == SIN && h == COS &&
     (
       difflag ||
       (pathlength >= 3 && path[pathlength-3] == ATAN) ||
       pathlength == 1 ||
       (pathlength == 3 && path[0] == '=')
     )
     &&
     equals(ARG(0,num),ARG(0,denom))
    )
     { o[i] = tanrule2; ++i;  /* arctan(sin x/cos x) => arctan(tan x) */
     }
  if(g == '^' && h == '^' &&
     FUNCTOR(ARG(0,num)) == COS && FUNCTOR(ARG(0,denom)) == SIN &&
     (difflag || pathlength == 1 || (pathlength == 3 && path[0] == '=')) &&
     equals(ARG(0,ARG(0,num)),ARG(0,ARG(0,denom)))
    )
     { o[i] = cotrule2; ++i;
     }
  if(g == COS && h == SIN &&
     (
       difflag ||
       (pathlength >= 3 && path[pathlength-3] == ACOT) ||
       pathlength == 1 ||
       (pathlength == 3 && path[0] == '=')
     ) &&
     equals(ARG(0,num),ARG(0,denom))
    )
     { o[i] = cotrule2; ++i;  /* arccot(cos x/sin x) => arccot(cot x) */
     }
  if(ONE(num) && ( h == SIN || (h == '^' && FUNCTOR(ARG(0,denom)) == SIN)) &&
     ( pathlength == 1 ||
       (pathlength >= 3 && path[pathlength-3] == ACSC) ||
       (pathlength >= 3 && path[pathlength-3] == '=' && problemtype == TRIG_IDENTITY)
     )
    )
     { o[i] = cscrule2; ++i;  /* 1/sin = csc, inside arcscs or at toplevel;
                                 but don't use it at toplevel when solving
                                 equations, as csc x = a is solved by
                                 writing csc x = 1/ sin x, not vice-versa */
     }
  if(ONE(num) && ( h == COS || (h == '^' && FUNCTOR(ARG(0,denom)) == COS)) &&
     ( pathlength == 1 ||
       (pathlength >= 3 && path[pathlength-3] == ASEC) ||
       (pathlength >= 3 && path[pathlength-3] == '=' && problemtype == TRIG_IDENTITY)
     )
    )
     { o[i] = secrule2; ++i;  /* 1/cos = sec, inside arcsec or at toplevel */
     }
  if(ONE(num) && ( h == TAN || (h == '^' && FUNCTOR(ARG(0,denom)) == TAN)) &&
     ( difflag ||
       pathlength == 1 ||
       (pathlength >= 3 && path[pathlength-3] == ACOT) ||
       (pathlength >= 3 && path[pathlength-3] == '=' && problemtype == TRIG_IDENTITY)
     )
    )
     { o[i] = tanrecip; ++i;  /* 1/tan = cot, inside arccot or at toplevel when
                                 verifying identities; but don't use it at
                                 toplevel when solving equations, e.g. 1/tan x = 1.
                              */
     }
  if(ONE(num) && ( h == COT || (h == '^' && FUNCTOR(ARG(0,denom)) == COT)) &&
     ( difflag ||
       pathlength == 1 ||
       (pathlength >= 3 && path[pathlength-3] == ATAN) ||
       (pathlength >= 3 && path[pathlength-3] == '=')
     )
    )
     { o[i] = cotrecip; ++i;  /* 1/cot = tan, inside arctan or at toplevel */
     }
  if(ONE(num) && ( h == SEC || (h == '^' && FUNCTOR(ARG(0,denom)) == SEC)) &&
     ( difflag ||
       pathlength == 1 ||
       (pathlength >= 3 && path[pathlength-3] == ACOS) ||
       (pathlength >= 3 && path[pathlength-3] == '=')
     )
    )
     { o[i] = secrecip; ++i;  /* 1/sec = cos, inside arccos or at toplevel */
     }
  if(ONE(num) && ( h == CSC || (h == '^' && FUNCTOR(ARG(0,denom)) == CSC)) &&
     ( difflag ||
       pathlength == 1 ||
       (pathlength >= 3 && path[pathlength-3] == ASIN) ||
       (pathlength >= 3 && path[pathlength-3] == '=')
     )
    )
     { o[i] = cscrecip; ++i;  /* 1/csc = sin, inside arcsin or at toplevel */
     }
  if(limitflag)   /* example:  lim(x->infinity, x(2x-1)/(x^2+3)) */
     { term x = get_eigenvariable();
       if(  (!contains(num,'^') && FUNCTOR(num) == '*' && ARITY(num) <= 3
             && contains(ARG(0,num),FUNCTOR(x))
             /* don't expand a numerator with a constant first factor */
            )
            ||
            (FUNCTOR(num) == '*' && status(limrationalfunction) <= LEARNING
             && polyprod(num,x)
             && contains(ARG(0,num),FUNCTOR(x))
             /* don't expand a numerator with a constant first factor */
            )
         )
          { o[i] = expandnumerator; ++i;}

       if( (!contains(denom,'^') && FUNCTOR(denom) == '*' && ARITY(denom) <= 3
            && contains(ARG(0,denom),FUNCTOR(x))
             /* don't expand a denominator with a constant first factor */
           )
           || (FUNCTOR(denom) == '*' && status(limrationalfunction) <= LEARNING
               && polyprod(denom,x)
               && contains(ARG(0,denom),FUNCTOR(x))
                /* don't expand a denominator with a constant first factor */
              )
         )
          { o[i] = expanddenominator; ++i;}
       /* this means that divnumdenom will get called on more complicated
          cases of quotients of products of powers of polynomials */
     }
  if(numerical(t) && contains(ARG(0,t),'^') && contains(ARG(1,t),'^'))
     { o[i] = factorbase; ++i;
     }
  if(pathlength >= 3 && path[pathlength-3] == LIMIT &&
     contains(ARG(1,t),SEC)
    )
     { o[i] = secrecip; ++i;
     }
  if(pathlength >= 3 && path[pathlength-3] == LIMIT &&
     contains(ARG(1,t),CSC)
    )
     { o[i] = cscrecip; ++i;
     }
  *nops = i;
}
/*________________________________________________________________________*/
int attractible(term t, term x)
/*  x is a variable, t is a compound term (if not return 0);
   return 1 if more than one arg of t contains x, else return 0. */

{ int i,k;
  unsigned short n,c;
  if(!ATOMIC(x) || ATOMIC(t))
      return 0;
  n = ARITY(t);
  c = FUNCTOR(x);
  k=0;
  for(i=0;i<n;++i)
    { if(contains(ARG(i,t),c))
           ++k;
      if(k==2) return 1;
    }
  return 0;
}
/*___________________________________________________________________*/
static int fractional_factor(term t)
/* t must be a quotient or product.  Return 1 if t is a fraction or
contains a factor which is a fraction, else return 0. */

{  unsigned short n;
   int i;
   if(ATOMIC(t))
      return 0;
   if(FUNCTOR(t) == '/')
      return 1;
   if(FUNCTOR(t) != '*')
      return 0;
   n = ARITY(t);
   for(i=0;i<n;i++)
     { if(fractional_factor(ARG(i,t)))
           return 1;
     }
   return 0;
}
/*_______________________________________________________________*/
static int sqrt_condition(term u, term t)
/* Return 1 if sqrtofquotient is to be used on t, which is a SQRT of
a quotient.  u is the current line of the computation, of which
t is a part.  */
/* Don't use it if t is equal to  � the current line, or is
one factor of the current line and the others are rational numbers
or terms not containing square roots or roots, and the num and denom
are both squarefree */
/* Designed to get (1/2) (sqrt 2/sqrt 3) changed to (1/2) sqrt(2/3) etc. */

{ term num,denom;
  if(NEGATIVE(u))
     u = ARG(0,u);
  assert(FUNCTOR(t) == SQRT && FUNCTOR(ARG(0,t)) == '/');
  num = ARG(0,ARG(0,t));
  denom = ARG(1,ARG(0,t));
  if(!nsquarefree(num) || !nsquarefree(denom))
      return 1;     /* use it!  the resulting � will simplify */
  if(equals(t,u))
      return 0;     /* don't use it */
  if(ONE(num))
      return 1;    /* DO change sqrt(1/x) to 1/sqrt x */
  if(ATOMIC(u) || FUNCTOR(u) != '*' || ARITY(u) != 2)
      return 0;
  if( INTEGERP(ARG(0,u)) || RATIONALP(ARG(0,u)))
      return !equals(t,ARG(1,u));   /* don't use it if  u = ct */
  if( INTEGERP(ARG(0,u)) || RATIONALP(ARG(1,u)))
      return !equals(t,ARG(0,u));
  return 0;
}
/*_______________________________________________________________*/
static int contains_integer_sqrt(term t)
/* t is a product; return 1 if one of the factors is a SQRT of an integer */
{ unsigned short n = ARITY(t);
  int i;
  term u;
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u) == SQRT && INTEGERP(ARG(0,u)))
           return 1;
     }
  return 0;
}
/*_______________________________________________________________*/
static int contains_integer_root(term t, term index)
/* t is a product; return 1 if one of the factors is an
index-th root of an integer */
{ unsigned short n = ARITY(t);
  int i;
  term u;
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u) == ROOT && equals(ARG(0,u),index)
          && INTEGERP(ARG(1,u))
         )
           return 1;
     }
  return 0;
}
/*________________________________________________________________*/
static int por_aux2(term num, term denom)
/* called by next function below; return 1 if num is a sqrt or n-th root
   of an integer, and denom is a product of such a thing and an integer. */
{ if(FUNCTOR(num) == SQRT && FUNCTOR(denom) == '*' && ARITY(denom)==2
    && INTEGERP(ARG(0,num)) && contains_integer_sqrt(denom)
   )  /* as in  sqrt 3/(2 sqrt 5)  which we want to go to (1/2)( sqrt 3/sqrt 5),
         whence quotientofsqrts will make it (1/2)sqrt(3/5)   */
    return 1;
 if(FUNCTOR(num) == ROOT && FUNCTOR(denom) == '*' && ARITY(denom)==2
    && INTEGERP(ARG(1,num)) && contains_integer_root(denom,ARG(0,num))
   )
    return 1;
 return 0;
}
/*________________________________________________________________*/
int pulloutrational_aux(term num, term denom)
/* return 1 if pulloutrational should be used on num/denom */
{ if(por_aux2(num,denom))
     return 1;
  if(por_aux2(denom,num))
     return 0;
  if(FUNCTOR(num) =='*' && ARITY(num) == 2 &&
     INTEGERP(ARG(0,num)) && contains_integer_sqrt(num) &&
     FUNCTOR(denom) == '*' && ARITY(denom) == 2 &&
     INTEGERP(ARG(0,denom)) && contains_integer_sqrt(denom)
    )  /* example:   (4 �3)/(5 �7) = (4/5)�(3/7)  */
      return 1;
  if(FUNCTOR(num) =='*' && ARITY(num) == 2 &&
     INTEGERP(ARG(0,num)) && FUNCTOR(ARG(1,num)) == ROOT &&
     INTEGERP(ARG(0,ARG(1,num))) &&
     FUNCTOR(denom) == '*' && ARITY(denom) == 2 &&
     INTEGERP(ARG(0,denom)) && FUNCTOR(ARG(1,denom)) == ROOT &&
     equals(ARG(0,ARG(1,denom)),ARG(0,ARG(1,num)))
    )
      return 1;
  return 0;
 }
/*_________________________________________________________*/
static int in_sum(void)
 /* return 1 if we are working on a summand and on the way up */
 /* when going up, the current functor is found at the end of the path.
 See documentation of 'push' and 'pop' in autosimp.c */

{ int pathlength = get_pathlength();
  unsigned short  *path = get_path();
  int n;
  if(pathlength == 0)
     return 0;
  if(pathlength == 1)
     return  path[0] == '+' ? 1: 0;
  n = pathlength-1;
  if(n&1)  /* pathlength even, means it terminates in an object or atom */
     --n;
  if(path[n] == '^')
     n-=2;
  if(n<0)
     return 0;
  if(path[n] == '*')
     n-=2;
  if(n<0)
     return 0;
  if(path[n] == '-')
     n-=2;
  if(n<0)
     return 0;
  if(path[n] == '+')
     return 1;
  return 0;
}
/*__________________________________________________________*/
int sqrtexp_conditions(term t)
/* Should we use sqrtexp on sqrt t ? If so return 1 */

{ int currenttopic = get_currenttopic();
  if(currenttopic == _cubic_one_root ||
     currenttopic == _complex_cubics ||
     (currenttopic == _diff_from_def && difflag) ||
     (currenttopic == _diff_exp_from_defn && difflag)
    )
    return 0;  /* don't rewrite sqrt(93) as 93^(1/2) under the cube root.*/
  if(get_radicalflag() == -1)
     { int pathlength = get_pathlength();
       unsigned short  *path = get_path();
       if(pathlength >= 3 &&
          path[pathlength-3] == LIMIT
         )
          return 0;  /* don't do it just inside a limit;
                        only if deeper within a limit. */
       if(pathlength >= 5 &&
          path[pathlength-5] == LIMIT &&
          path[pathlength-3] == '/'
         )
          return 0;  /* don't do it to num and denom just inside a limit either */
       return 1;
      }
  if(iscomplex(t) ||
     (NEGATIVE(t) && obviously_positive(ARG(0,t)))
    )
     return 1;  /* needed in de_moivre */
  if(!get_intflag() || constant(t))
     return 0;   /* use it only inside integrals, and don't use it on �2 */
  if(ISATOM(t) || monomial(t))  /* handle 1/�x => x^(1/2) and �(x�)  */
     return 1;
  return 0;        /* but don't touch 1/�(3x^2-1)     */
}
/*________________________________________________________*/
static int unsolved_lineqns(term t)
/* t is presumed to be an AND.  Return 0 if any of its
arguments fails to be an equation.  Otherwise,
return 0 if all the arguments are solved equations,
1 if any is not solved.
  The use of this function is to prevent trying to
solve systems that are already solved, e.g. by applying
Cramer's rule again and again.
*/

{ unsigned short n;
  term u;
  int i;
  if(FUNCTOR(t) != AND)
     return 0;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(FUNCTOR(ARG(i,t)) != '=')
          return 0;
     }
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(!ISATOM(ARG(0,u)) || !constant(ARG(1,u)))
          /* the i-th equation is not solved */
          return 1;
     }
  return 0;
}
/*__________________________________________________________*/
int contains_double(term t)
/* return 1 if t contains an object of type DOUBLE,
0 if not */
{ unsigned short n;
  int i;
  if(OBJECT(t) && TYPE(t) == DOUBLE)
     return 1;
  if(ATOMIC(t))
     return 0;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_double(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*___________________________________________________________*/
MEXPORT_AUTOMODE int stop_apart(term t)
/* t is a fraction inside an integral
Return 1 if we should NOT use apart on it, 0 otherwise.
Specifically, return 1 under the following conditions:

(1) if the denominator has the
form cos x � sin x or 1 � cos x or 1 � sin x;
(2) or if the parent is a trig functor, so we don't
touch sin((u+v)/2) for example, because we don't want
it to go to sin(u/2 + v/2) which will be attacked by sinsum
instead of by a half-angle formula.
(3) when the integrand has the form linear/sqrt(linear) so
that u = sqrt(linear) should be used on the whole integral.
The old variable x will be quadratic in u so SQRT will be
eliminated.
*/
{ term x = get_eigenvariable();
  term denom,p,q;
  unsigned short f,g,h;
  int pathlength = get_pathlength();
  unsigned short  *path = get_path();
  assert(FRACTION(t));
  denom = ARG(1,t);
  if(pathlength >= 3)
     { h = path[pathlength-3];
       if(TRIGFUNCTOR(h))  /* this includes arctrig functors too */
          return 1;
     }
  if(FUNCTOR(denom) == SQRT && is_linear_in(ARG(0,denom),x))
     return 1;
  if(FUNCTOR(denom) != '+')
     return 0;
  if(ARITY(denom) != 2)
     return 0;
  p = ARG(0,denom);
  q = ARG(1,denom);
  if(NEGATIVE(p))
     p = ARG(0,p);
  if(NEGATIVE(q))
     q = ARG(0,q);
  f = FUNCTOR(p);
  g = FUNCTOR(q);
  if(f==SIN && g==COS && equals(ARG(0,p),x) && equals(ARG(0,q),x))
     return 1;
  if(f==COS && g==SIN && equals(ARG(0,p),x) && equals(ARG(0,q),x))
     return 1;
  if((f == COS || f == SIN) && ONE(q) && equals(ARG(0,p),x))
     return 1;
  if((g == COS || g == SIN) && ONE(p) && equals(ARG(0,q),x))
     return 1;
  return 0;
}
/*________________________________________________________________________*/
static int elimfractexp_conditions(unsigned pathlength, unsigned short *path)
/* don't convert fractional exponents to roots while just inside
  another exponent. Return 0 to block elimination of fractional exponents,
  1 to permit it. */
{ int k;
  if(pathlength < 3)
     return 1;
  for(k = pathlength-3; k >= 0; k -= 2)
    { if(path[k] == '^')
         return 0;
      if(path[k] == '/' || path[k] == '*' || path[0] == '-')
         continue;
      return 1;
    }
  return 1;
}
/*______________________________________________________________________*/
static int ok_complexexp(unsigned short *path, int pathlength)
/*  path gives the position of t in the current line,
with path[pathlength-3] the position of the parent,
which is presumed to be a product.
Return 1 if this parent does not contain complexi
outside of the subterm t. Return 0 if it does.
*/
{ unsigned short n;
  int i,k,err;
  term u;
  unsigned short saveit = path[pathlength-2];
  path[pathlength-2] = 0;
  err = subterm_at_path(history(get_activeline()),path,&u);
  if(err)
     return 1;
  path[pathlength-2] = saveit;
  if(FUNCTOR(u) != '*')
     return 1;
  k = path[pathlength-2]-1;
  if(k < 0)
     assert(0);
  n = ARITY(u);
  for(i=0;i<n;i++)
     { if(i==k)
          continue;
       if(iscomplex(ARG(i,u)))
          return 0;
     }
  return 1;
}

/*_________________________________________________________________________*/
static int stop_logofproduct(term t)
/* t is a log term.  If its arg is linear with a small integer coefficient,
and it's at toplevel or separated by a minus sign from toplevel then
return 1, else return 0. Examples: on log(3x) or - log(3x) return 1; on
log(x/3) return 0, so this will become log x - log 3.   Even log(12 x)
will become log x + log 12 rather than remain log(12 x).
*/

{ unsigned short f = FUNCTOR(t);
  term u;
  unsigned short  *path = get_path();
  int pathlength = get_pathlength();
  if(pathlength > 5)
     return 0;
  if(pathlength > 3 && path[0] != '-')
     return 0;
  if(f == LN || f == LOG)
     u = ARG(0,t);
  else if(f == LOGB)
     u = ARG(1,t);
  else
     return 0;
  if(FUNCTOR(u) == '*' && ARITY(u) == 2 &&
     ISINTEGER(ARG(0,u)) && INTDATA(ARG(0,u)) < 10 &&
     ATOMIC(ARG(1,u))
    )
     return 1;
  return 0;
}
/*_______________________________________________________________________*/
static int log_in_exponent(term t)
/* return 1 if there is a LN, LOG, or LOGB in an exponent in t,
0 otherwise.
*/
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == '^')
     { if(log_in_exponent(ARG(0,t)))
          return 1;
       return contains_log(ARG(1,t));
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(log_in_exponent(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*__________________________________________________________________________*/
static int logbases(term t, term *a, term *b)
/* return 0 if t has no LOGB subterms; return 1 if there is only one
base of LOGB subterms, and put the base in *a;  return 2 if there are
two or more, and put the first two in *a and *b.  If 1 is returned,
*b is garbage; if 0 is returned, both *a and *b are garbage.
Nested LOGB terms will be ignored.  Don't count terms of the form logb(b,b^z)
since the base b log will be simplified away when this term is changed to z very soon.
*/
{ unsigned short n,f;
  int i,k,count;
  term p,q;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == LOGB &&
     !(FUNCTOR(ARG(1,t)) == '^' && equals(ARG(0,t),ARG(0,ARG(1,t))))
    )
     { *a = ARG(0,t);
       return 1;   /* Don't look in ARG(1,t) */
     }
  n = ARITY(t);
  count = 0;
  for(i=0;i<n;i++)
     { k = logbases(ARG(i,t),&p,&q);
       if(k == 0)
          continue;
       if(k == 1)
          { if(count && !equals(*a,p))
               { *b = p;
                 return 2;
               }
            *a = p;
            count = 1;
            continue;
          }
       if(k == 2)
          { if(count == 0)
               { *a = p;
                 *b = q;
                 return 2;
               }
            if(count == 1 && !equals(*a,p))
               { *b = p;
                 return 2;
               }
            if(count == 1 && !equals(*a,q))
               { *b = q;
                 return 2;
               }
            assert(0);
          }
     }
  return count;
}
/*_______________________________________________________________________*/
static int stop_changebase(void)
/* return 1 if all log bases in the active line are the same,
and there are no logs in exponents. */
{ term t,a,b;
  int activeline = get_activeline();
  int nlogbases;
  if(activeline < 0)
     return 0;
  t = history(activeline);
  nlogbases = logbases(t,&a,&b);
  if(nlogbases > 1)
     return 0;
  if(log_in_exponent(t))
     return 0;
  return 1;
}

/*_________________________________________________________________________*/
static int rationalizable_cubic(term denom)
/* return 1 if denom is of the form root(3,a+sqrt(b))
or a constant times such a term, zero otherwise.  Any odd root
is allowed, not just a cube root.
*/
{ unsigned short h = FUNCTOR(denom);
  unsigned short n;
  int i, rootflag;
  term u,v,index;
  if(h == ROOT)
     { index = ARG(0,denom);
       if(!isodd(index))
          return 0;
       u = ARG(1,denom);
       if(FUNCTOR(u) != '+' || ARITY(u) != 2)
          return 0;
       v = ARG(1,u);
       if(NEGATIVE(v))
          v = ARG(0,v);
       if(FUNCTOR(v) == SQRT)
          return 1;
       if(FUNCTOR(v) == '*' && contains_sqrt(v))
          return 1;
     }
 if(h == '*')
     { n = ARITY(denom);
       rootflag = 0;
       for(i=0;i<n;i++)
          { if(FUNCTOR(ARG(i,denom)) == ROOT)
               { if(rootflag)
                    return 0;
                 rootflag = i+1;
               }
          }
       if(!rootflag)
          return 0;
       if(rationalizable_cubic(ARG(rootflag-1,denom)))
          return 1;
     }
  return 0;
}
/*______________________________________________________________________*/
static int insumfract(void)
/* return 1 if the path terminates in a term which is either
a (possibly negated) summand, or a factor of such a summand. */

{ unsigned short  *path = get_path();
  int k = get_pathlength()-3;
  if(k < 0)
     return 0;
  while(k >= 0 && (path[k] == '-' || path[k] == '*'))
     k -= 2;
  if(k < 0)
     return 0;
  if(path[k] == '+')
     return 1;
  return 0;
}

/*_____________________________________________________________________*/
static int sg_ok(void)
/* t is an ABS term in an indefinite integral.  Return 1 if it's a
good idea to replace |u| by u sg u; return 0 if not.  We need to
do this after trig substitutions, e.g. to change abs(tan theta)^3 to
tan^3 theta  sg(tan theta), etc.  But,
    ln(|x|) = ln(x sg(x)) = ln(|x|) + ln(|sg(x)|)
leads to a regress, and
    arcsec |x| = arcsec(x sg x) is a dead end.
We don't want to use this law anywhere inside a log or an arctrig
functor, e.g. not in arccos(1/|x|), so it won't do just to look at
path[pathlength-3].
*/

{ int k = get_pathlength()-3;
  unsigned short  *path = get_path();
  if(k < 0)
     return 0;  /* assert(0) */
  while(k >= 0)
     { if(UNARY(path[k]) && path[k] != '-')
          return 0;
       if(path[k] == LOGB || path[k] == ROOT || path[k] == DIFF)
          return 0;
       if(path[k] == INTEGRAL)
          return 1;
       k -=2;
     }
  return 0;  /* assert(0), this is only called when t is somewhere in an integral */
}
/*_______________________________________________________________________*/
static int contains_root_in_denom(term t)
/* return 1 if t contains a ROOT in a denominator */
{ int i;
  unsigned short n;
  if(ATOMIC(t))
     return 0;
  if(FRACTION(t))
     { if(contains(ARG(1,t),ROOT))
          return 1;
       return contains_root_in_denom(ARG(0,t));
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_root_in_denom(ARG(i,t)))
          return 1;
     }
  return 0;
}

/*_______________________________________________________________________*/
static int solved_cubic(term t)
/* return 1 if t is one of two cubic equations ready to have
translatevar used on them.
*/
{ term u,v,w;
  int i;
  if(FUNCTOR(t) != '=')
     return 0;
  if(!ISATOM(ARG(0,t)))
     return 0;
  u = ARG(1,t);
  if(FUNCTOR(u) != '+' || ARITY(t) != 2)
     return 0;
  for(i=0;i<2;i++)
     { v = ARG(i,u);
       if(FUNCTOR(v) != '*')
          return 0;
       w = ARG(ARITY(v)-1,v);
       if(contains_root_in_denom(w))
          return 0;
       if(FUNCTOR(w) != '^')
          return 0;
       if(!equals(ARG(0,w),eulere))
          return 0;
       if(!iscomplex(ARG(1,w)))
          return 0;
     }
  return 1;
}

/*_____________________________________________________*/
static int oksqrt_aux(term u, term v)
/* At the top-level call:
      u contains a SQRT term, v does not;
      v contains a fractional exponent, u does not;
   return 1 if OK to leave u/v without converting
   the SQRT to fractional exponent.
*/
{ int i,err;
  unsigned short n;
  term p;
  if(ATOMIC(u) || ATOMIC(v))
     return 1;
  if(FUNCTOR(u) == '+' || FUNCTOR(u) == '*')
     { n = ARITY(u);
       for(i=0;i<n;i++)
          { if(!oksqrt_aux(ARG(i,u),v))
                return 0;
          }
       return 1;
     }
  if(FUNCTOR(v) == '+' || FUNCTOR(v) == '*')
     { n = ARITY(u);
       for(i=0;i<n;i++)
          { if(!oksqrt_aux(u,ARG(i,v)))
                return 0;
          }
       return 1;
     }
  if(NEGATIVE(u))
     return oksqrt_aux(ARG(0,u),v);
  if(NEGATIVE(v))
     return oksqrt_aux(u,ARG(0,v));
  if(FUNCTOR(u) == SQRT && !INTEGERP(ARG(0,u)))
     return 0;
  if(FUNCTOR(v) == '^' && !(INTEGERP(ARG(0,v)) && SIGNEDRATIONAL(ARG(1,v))))
     return 0;
  if(FUNCTOR(u) == SQRT && FUNCTOR(v) == '^')
     { err = gcd(ARG(0,u),ARG(0,v),&p);
       if(err || !ONE(p))
          return 0;
       return 1;
     }
  return 0;
}



/*_____________________________________________________*/
static int oksqrt(term t)
/* return 1 if it's ok to leave both fractional exponents
and square roots in t; return 0 if not.  Example: return 1 on
(sqrt(3)-i)/2^(3/4).  We want to watch out for cases where
we might miss some simplifications, like sqrt(2)/2^(1/4).
*/
{ int i;
  unsigned short n;
  term u,v;
  if(ATOMIC(t))
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(!oksqrt(ARG(i,t)))
          return 0;
     }
  if(!FRACTION(t))
     return 1;
  /* Now look for cancellations we might miss if we don't
     convert sqrts to fractional exponents */
  u = ARG(0,t);
  v = ARG(1,t);
  if(contains(u,SQRT) && contains_fractional_exponents(u))
     return 0;
  if(contains(v,SQRT) && contains_fractional_exponents(v))
     return 0;
  if(contains(v,SQRT) && contains_fractional_exponents(u))
     { term swap = u;
       u = v;
       v = swap;
     }
  if(!(contains(u,SQRT) && contains_fractional_exponents(v)))
     return 1;
  return oksqrt_aux(u,v);
}

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