Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/automode/
Upload File :
Current File : /usr/home/beeson/MathXpert/automode/autosum.c

/*
M. Beeson;  automode strategy for sums and products in Mathpert
1.7.91 original date
3.19.99  modified
1.5.00 added code to block addfractions on complex numbers
1.6.00 added ...|| topic == _trigproduct lines
1.6.00 removed 'finishedfactoring'
1.10.00 added differenceofnth2
1.15.00 modified conditions for rectangulartopolar in autoproduct
1.15.00 removed conditions for using contentfactor in a complex exponent
2.3.00  modified conditions for complexform
2.3.00  added complexexpression(t) as a precondition for using common denominators
2.3.00 added rectangulartopolar in autoproduct
10.3.02 changed the line bearing this date, because VS.NET caught an error.
1.27.06 added abstimessg
1.29.06 added !difflag at line 294
8.9.07  modified conditions for expand
4.30.13 added constantintosigma for POWERSERIES
5.1.13 added multiplyoutandsimp for POWERSERIES
5.6.13  include stdef.h
5.9.13  made some flags const, and used get_difflag() instead of just difflag
5.30.13  added code at the end of comdenom_conditions to get common denoms used on sums occurring in products
5.31.13 added code to prevent factoring by differenceofnthpowers,  factorbypolydiv,  etc. in POWERSERIES,
        and not to use contentfactor on a sum of arity 2 with one summand equal to 1.
        changed code controlling constantintosigma so x^-2 can get multiplied into a series
        added code to get cleardenoms used just inside a series
6.8.13 only use writeaspoly if its status is at least KNOWN
6.11.13 modified code controlling constantintosigma in powerseries
7.13.13 added code to block rectangular_to_polar on _complex_cubics
10.7.24 made autoproduct not use multiplycomplexconjugates if problemtype is FACTOR.
12.9.24 commented out something at the dated line to prevent multiply when factoring.
1.10.25  don't use addfractions inside infinite series
1.25.25.  Put dated lines in autoproduct to stop sinsin, coscos, sincos if sumofsin
or sumofcos have been used.
1.31.25  added another clause to use reversedoublesin
2.25.25  modified autosum and autopoduct for differentiating sin(ax) from definition.
*/

#include <assert.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>  // for debug via printf
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "mpdoc.h"
#include "tdefn.h"
#include "checkarg.h" /* for operator typedef */
#include "ops.h"  /* for prototypes of operators */
#include "operator.h"
#include "trig.h"
#include "calc.h"
#include "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 "eqn.h"
#include "pvalaux.h"
#include "symbols.h"
#include "cflags.h"   /* display_on() etc. */
#include "factor.h"   /* numerical_poly   */
#include "cancel.h"
#include "autosum.h"
#include "trigpoly.h"  /* algpoly         */
#include "binders.h"   /* get_limit_info  */
#include "autosimp.h"  /* get_pathlength, get_path */
#include "automode.h"
#include "autoeqn.h"   /* noxious          */
#include "preops.h"    /* get_trigpolyflag */
#include "deval.h"     /* seminumerical    */
#include "relrates.h"  /* used             */

static int not_quadratic(term t);
static int essentially_linear(term);
static int cfc(void);
static int fc(void);
static int no_factor_functor(unsigned short f);
static int quadratic_in(term t, term x);
static int tanflag(term);
static int comdenom_conditions(int problemtype, term t);
static int stop_factorundersqrt(term t);
static int stop_factorunderroot(term t);
static int all_trig(term t);
static int trig_in_denom(term t);
static int stop_complexnegexp(term t);
static int reversesinsq_conditions(term t);
static int different_trig_args(term t);
/*______________________________________________________________*/
static int hintflag;

void set_hintflag(int n)
{ hintflag = n;
}
/*______________________________________________________________*/

#define HIEVENPOWER(u) (FUNCTOR(u) == '^' && INTEGERP(ARG(1,u)) && ISEVEN(ARG(1,u)) && INTDATA(ARG(1,u)) > 2 && TRIGFUNCTOR(FUNCTOR(ARG(0,u))))
/*_______________________________________________________________*/
void  autosum(term t,actualop *o, int *nops)
/*  post_ops for the case of a sum t */
/* the trig (and arctrig) functors are numbered 1-12; we set the k-th bit of
trigflag (or k-1st, if you number from 0)
when we meet the k-th trig functor as a summand
OR A FACTOR OF A SUMMAND. Then later we use the following macro to
determine which functors were encountered: */
#define INDICATES(trigflag,f)  ((trigflag) & (1 << TRIGINDEX(f)))

/*  Similarly, trigsqflag keeps track of the occurrences of squares of
the trig functors.  */

/* The 13-th bit (bit 12, since they start at 0)
of trigflag is set if we encounter a non-atomic argument
of a trig function. */

{ int i=0,j,k;
  unsigned short n = ARITY(t);
  unsigned short f = FUNCTOR(t);
  int atantan2flag =0;
  const int trigexpandflag= get_trigexpandflag();
  const  int comdenomflag = get_comdenomflag();
  const int logcollectflag = get_logcollectflag();
  const int factorflag = get_factorflag();
  const int problemtype = get_problemtype();
  const int topic = get_currenttopic();
  const int intflag = get_intflag();
  const  int indexflag = get_indexsumflag();
  const int seriesflag = get_seriesflag();
  const int indenomflag = get_indenomflag();
  const int innumflag = get_innumflag();
  const int limitflag = get_limitflag();
  const int inpowerflag = get_inpowerflag();
  const int insqrtflag = get_insqrtflag();
  const int inrootflag = get_inrootflag();
  const int infractexpflag = get_infractexpflag();
  int pathlength = get_pathlength();
  const int expandflag = get_expandflag();
  const int difflag = get_difflag();
  int contentfactorflag = 0;  /* don't try contentfactor more than once */
  unsigned short const *path = get_path();
  unsigned short m,g,h;
  int trigpolyflag;
  term summand,temp;
  int stopfactor = 0;
  int fractionflag,varflag,constflag,trigflag,localexpandflag,minusflag,
     logflag,infinityflag,trigsqflag,localintflag,iflag,cofiflag,sincosflag;
  infinityflag=fractionflag=varflag=constflag=localintflag=minusflag=
     trigflag=localexpandflag=logflag=trigsqflag=iflag=cofiflag=sincosflag=0;
  assert(f=='+');
  if(problemtype == LINEAR_EQUATIONS &&
     pathlength <= 6 &&
     path[0] == AND &&
     path[2] == '=' &&  path[3] == 1 &&
     LINEUP(history(get_currentline()))
    )
     /* Once variables are lined up, don't do anything to the sums
        on the left of = in systems of linear equations.  For
        example, don't change  -x + y  to y-x  */
     { *nops = i;
       return;
     }
  if(get_whichpass() == 5 && seminumerical(t) && contains_double(t))
     { o[i] = devalop; ++i;
       *nops = i;
       return;
     }
  for(j=0;j<n;j++)
     { temp = ARG(j,t);
       g = FUNCTOR(temp);
       if(g == CONSTANTOFINTEGRATION)
          { atantan2flag = atantan2flag == 1 ? 3 : 2;
            ++cofiflag;
          }
       if(ATOMIC(temp))
          { if(equals(infinity,temp))
               infinityflag = 1;
            if(get_complex() && FUNCTOR(temp) == 'i')
               ++iflag;  /* count occurrences of complexi */
            continue;
          }
       if(g=='-')
          { summand = ARG(0,temp);
            ++minusflag;
            g = FUNCTOR(summand);
            if(FUNCTOR(temp) == CONSTANTOFINTEGRATION)
               atantan2flag = atantan2flag == 1 ? 3 : 2;
            if(ATOMIC(summand))
               { if(equals(infinity,summand))
                   infinityflag = 1;
                 if(get_complex() && FUNCTOR(summand) == 'i')
                    ++iflag;
                 continue;
               }
          }
       else
          summand = temp;
       if(get_problemtype() == IMPLICIT_DIFF)
          { if(contains(summand,DIFF))
               ++varflag;
            else
               ++constflag;
          }
       else if(SOLVETYPE(problemtype))
          { if(contains(summand,FUNCTOR(get_eigenvariable())))
               ++varflag;
            else
               ++constflag;
          }
       else
          { if(constant(summand))
               ++constflag;
            else
               ++varflag;
          }
       if(g==INTEGRAL)
          ++localintflag;
       if(g == '*' && contains_monomially(summand,INTEGRAL))
          ++localintflag;
       if(g == '*')
          atantan2flag = tanflag(summand) | atantan2flag;
       if(g == '/')
          fractionflag = 1;
       if(trigfunctor(g))
          { trigflag  |= (1<<TRIGINDEX(g));  /* see comments above function */
            if(!ATOMIC(ARG(0,summand)) && TRIGFUNCTOR(g))
                /* don't set this bit for arctrig functions of non-atomic args */
                trigflag |= 0x1000;  /* set bit 12 */
          }
       if(g == '^'  && trigfunctor(FUNCTOR(ARG(0,summand))))
          { trigflag |= (1<<TRIGINDEX(FUNCTOR(ARG(0,summand))));
            if(equals(ARG(1,summand),two))
               trigsqflag |= (1<<TRIGINDEX(FUNCTOR(ARG(0,summand))));
            if(!ATOMIC(ARG(0,ARG(0,summand))) &&
               TRIGFUNCTOR(FUNCTOR(ARG(0,summand)))
               /* don't set this bit for arctrig functions of non-atomic args */
              )
               trigflag |= 0x1000;
          }

        /* logflag uses 2 bits each to 'count'
           ln, log, logb crudely, setting the second
           bit when a second occurrence is encountered.
           We collect both constant and nonconstant logs,
           for example to solve log 2 + log x = 2 => log 2x = 1.
           On the other hand consider 3^(log x) = 3x =>
           10^(log3 log x) = 3x => log 3 log x = log(3x) =>
           log 3 log x - log(3x) = 0 which can't be finished
           without expanding log(3x).
        */
       if(g == LN)
          logflag |= (logflag & 1 ? 2 : 1);
       if(g == LOG)
          logflag |= (logflag & 4 ? 8 : 4);
       if(g == LOGB)
          logflag |= (logflag & 16 ? 32 : 16);
       if(g == ATAN)
          { if(FUNCTOR(ARG(0,summand)) == TAN)
               atantan2flag = atantan2flag == 2 ? 3 : 1;
               /* we set bit 0 of this flag when atan(tan(x)) is
                  encountered and bit 1 when an indefinite integral
                  or constant of integration is encountered */
          }
       if(g == INTEGRAL && ARITY(summand) == 2)
          { atantan2flag = atantan2flag == 1 ? 3 : 2;
          }
       if(g == '*' && ARITY(summand) == 2)
          { term a = ARG(0,summand);
            term b = ARG(1,summand);
            unsigned short fa = FUNCTOR(a);
            unsigned short fb = FUNCTOR(b);
            if(
               (fa == SIN || fa == COS) &&
               (fb == SIN || fb == COS) &&
               !contains_arctrigs(a) &&
               !contains_arctrigs(b)
              )
               ++sincosflag;
          }
       if(g == '*') /* is there a product whose factors include a sum ? */
                    /* or whose factors include an integral power of a sum? */
                    /* or a log, ln, or logb ? */
          { m = ARITY(summand);
            for(k=0;k<m;k++)
               { temp = ARG(k,summand);
                 h = FUNCTOR(temp);
                 if(
                     ( h=='+' ||
                       (h == '^' && FUNCTOR(ARG(0,temp))=='+' &&
                        ISINTEGER(ARG(1,temp))
                       )
                     )
                     &&
                     ( !comdenomflag || indenomflag ||
                       innumflag || !fractionflag
                     )
                   )
                  /* don't expand outside fractions in commondenom problems;
                     example:  n(n+1) + 1/(n+1) + 1/n ;
                     but:  we DO expand in fractions, as in
                         (x(x+1) +x )/((x-1)(x-2))
                     even if this is in a common denom problem
                     and we DO expand when there are NO fractions as in
                         2a^2 - (a-3)(2a+1)
                  */

                    { ++localexpandflag;
                      break;
                    }
                 if(ISATOM(temp) && h == 'i' && get_complex())
                    ++iflag;
                 if(trigfunctor(h))
                    { trigflag  |= (1<<TRIGINDEX(h));  /* see comments above function */
                      if(!ATOMIC(ARG(0,temp)))
                         trigflag |= 0x1000;  /* set bit 12 */
                    }
                 if(h == '^' && trigfunctor(FUNCTOR(ARG(0,temp))))
                    { unsigned short hh = FUNCTOR(ARG(0,temp));
                      trigflag |= (1<<TRIGINDEX(hh));
                      if(equals(ARG(1,temp),two))
                         trigsqflag |= (1<<TRIGINDEX(hh));;
                    }
               }
          }
       if(g == '^' && FUNCTOR(ARG(0,summand))=='+' && INTEGERP(ARG(1,summand)) && !difflag)
          ++localexpandflag;
     }
  if(constflag && varflag==1 && !get_infractionflag() && SOLVETYPE(problemtype))
     fractionflag = 0;  /* don't use common denoms then */
                /* but do use commondenom or even addfractions
                 on either all-constant terms or all-variable terms;
                 and do use them inside compound fractions.  The aim is
                 to not use common denominators when solving
                  (1/2) x + 3 = 6;  we will transfer 3 first.  */
  if(infinityflag)
     { o[i] = addinfinity; ++i;
     }
  if(cofiflag && !localintflag && constflag)
     { o[i] = absorbconstant; ++i;
     }
  if(atantan2flag == 3)
     { o[i] = atantan2; ++i;
     }
  if(iflag == 1 && topic != _complex_arithmetic && topic != _complex_cubics && 
     (
       ( pathlength >= 3 &&
         path[pathlength-3] == '^' &&
         path[pathlength-2] == 1  /* we are in the base of an exponent */
       )
       ||
       ( pathlength >= 3 &&
         path[pathlength-3] == LN
       )
       ||
       ( topic == _polar_form &&
         (
          pathlength <= 1 ||
          (pathlength == 3 && path[0] == '*')
         )
       )
       /* if you don't put some restrictions on the path you'll get an infinite
          regress on i = e^(i pi/2) = e^(pi/2 e^(i pi/2)) = ... as
          the i in the exponent is continually replaced by e^(i pi/2) */
     )
    )
     { o[i] = rectangulartopolar; ++i;
     }
  if(topic != _factor_by_grouping || get_currentline() > 2)
     {/* In factor by grouping, don't collect right away but give
         content_factor a chance first */
       if(get_complex() &&
          (
            pathlength <= 1 ||
            (pathlength == 3 && path[0] == '=') ||
            (pathlength == 5 && path[0] == OR && path[2] == '=')
          )
         )
          { if(problemtype == SOLVE_EQUATION && contains(t,FUNCTOR(get_eigenvariable())) && status(writeaspoly) > LEARNING)
               /* example,  ix^2 + x + i,  don't write it as x + (x^2 + 1)i  */
               { o[i] = writeaspoly; ++i;
               }
            else  /* after solving, when simplifying the right side */
               { o[i] = complexform; ++i;  /* preempt collectall and orderterms */
               }
          }
       if(status(collectall) >= KNOWN)
          { o[i] = collectall; ++i;
          }
       else
          { o[i] = collectterms; ++i;
          }
     }
  if(fractionflag &&
     (!limitflag || (pathlength >= 3 && path[pathlength-3] != LIMIT)) &&
     /* don't add fractions when limsum could be used.  If the
        individual limits are indeterminate, then eventually
        commondenom will be used to add them.
     */
      ! get_seriesflag() &&  // don't use addfractions inside infinite series
      (
       !iscomplex(t) || n > 2 ||
       (iscomplex(ARG(1,t)) && iscomplex(ARG(0,t))) ||
       (FRACTION(ARG(0,t)) && iscomplex(ARG(1,ARG(0,t)))) ||
       (NEGATIVE(ARG(0,t)) && FRACTION(ARG(0,ARG(0,t))) && iscomplex(ARG(1,ARG(0,ARG(0,t)))))
       /* don't add fractions in sqrt(3)/2^(3/4) + i/2^(3/4)  */
      )
    )
     { o[i] = addfractions; ++i;
     }
  if(limitflag || inpowerflag || logcollectflag || SOLVETYPE(problemtype))

    /* when autosum is called from ssolve, we have SOLVETYPE(problemtype)
       temporarily, and logcollectflag can be zero, because it has not
       been changed along with problemtype */

     { if(logflag & 2) /* two or more lns */
          { o[i] = minusflag ? collectlns2 : collectlns; ++i;
            if(n > 2 && minusflag)
               { o[i] = collectlns; ++i;
                 /* e.g.  ln x + ln y - log z;  collectlns2 only works
                    on ln x - ln y  */
               }
          }
       if(logflag & 8) /* two or more logs */
          { o[i] = minusflag ? collectlogs2 : collectlogs; ++i;
            if(n > 2 && minusflag)
               { o[i] = collectlogs; ++i;
               }
          }
       if(logflag & 32)  /* two or more logb's */
          { o[i] = minusflag? collectlogb2 : collectlogb; ++i;
            if(n > 2 && minusflag)
               { o[i] = collectlogb; ++i;
               }
          }
     }
  trigpolyflag = get_trigpolyflag();
  if(trigflag &&
     trigpolyflag != -1  &&   /* this means it's an algebraic identity */
     (
      (
       !(trigexpandflag & 2) && /* only one trig function occurs in
                                  the entire problem. */
       !(trigexpandflag & 1)    /* all the trig functions have the same args,
                                   or all have atomic args */
      ) ||
          /* If both these conditions hold it's
             a purely algebraic problem, so don't do any trig,
             UNLESS
               we're doing an integral; or
               we're at toplevel, or in a fraction or power at toplevel etc;
               or the parent functor is a SQRT, and arity is 2.
               This will still make us miss f(1-sin^2) for complicated f.
          */
      intflag ||   /* but not || indexflag as there's no use
                    doing trig manipulations in indexed sums */
      pathlength == 1 ||
      ( pathlength == 3 && path[0] == '/') ||  /* example:  (1-sin^2 t)/sin t  */
      ( pathlength == 5 && path[0] == '-' && path[2] == '/') ||
      ( pathlength == 3 && path[0] == '^' && path[1] == 1) || /* (1-sin^2 t)^5  */
      ( pathlength == 5 && path[0] == '/' && path[3] == '^' && path[4] == 1) ||
      ( pathlength >= 3 && path[pathlength-3] == SQRT && n == 2)
     )
    )
     { if(INDICATES(trigsqflag,SIN) && INDICATES(trigsqflag,COS))
          {  /* sin^2 + cos^2 = 1 is in pre_ops, so it gets used before
                double angle formulas in sin^2 2x + cos^2 2x + sin x, and
                before half-angle formulas in integral((sin^2 x + cos^2 x),x).
                   1 - sin^2 u = cos^2 u   and 1 - cos^2 u = sin^2 u
                are in pre_ops, so they get used before factoring,
                but only for arity 2, so they must appear here for larger
                arities when required.
             */
            if(ARITY(t) == 2 && all_trig(t))
               { o[i] = reversedoublecos1; ++i;  /* cos^2 x - sin^2 x = cos 2x */
               }
            if(problemtype == TRIG_IDENTITY || FACTORFLAG || ARITY(t)==2)
               { if(trigpolyflag != COS)
                    { o[i] = sinsquare2; ++i;  /* 1-sin^2 = cos^2 */
                    }
                 /* We use sinsqtocosq unless trigpolyflag says we're
                 trying to get rid of sin. But then we're not so liberal
                 about using cossqtosinsq; we use it only if trigpolyflag
                 says we ARE trying to get rid of cos.  The intention is
                 to prevent loops or near-loops using these two operations.
                 */

                 if(trigpolyflag == COS)
                    { o[i] = cossqtosinsq; ++i;
                    }
                    /* rewrite cos^2 as 1-sin^2           */
                    /* example:  sin^2x - cos^2x = 0      */
                    /* or:   sin^2x - cos^2x = 1 - 2cos^2x */
                    /* but watch out for sin^4 x + 2 sin^2x cos^2x + cos^4 x,
                       which should be factored instead.  So, on the first
                       pass when factoring is off, don't use this operator
                       on sums with 3 or more terms.  You do HAVE to use
                       it on sums of arity 2 BEFORE they get factored,
                       as the above examples show.  */
               }
          }
       else if(INDICATES(trigsqflag,SIN)  /* and cos^2 isn't present */
                && (!SOLVETYPE(problemtype)
                         /* when solving trig equations, don't rewrite
                       sin^2 as cos^2 if sin is the only trig function present */
                    || ((trigflag & ~(1 << TRIGINDEX(SIN))) & 0x0fff) != 0
                     /* something besides SIN is present */
                   )

              )
               { if(trigpolyflag != COS)
                    { o[i] = sinsquare2; ++i;  /* 1-sin^2 = cos^2 */
                    }
                 if(TRIGCONTAINS(trigflag,COS) && trigpolyflag == SIN)

               /* example:  2 sin^2 x + 3 cos x - 3, the
                  sin^2 should be written as 1-cos^2 so the expression
                  becomes a polynomial in cos.  */

                    { o[i] = sinsqtocossq; ++i;
                      o[i] = sinsquare2; ++i;
                    }
               }
       else if(INDICATES(trigsqflag,COS)  /* and sin^2 isn't present */
               && ( !SOLVETYPE(problemtype)
                       /* when solving trig equations, don't rewrite
                       cos^2 as sin^2 if cos is the only trig function present */
                     || ((trigflag & ~(1 << TRIGINDEX(COS))) & 0x0fff) != 0
                        /* something besides COS is present */
                  )
              )
               { if(trigpolyflag != SIN)
                    { o[i] = sinsquare3; ++i;  /* 1-cos^2 = sin^2 */
                    }
                 if(TRIGCONTAINS(trigflag,SIN) && trigpolyflag == COS)
                   /* rewrite cos^2 as sin^2-1 so it becomes a
                      polynomial in sin
                   */
                    { o[i] = cossqtosinsq; ++i;
                      o[i] = sinsquare3; ++i;
                    }
               }
       if(ARITY(t) == 2 && INDICATES(trigsqflag,SIN) && !INDICATES(trigsqflag,COS) && all_trig(t))
          { o[i] = reversedoublecos2; ++i;  /* 1 - 2 sin^2 x = cos 2x */
          }
       if(ARITY(t) == 2 && INDICATES(trigsqflag,COS) && !INDICATES(trigsqflag,SIN) && all_trig(t))
          { o[i] = reversedoublecos3; ++i;
          }
       if(INDICATES(trigsqflag,TAN) && ARITY(t) == 2 && pathlength == 1)
          {  o[i] = tansquare1; ++i;      /* tan^2 u + 1 = sec^2 u */
             /* finish the derivative of tan x from defn, for example;
                but if you ALWAYS do this, you'll screw up polynomials
                in tan x  */
          }
       if(INDICATES(trigsqflag,TAN) && INDICATES(trigsqflag,SEC))
          { o[i] = secsqminustansq; ++i; /* sec^2 u - tan^2 u = 1 */
            if(trigpolyflag != SEC)
               { o[i] = tansquare1; ++i;      /* tan^2 u + 1 = sec^2 u */
               }
            if(trigpolyflag != TAN)
               { o[i] = tansquare2; ++i;      /* sec^2 u - 1 = tan^2 u */
               }
            if(trigexpandflag & 0x0010)  // bit 4 is set 
               { o[i] = secsqtotansq; ++i;    /* sec^2 u = tan^2 u + 1 */
               }
          }
       else if(INDICATES(trigsqflag,TAN) && /* and sec^2 isn't present */
               (!SOLVETYPE(problemtype)
                         /* when solving trig equations, don't rewrite
                            tan^2 as sec^2 if tan is the only trig function present */
                 || ((trigflag & ~(1 << TRIGINDEX(TAN))) & 0x0fff) != 0
                      /* something besides TAN is present */
               ) &&
               trigpolyflag != SEC
              )
          { o[i] = tansquare1; ++i;
          }
       else if(INDICATES(trigsqflag,SEC) && /* and tan^2 isn't present */
               (!SOLVETYPE(problemtype)
                    /* when solving trig equations, don't rewrite
                       sec^2 as tan^2 if sec is the only trig function present */
                 || ((trigflag & ~(1 << TRIGINDEX(SEC))) & 0x0fff) != 0
                    /* something besides SEC is present */
               ) &&
                trigpolyflag != TAN
              )
          { o[i] = tansquare2; ++i;   /* sec^2 u - 1 = tan^2 u */
            if(trigpolyflag == SEC)
               { o[i] = secsqtotansq; ++i; /* sec^2 u = tan^2 u + 1 */
               }
          }
       if(INDICATES(trigsqflag,CSC) && trigpolyflag == CSC)
          { o[i] = cotsquare2; ++i;   /* csc^2 u - 1 = cot^2 u */
            if(trigpolyflag == CSC)
               { o[i] = cscsqtocotsq; ++i; /* csc^2 u = cot^2 u + 1 */
               }
          }
       if(INDICATES(trigsqflag,COT) && trigpolyflag == COT)
          { o[i] = cotsquare1; ++i;  /* cot^2 u + 1 = csc^2 u */
          }
       if(INDICATES(trigsqflag,COT) && INDICATES(trigsqflag,CSC))
          { o[i] = cscsqminuscotsq; ++i; /* csc^2 u - cot^2 u = 1 */
            if(trigpolyflag != CSC)
               { o[i] = cotsquare1; ++i;      /* cot^2 u + 1 = csc^2 u */
               }
            if(trigpolyflag != COT)
               { o[i] = cotsquare2; ++i;      /* csc^2 u - 1 = cot^2 u */
               }
          }
       else if(INDICATES(trigsqflag,COT) && /* and csc^2 isn't present */
               SOLVETYPE(problemtype) &&
                    /* when solving trig equations, don't rewrite
                       cot^2 as csc^2 if cot is the only trig function present */
               ((trigflag & ~(1 << TRIGINDEX(COT))) & 0x0fff) != 0  &&
                       /* something besides COT is present */
               trigpolyflag != CSC
              )
          { o[i] = cotsquare1; ++i;
            if(trigpolyflag == COT)
               { o[i] = cotsqtocscsq; ++i;  /* cot^2 u = csc^2 u - 1 */
               }
          }
       else if(INDICATES(trigsqflag,CSC) && /* and cot^2 isn't present */
               (!SOLVETYPE(problemtype)
                      /* when solving trig equations, don't rewrite
                       csc^2 as cot^2 if csc is the only trig function present */
                || ((trigflag & ~(1 << TRIGINDEX(CSC))) & 0x0fff) != 0
                       /* something besides CSC is present */
               ) &&
               trigpolyflag != COT
              )
          { o[i] = cotsquare2; ++i;   /* csc^2 u - 1 = cot^2 u */
            if(trigpolyflag == CSC)
               { o[i] = cscsqtocotsq; ++i; /* csc^2 u = cot^2 u + 1 */
               }
          }


       /* A couple of ad-hoc rules about factoring certain trig identities
          which could also be done by expanding:  */

       if( n==3 && (trigexpandflag & 8))
          /* only even powers of trig functions appear */
          { o[i] = factorsquareofdif; ++i;   /* sin^4 + 2sin^2cos^2 + cos^4 */
            o[i] = factorsquareofsum; ++i;
          }
       if( n== 2 && (trigexpandflag & 8))   /* sin^4 - cos^4 */
          /* only even powers of trig functions appear */
          { term u = ARG(0,t);
            term v = ARG(1,t);
            int sign = 0;
            if(NEGATIVE(u))
                { u = ARG(0,u);
                  sign = 1;
                }
            if(NEGATIVE(v))
                { v = ARG(0,v);
                  sign = sign ? 0 : 1;
                }
            if(sign &&
               (HIEVENPOWER(u)  || OBJECT(u)) &&
               (HIEVENPOWER(v) || OBJECT(v))
              )
               { o[i] = differenceofsquares; ++i;
               }
          }
       if(
          (trigexpandflag & 8) && !((trigexpandflag & 2) && !(trigexpandflag & 1)) &&
          /* only even powers of trig functions appear */
          /* and not only one function                 */

          /* Given a polynomial f(sin^2 x, cos^2 x), we
             need to make it a polynomial of sin^2 x.
             But, we don't want to use cossqtosinsq on
             sin^2 t/cos^22 = tan^2 t !
             Also, it looks awkward in 1-sin^2 x = cos^2 x
             to rewrite the right side instead of the left.
             And it looks worse in sin^2 x = 1 - cos^2 x to
             write it 1-(1-sin^2 x).  However,
             you don't want to use 1-sin^2x => cos x indiscriminately
             in pre_ops, because if in a fraction you may need
             to factor and cancel.  But if NOT in a fraction,
             I can't see why wouldn't want to do this in
             pre_ops, so it would never get here.  */

          !get_infractionflag()  &&
             /* that ought to take care of it.  Rational functions
                in sin^2 and cos^2 will eventually get
                cross-multiplied.  */
          !trig_in_denom(t)  /* in cos^2 x / sin^2 x - 2 cos^2 x,
                                don't change cos^2 to 1- sin^2, first
                                use common denoms */
         )
          { if(INDICATES(trigflag,COS) && trigpolyflag != SIN)
               { o[i] = cossqtosinsq; ++i;
               }
            if(INDICATES(trigflag,CSC) && trigpolyflag != COT)
               { o[i] = cscsqtocotsq; ++i;
               }
            if(INDICATES(trigflag,SEC) && trigpolyflag != TAN)
               { o[i] = secsqtotansq; ++i;
               }
          }
     }

  if(pathlength >= 3 && path[pathlength-3] == SQRT)
     stopfactor = stop_factorundersqrt(t);  /* don't factor sqrt(2x + 4) */
  if(pathlength >= 3 && path[pathlength-3] == ROOT)
     stopfactor = stop_factorunderroot(t);  /* don't factor sqrt(2x + 4) */

  /* The following prevents  contentfactoring e^ix - e^-ix or even e^x-e^-x */
  if(!stopfactor && contains(t,'e'))
     stopfactor = stop_complexnegexp(t);
  /* The following prevents content-factoring a + ai = (1+i)a  */
  if(!stopfactor && get_complex() && complexexpression(t))
     stopfactor = 1;

  /* The following stops contentfactoring in e.g.
     (x - (1/2) - sqrt(5)/2)  as a factor on one side of an inequality
  */
  if(!stopfactor && pathlength == 5 &&
     SOLVETYPE(problemtype) &&
     INEQUALITY(path[0]) &&
     path[2] == '*' && path[4] == '+' &&
     equals(ARG(0,t),get_eigenvariable())
    )
     { for(j=1;j<ARITY(t);j++)
          { if(!econstant(ARG(j,t)))
               break;
          }
       if(j==ARITY(t))
          stopfactor = 1;
     }
  if(! SOLVETYPE(problemtype) && !limitflag && !localintflag &&
     ! EXPANDFLAG &&
     problemtype != LINEAR_EQUATIONS &&
     problemtype != ROOTS &&
     !difflag && !intflag && !indexflag && !seriesflag &&
     !contains(t,CONSTANTOFINTEGRATION) &&
     /* don't content-factor after an integral is finished */
     (localexpandflag || get_infractionflag() ||
      (!TRIG_TOPIC(topic) && !CALCULUS_TOPIC(topic)) ||
      (problemtype == TRIG_IDENTITY &&
       (
        (pathlength >= 5 && path[pathlength-5] == '=' && path[pathlength-3] == '*') ||
        (pathlength >= 3 && path[pathlength-3] == '=')
       )
      )
      /* in trig identities, content-factor in sums at toplevel or in products
         at toplevel. This eliminates useless factoring while still enabling
         us to find a common factor of both sides. */
     ) &&
     !(pathlength >= 3 && no_factor_functor(path[pathlength-3])) &&
       /* NEVER factor inside an arctrig functor */
       /* !localintflag says don't factor a sum of integrals */
     !algebraic_number(t) && !complex_number(t) &&
     !(contains(t,PI_ATOM) && is_linear_in(t,pi_term)) &&
     !stopfactor &&
     !(problemtype == POWERSERIES && ARITY(t) == 2 && (ONE(ARG(0,t)) || ONE(ARG(1,t)))) && 
     !(problemtype == INTEGRATION && contains(t,CONSTANTOFINTEGRATION)) &&
     /* it looks silly to write (1/2) a + c  = (1/2) (a + 2c)  */
     topic != _trig_product   /* on this topic we use distriblaw to reduce to a sum
                                 of trig functions */
    )
     { o[i] = contentfactor; ++i;
       contentfactorflag = 1;
     }
  else if(!stopfactor &&
          !(SOLVETYPE(problemtype) && ARITY(t) == 2 && equals(ARG(0,t),get_eigenvariable())) &&
                      /* don't factor (x-1/2) into 1/2(2x-1)  */
          (topic != _complete_the_square || inhibited(alltoleft)) &&
          !(problemtype == POWERSERIES && ARITY(t) == 2 && (ONE(ARG(0,t)) || ONE(ARG(1,t)))) &&
          (
            inhibited(alltoleft) ||   /* after completethesquare before factoring is done */
            (FACTORFLAG && !constant(t) &&
             (!(intflag || seriesflag || indexflag) || (!insqrtflag && !inrootflag && !infractexpflag)) &&
             problemtype != LINEAR_EQUATIONS
            )
          )
         )
     /* for example inside ABSFUNCTOR inside an integral; but don't
     factor under sqrts or roots in integrals, because it blocks
     completethesquare1. */
     { o[i] = contentfactor; ++i;
       contentfactorflag = 1;
     }
  else if(limitflag &&
          !(pathlength >= 3 && no_factor_functor(path[pathlength-3])) &&
          ! TRIGFUNCTOR(path[pathlength-3])
          // don't factor in sin(ax+ah)) inside a LIMIT term
          // needed for differentiating sin(ax) from definition.
         )
     { o[i] = factoroutconstant; ++i;
     }
  else if(intflag &&
          (path[pathlength-3] == INTEGRAL ||
           (pathlength >= 5 && path[pathlength-3] == '/' && path[pathlength-5] == INTEGRAL)
          )
         )
     /* example, integral((-2yu + y + yu^2)/((u-1)(u+1)),u);
        we need to factor out 'y' from the numerator and then
        from the integral, after which the rest of the numerator
        will factor and cancel. */
     { o[i] = factoroutconstant; ++i;
     }
  if(fractionflag &&
     ((!difflag && !intflag && !seriesflag && !indexflag) || (get_infractionflag() && fc()))
    )
      /* It's easier to differentiate or integrate the sum of fractions;
         but a compound fraction, like x/(x+2/x), should be simplified
         first */
     { if( !algebraic_number(t) &&
           (!get_complex() || !complexexpression(t)) &&
           (
             (comdenomflag && !(stopfactor && path[pathlength-3] == '*')) ||
             comdenom_conditions(problemtype,t)
           )
         )
         { o[i] = factordenominator; ++i;
           if( /* t is left side of equation with right side 0 */
              (
               ( pathlength >= 3 && path[0] == '=' && path[1] == 1 &&
                 path[2] == '+' && path[3] == 0 &&
                 ZERO(ARG(1,history(get_currentline())))
               ) ||
               ( pathlength >= 5 && path[0] == OR && path[2] == '=' && path[3] == 1 &&
                 path[4] == '+' && path[5] == 0 &&
                 ZERO(ARG(1,ARG(path[1]-1,history(get_currentline()))))
               )
              )
               &&
              !contains(t,DIFF)
              &&
              !immediate_comdenom(t)
             )
              { /* example, (x-1)(x+1)/(3x) - 3x/((x-1)(x+1)) + 3/2 = 0 */
                   set_substitutionflag(VISIBLESUBS);
                   o[i] = makesubstitution; ++i;
              }
           if(topic == _complete_the_square &&  /* t is the left side of the equation */
              ( pathlength >= 3 && path[0] == '=' && path[1] == 1 &&
                path[2] == '+' && path[3] == 0
              )
             )
               ;  /* don't use common denoms */
           else if(status(commondenomandsimp) >= KNOWN)
              { o[i] = commondenomandsimp; ++i;
              }
           else if(status(commondenom) >= KNOWN)
              { o[i] = commondenom; ++i;
              }
           else
              { o[i] = findcommondenom; ++i;
              }
         }
     }
  if(!localexpandflag &&  /* but otherwise wait till after we expand */
     !localintflag  /* don't reorder till all the integration is done. */
               /* If you can't finish the integration it really doesn't
                  matter in what order you leave the terms anyway. */
    )
     { if( 4 <= n && n <= 6 &&
          (topic == _factor_by_grouping || topic == _advanced_factoring)
         )
          { o[i] = collectall; ++i;
            o[i] = factorbygrouping; ++i;
            /* try it before orderterms, because it looks silly to
               order the terms when they're already grouped for
               factorbygrouping */
          }
       if(!stop_orderterms(t))
          { o[i] = orderterms; ++i;
          }
     }
  if( topic == _cubic_one_root ||
      topic == _complex_cubics
     )
     { o[i] = factorbypolydiv; ++i;
     }
  if(problemtype == POWERSERIES && path[pathlength-5] == SUM && path[pathlength-4] ==1 && path[pathlength-3] == '*' && path[pathlength-2] == 1)
     { o[i] = cleardenoms; ++i;  // must factor out a rational constant before seriesevenodd will kick in 
     } 
  if( FACTORFLAG && !constant(t) &&
      !CANTFACTOR(t) && !IRREDUCIBLE(t) &&
      !stopfactor &&
      !suppress_factoring(problemtype,1) &&
      topic != _cubic_one_root &&
      topic != _complex_cubics
    )
    /* The part about FACTORFLAG causes numerators
       and denominators to be factored, but not if they
       are in COMPOUND fractions. */

    /* The part about suppress_factoring prevents factoring sums which
       occur in sums, as 3x - 2(x^2-1)  */

     {  /* Note: if these are changed, change factorops in factor.c too */
       o[i] = factoroutnumber; ++i;
       /* don't clear denoms in a sum of the form (x - a) which occurs in a
          product on one side of an inequality or equation */
       if(topic != _complete_the_square &&
          !(pathlength >= 5 && path[pathlength-3] == '*' && INEQUALITY(path[pathlength-5])) &&
          !(problemtype == POWERSERIES && ARITY(t) == 2 && (ONE(ARG(0,t)) || ONE(ARG(1,t))))
         )
          { o[i] = cleardenoms; ++i;
          }
       if(!contentfactorflag &&
          (SOLVETYPE(problemtype) || limitflag) &&
          !(pathlength >= 3 && trigfunctor(path[pathlength-3])) &&
          !(pathlength >= 3 && no_factor_functor(path[pathlength-3])) &&
          topic != _cubic_one_root &&
          topic != _complex_cubics &&
          (topic != _complete_the_square || pathlength > 3)
         )
          { o[i] = contentfactor; ++i;
            contentfactorflag = 1;
          }

          /*  contentfactor is otherwise included above */
       if((!SOLVETYPE(problemtype) || problemtype == INEQUALITIES) &&
           /* quadraticformula will be used on an equation,
              not on a sum, when solving equations; but for
              inequalities, we must do it here because there's
              nothing like spliteqn until we have linear factors.
           */
          problemtype != LIMITS &&
          problemtype != LHOPITAL &&
          (get_ringflag() & ALGINT)  /* don't use this when ringflag is set
                                to exclude factors involving radicals */
          && !insqrtflag && !inrootflag  &&  /* don't use it under sqrt or root */
          !infractexpflag     /* or in the base of a fractional exponent */
         )
    /* The part about LHOPITAL says not to bother factoring numerator and
       denom first when using L'Hospital's rule.  */

          { o[i] = quadraticformula; ++i;
            /* In 0 < (x-a)(x-b)(x^2 + cx + d), you should complete the
               square so that the quadratic can be recognized as positive
               and dropped.  It would have been factored by now if if
               had real roots.
            */
            if(
               (pathlength >= 3 && INEQUALITY(path[pathlength-3])) ||
               (pathlength >= 5 && INEQUALITY(path[pathlength-5]) &&
                path[pathlength-3] == '*'
               )
              )
               { o[i] = completethesquare; ++i;
                 /* if there are no real roots we should complete the square
                    so that the inequality can be reduced to true or false.
                 */
               }
          }

       if(problemtype != INDUCTION && n==2 &&
          !insqrtflag && !inrootflag && !infractexpflag
          //  && !(trigexpandflag & 8)  /* don't factor cos^4 x - sin^4 x */
          /*  No longer necessary as differenceofsquares should already have
              taken care of this example above, and (trigexpandflag & 8) is
              nonzero even on pure polynomial input which we DO want to factor.*/
         )
          { term a = ARG(0,t);
            term b = ARG(1,t);
            if(NEGATIVE(a))
               a = ARG(0,a);
            if(NEGATIVE(b))
               b = ARG(0,b);
            if(
               problemtype != POWERSERIES && 
               !(FUNCTOR(a) == '^' && (equals(ARG(1,a),two) || equals(ARG(1,b),three))) &&
               !(FUNCTOR(a) == '^' && (equals(ARG(1,b),two) || equals(ARG(1,b),three))) &&
               /* Don't use it on a sum or difference of squares or cubes,
                    that's already been tried (or blocked on purpose) in pre_ops */
               !(pathlength >= 5 && path[pathlength-3] == '/' && path[pathlength-5] == '+')
               /* Don't use it in a fraction which is part of a sum, because common denoms
                  will be used.  If it actually will cancel after factoring,
                  then cancelgcd will factor it.   Example:  v + (v^5-1)/v^4 = 0
               */
              )
               { o[i] = differenceofnthpowers; ++i;
                 o[i] = differenceofnth2; ++i;
                 /* this creates sigma terms and screws up induction proofs, that's
                    why we don't use it on problemtype INDUCTION */
                 o[i] = sumofnthpowers; ++i;
               }
          }
       if( topic != _simple_factoring &&
           topic != _trig_product &&
           // !SOLVETYPE(problemtype) && /* includes IMPLICIT_DIFF */
           (!comdenomflag || !get_infractionflag())
           && !insqrtflag && !inrootflag && !infractexpflag
           && !essentially_linear(t)
         )
          /* while doing common denoms, cancelgcd will find any useful
             factors; however it looks better to use the above ops instead.
             But these time-consuming ops should not be used in fractions.
             For example, 5x^3 - 12y^3  takes a long time to fail to factor,
             and it's better to use cancelgcd.  But, if NOT in a fraction
             and with factoring turned on, we have to put up with the wait.
          */
          { int subflag = get_substitutionflag();
            if(subflag == VISIBLESUBS && pathlength <=1 &&
               !contains_bound_vars(t)  /* don't wind up with d/d(u^2) for example */
              )
               { o[i] = makesubstitution; ++i;
               }
            else if(problemtype != SIMPLIFY && subflag != VISIBLESUBS)
               { o[i] = invisiblesub; ++i;
               }
            o[i] = factorquartic; ++i;   /* before factorbypolydiv */
            if(localexpandflag == n)
               { /* every term contained a sum; these sums might have a common
                  factor, as in  2(x^2-1) + 3(x^2+2x+1)  = 2(x-1)(x+1) + 3(x+1)^2
                 = (x+1)(2(x-1) + 3(x+1))  */
                 o[i] = contentgcd; ++i;
               }
            if( (inq_display_on() || hintflag)
             /* don't use this in think_ahead, because it
             takes a long time and you wouldn't see any progress display;
             if hintflag is on, though, user has pressed 'h' so we
             do need to use it */
                && problemtype != POWERSERIES
                && (not_quadratic(t))   /* don't use on quadratics; if it
                                       could work, factorquadratic would too */
                &&  !(pathlength >= 3 && path[pathlength-2] == '='  && NEGATIVE(ARG(0,t)))
                   /* don't work on  -ax^3... = 0 because changesigns will
                      be applied next and cause a duplication of effort.
                      Just let the sign be changed first. */

              )
               { o[i] = factorbypolydiv; ++i; /* brother of guesslinearfactor */
               }
          }
       else if(n==3)
          { o[i] = factorquartic; ++i; }
          /* because of x^4 - 2x^2y^2 + y^4, we put factorquartic
             AFTER invisiblesub, so this gets done as (x^2-y^2)^2 */
       if(n==4 || n==5 || n == 6)
          { o[i] = collectterms; ++i;
            o[i] = factorbygrouping; ++i;
          }
       if(n==3)
          { /* a(b+c) + ab + ac needs factorbygrouping */
            int j;
            term w;
            for(j=0;j<3;j++)
               { w = ARG(j,t);
                 if(FUNCTOR(w) == '*' && contains(w,'+'))
                     { o[i] = factorbygrouping; ++i;
                       break;
                     }
               }
          }
       if( topic != _simple_factoring)
          { o[i] = squarefreefactors; ++i;
          }
       if( numerical_poly(t) &&
           contains(t,'^') &&  /* don't try this on linear polynomials */
           SOLVETYPE(problemtype) &&
            /* don't supersede quadraticformula,
               which is used on equations,
               and so hasn't yet been tried. */
           !quadratic_in(t,get_eigenvariable()) &&
           ! (path[0] == '=' && path[2] == '+' && NEGATIVE(ARG(0,t))) &&
            /* don't work on  -ax^3... = 0 because changesigns will
               be applied next and cause a duplication of effort.
               Just let the sign be changed first. */
           ! (path[0] == OR && path[2] == '=' && path[4] == '+' && NEGATIVE(ARG(0,t))) &&
           topic != _complex_cubics &&
           topic != _cubic_one_root &&
           pathlength >= 3 && INEQUALITY(path[pathlength-1])
           /* don't do it in the numerators of fractions, for instance */
         )
          { o[i] = factornumerically; ++i;  /* as a last resort */
          }
     }
  if(INDICATES(trigflag,COS) &&
     reversesinsq_conditions(t)
    )
     { o[i] = reversesinsq; ++i;
     }
  if(INDICATES(trigflag,ATAN))
     { o[i] = sumofarctan; ++i;
     }
  if(INDICATES(trigflag,ASIN) && INDICATES(trigflag,ACOS))
     { o[i] = sumofarcsin; ++i;
     }

  if(localexpandflag && 
     !contains(t,CONSTANTOFINTEGRATION) &&
     (!get_complex() || !iscomplex(t))  /* example, x^2 + (1+i) x + 3   should not be expanded  */
    )
     { o[i] = expand; ++i;
       if(!stop_orderterms(t))  
          { o[i] = orderterms; ++i;  /* postponed above */
          }
     }
  if(pathlength >= 3 && path[pathlength-3] == SQRT)
     { o[i] = factorsquareofsum; ++i;
       o[i] = factorsquareofdif; ++i;
       /* example:  sqrt(36a^4 - 12a^2b^2 + b^4)    */
       
     }
  if(pathlength >= 3  && 
     ( path[pathlength-3] == SQRT || path[pathlength-3] == ROOT) && 
     is_complex(t) &&
     topic != _complex_cubics  // where it makes solutions too long and complicated
    )
      { o[i] = rectangulartopolar; ++i;  /* need polar form to evaluate sqrt(3-4i)  */
      }
        
  if(n > 2 && SOLVETYPE(problemtype) && status(writeaspoly) > LEARNING)
     { o[i] = writeaspoly; ++i;
       /* writeaspoly chooses the term or variable we are solving
          for and writes the sum as a polynomial in that term */
     }
  *nops = i;
}

/*__________________________________________________________________*/
void autoproduct(term t,actualop *o, int *nops)
/* do the work of post_ops for a product term */
{ int i=0,j;
  unsigned short n = ARITY(t);
  unsigned short g,h;
  int coshsinhflag = 0;
  term coshsinharg = zero;  /* must be initialized to avoid a warning */
  const int intflag = get_intflag();
  const int indexflag = get_indexsumflag();
  const int seriesflag = get_seriesflag();
  const int problemtype = get_problemtype();
  const int topic = get_currenttopic();
  const int logcollectflag = get_logcollectflag();
  const int expandflag = get_expandflag();
  const int factorflag = get_factorflag();
  const int comdenomflag = get_comdenomflag();
  const int radicalflag = get_radicalflag();
  const int limitflag = get_limitflag();
  const int inpowerflag = get_inpowerflag();
  const int difflag = get_difflag();
  const int limfractflag = get_limfractflag();
  const int indenomflag = get_indenomflag();
  const int innumflag = get_innumflag();
  int pathlength = get_pathlength();
  unsigned short const *path = get_path();
  term u,logrootindex,cancelled,trash;
  int complexflag = get_complex();
  int  sumflag, vectorflag, trigflag, ntrigflag,logflag, nlogs, nconst,iflag,complexpolyflag;
  int infinityflag, sqrtflag, rootflag, sigmaflag, absflag, fractintpowerflag,fractexpflag,sgflag;
  int logsqrtflag=0, logrootflag=0, complexargflag = 0, powerofsumflag = 0;
  sumflag = vectorflag = trigflag = ntrigflag = logflag = nlogs = nconst = iflag = complexpolyflag = 0;
  infinityflag = sqrtflag = rootflag = sigmaflag = absflag = fractintpowerflag = fractexpflag = sgflag = 0;
  if(problemtype == POWERSERIES  && ARITY(t) == 2 && FUNCTOR(ARG(1,t)) == SUM &&
     (  ATOMIC(ARG(0,t)) || (FUNCTOR(ARG(0,t)) == '^' && isinteger(ARG(1,t)) ) ||    // isinteger will accept a negative integer (and more)
      (!numerical(ARG(0,t)) && FRACTION(ARG(0,t)) && monomial(ARG(0,ARG(0,t))) && monomial(ARG(1,ARG(0,t))))  // example,  (1/x^2) times a series 
     )
    )
     { o[i] = constantintosigma; ++i;  
     }
  if(get_whichpass() == 5 && seminumerical(t) && contains_double(t))
     { o[i] = devalop; ++i;
       *nops = i;
       return;
     }
  if(pathlength >= 5 &&
     path[pathlength-3] == SQRT &&
     intflag  /* sqrt of product in an integrand */
    )
     { unsigned short j;
       term x = get_eigenvariable();
       for(j=0;j<n;j++)
           { if(!is_linear_in(ARG(j,t),x))
                break;
           }
       if(j==n)
          { /* product of linear functions in a square root */
            /* example:  integral(x^2 sqrt((x-3)(3-x)),x),
               which can be done by trig substitution once the (x-3)(3-x) is multiplied out.
            */
             o[i] = multiplyoutandsimp; ++i;
          }
      }
  if(pathlength >= 3 && path[pathlength-3] == DIFF &&
     topic == _diff_polynomial &&  /* product rule and chain rule are not to be used */
     contains(t,'+')
    )
     { o[i] = multiplyoutandsimp;++i;
     }
  if(problemtype == POWERSERIES && contains(t,SUM))
     { o[i] = multiplyoutandsimp;++i;
     }
  for(j=0;j<n;j++)
     { u = ARG(j,t);
       if(ISINFINITE(u))
          infinityflag = 1;
       if(constant(u))
          ++nconst;
       if(iscomplex(u))
          { ++complexargflag;
            if(equals(u,complexi))
               { ++iflag;
                 ++complexpolyflag;
                 continue;
               }
          }
       if(ATOMIC(u))
          continue;
       g = FUNCTOR(u);
       switch(g)
          { case '/' :
               break;
            case '+' :
               if(SOLVETYPE(problemtype) && econstant(u) && !econstant(t))
                   break; /* constant sums should be left alone; for example,
                             (3x + 2) dy/dx should not be distributed in
                             implicit diff problems */
               ++sumflag;
               h = contains_sqrt(u);
               if(h==ABSFUNCTOR)
                  ++absflag;
               else if(h==SQRT)
                  ++sqrtflag;
               else if(h==ROOT)
                  ++rootflag;
               if(contains_fractional_exponents(u))
                  ++fractexpflag;
               if(complexflag && iscomplex(u) && ispolyin(u,complexi))
                  ++complexpolyflag;
               if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == COSH &&
                  FUNCTOR(ARG(1,u)) == SINH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(1,u))) &&
                  (coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
                 )
                   { ++coshsinhflag;
                     coshsinharg = ARG(0,ARG(0,u));
                   }
               else if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == SINH &&
                  FUNCTOR(ARG(1,u)) == COSH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(1,u))) &&
                  (coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
                 )
                   { ++coshsinhflag;
                      coshsinharg = ARG(0,ARG(0,u));
                   }
               else if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == SINH &&
                  NEGATIVE(ARG(1,u)) &&
                  FUNCTOR(ARG(0,ARG(1,u))) == COSH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(0,ARG(1,u)))) &&
                  (coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
                 )
                   { ++coshsinhflag;
                      coshsinharg = ARG(0,ARG(0,u));
                   }
               else if(ARITY(u) == 2 && FUNCTOR(ARG(0,u)) == COSH &&
                  NEGATIVE(ARG(1,u)) &&
                  FUNCTOR(ARG(0,ARG(1,u))) == SINH && equals(ARG(0,ARG(0,u)),ARG(0,ARG(0,ARG(1,u)))) &&
                  (coshsinhflag == 0 || equals(coshsinharg,ARG(0,ARG(0,u))))
                 )
                   { ++coshsinhflag;
                      coshsinharg = ARG(0,ARG(0,u));
                   }
               break;
            case MATRIX:
               vectorflag = 1;
               break;
            case VECTOR:
               vectorflag = 1;
               break;
            case SQRT:
               ++sqrtflag;
               break;
            case ROOT:
               ++rootflag;
               break;
            case SUM:
               sigmaflag = 1;
               break;
            case ABSFUNCTOR:
               ++absflag;
               break;
            case SG:
               ++sgflag;
               break;
            case SIN:
               ++ntrigflag;
               trigflag |= (1 << TRIGINDEX(SIN));
               break;
            case COS:
               ++ntrigflag;
               trigflag |= (1 << TRIGINDEX(COS));
               break;
            case TAN:
               ++ntrigflag;
               trigflag |= (1 << TRIGINDEX(TAN));
               break;
            case SEC:
               ++ntrigflag;
               trigflag |= (1 << TRIGINDEX(SEC));
               break;
            case CSC:
               ++ntrigflag;
               trigflag |= (1 << TRIGINDEX(CSC));
               break;
            case COT:
               ++ntrigflag;
               trigflag |= (1 << TRIGINDEX(COT));
               break;
            case LN:
               if(!constant(u))
                  { logflag = LN;
                    ++nlogs;
                  }
               if(FUNCTOR(ARG(0,u))==SQRT)
                  logsqrtflag = LN;
               if(FUNCTOR(ARG(0,u))==ROOT)
                  { logrootflag = LN;
                    logrootindex = ARG(0,ARG(0,u));
                  }
               break;
            case LOG:
               if(!constant(u) || constant(t))
               /* we need to change 2 log 2 to log 2^2, that's why
                  the part about constant(t); otherwise 10^(2 log 2)
                  does not simplify to 4 */

                  { logflag = LOG;
                    ++nlogs;
                  }
               if(FUNCTOR(ARG(0,u))==SQRT)
                  logsqrtflag = LOG;
               if(FUNCTOR(ARG(0,u))==ROOT)
                  { logrootflag = LOG;
                    logrootindex = ARG(0,ARG(0,u));
                  }
               break;
            case LOGB:
               if(!constant(u) || constant(t))
                  { logflag = LOGB;
                    ++nlogs;
                  }
               if(FUNCTOR(ARG(1,u))==SQRT)
                  logsqrtflag = LOGB;
               if(FUNCTOR(ARG(1,u))==ROOT)
                  { logrootflag = LOGB;
                    logrootindex = ARG(0,ARG(1,u));
                  }
               break;
            case '^':
               if(FRACTION(ARG(1,u)))
                  { if(INTEGERP(ARG(0,u)))
                       ++fractintpowerflag;
                    ++fractexpflag;
                  }
               if(FUNCTOR(ARG(0,u)) == '+')
                  ++powerofsumflag;
               break;
          }
     }
  if(FUNCTOR(ARG(0,t)) == '^' &&
     FUNCTOR(ARG(1,t)) == '^' &&
     equals(ARG(1,ARG(0,t)),ARG(1,ARG(1,t))) &&
     ISINTEGER(ARG(0,ARG(0,t))) &&
     ISINTEGER(ARG(0,ARG(1,t)))
    )
     /* example:  4^n 27^n x^(6n) should become (4*27)^n x^6n */
     { o[i] = prodofpowers; ++i;
     }
  if(coshsinhflag > 1)
     { o[i] = difofsquares; ++i;
       /* (cosh + sinh)(cosh - sinh) = cosh^2 - sinh^2 = 1.
       */
     }
  if(absflag && sgflag)
     { o[i] = abstimessg; ++i;
       /* |u| sg(u) = u */
     }
  if(ARITY(t) > 2 && (get_ringflag() & RATRING) &&
     RATIONALP(ARG(0,t)) &&
     numerical(ARG(1,t)) && !numerical(ARG(2,t)) &&
     ! get_factorflag()   // prevent loop with pulloutdenom
    )
     { o[i] = multiplycoefs; ++i;
       /* change (1/2) sqrt(3) x  to (sqrt(3)/2) x;
       */
     }
  if(logflag == LN &&
     nlogs == 1 &&  /* Don't create ln(x^ln x) or even ln (x^ln 2) */
     logcollectflag &&
     problemtype != DIFFERENTIATE_FROM_DEFN &&
     (!limitflag || (pathlength >= 3 && path[pathlength-3] == '+')) &&
         /* attract logs in a sum even in a limit.  The reason for
            !limitflag is that it blocks changelimitvariable in
            differentiating a^x from definition. */
         /* Note e^(n ln a) => a^n anyway by another operator. */
         /* But we don't say !inpowerflag as formerly, because
            of e^(2ln x + 5ln y) which
            should go to e^(ln x^2 + ln y^5) */
     nconst >= n-1  /* otherwise we'll create something like ln(x^x) or ln(x^sqrt x) */
    )

     { o[i] = attractlns; ++i;
     }
  if(sumflag == 1 && !contains_calc(t))
     { o[i] = multiplyifcancels; ++i;  /* a(b+c) = ab + ac if there's a cancellation in ab or ac */
     }
  if(logflag == LOG && nlogs == 1 && (inpowerflag || logcollectflag) && nconst >= n-1)
     { o[i] = attractlogs; ++i;
     }
  if(logflag == LOGB && nlogs == 1 && (inpowerflag || logcollectflag) && nconst >= n-1)
     { o[i] = attractlogb2; ++i;
     }
  if(logsqrtflag || logrootflag)
     { term c,s,cancelled,trash;
       ratpart2(t,&c,&s);
       if(logsqrtflag && INTEGERP(c) && ISEVEN(c))
          { if(logsqrtflag == LN)
               { o[i] = attractlns; ++i;
               }
            else if(logsqrtflag == LOG)
               { o[i] = attractlogs; ++i;
               }
            else if(logsqrtflag == LOGB)
               { o[i] = attractlogb2; ++i;
               }
          }
       if(logrootflag && INTEGERP(c) && !cancel(c,logrootindex,&cancelled,&trash))
          { if(logrootflag == LN)
               { o[i] = attractlns; ++i;
               }
            else if(logrootflag == LOG)
               { o[i] = attractlogs; ++i;
               }
            else if(logrootflag == LOGB)
               { o[i] = attractlogb2; ++i;
               }
          }
     }
  if(infinityflag)
     { o[i] = timesinfinity; ++i;
     }
  if(limitflag)
     { if(get_complex())
          { o[i] = multiplycomplexconjugates; ++i;
            /* must precede difofsquares */
          }
       o[i] = difofsquares; ++i;
       o[i] = makedifofcubes; ++i;
       o[i] = makesumofcubes; ++i;
       o[i] = difofpowers; ++i;
     }
  if(ntrigflag > 1 &&   /* more than one trig function in this product */
     (
      topic == _trig_product ||
      difflag ||  /* d/dx (sin x cos x) = d/dx (1/2) sin 2x  */
      (limfractflag == -1  && pathlength >= 3 && path[pathlength-3] != '/') ||
          /* use reversedoublesin in limits of fractions where the
             product in question is not the numerator or denom itself
             but is deeper.  So we don't use it on
                 lim(x->0, sin x cos x)
                 lim(x->0, (sin x cos x)/x
         */
      (pathlength >= 3 &&
           ( path[pathlength-3] == '='  ||
             (path[pathlength-5] == '=' && path[pathlength-3] == '+') ||
             (path[pathlength-7] == '=' && path[pathlength-5] == '+' && path[pathlength-3] == '-')
           )
      )   
        /* to solve 2 sin x cos x = 1,  write the lhs as  sin 2x.  From
           here we can't check easily whether the other side of the equation
           is constant, and in general it is NOT a good thing to do otherwise,
           e.g. in verifying sin^4 x = cos^3 x sin x + sin^3 x cos x,
           we get this expression in a factor on the left and thrash around
           considerably if reversedoublesin is used.  Therefore we appeal
           to all_trig here to look outside this term.
        */
     )
    )
     { if(TRIGCONTAINS(trigflag,SIN))
          { if(!intflag && !seriesflag && !indexflag &&
                (topic == _trig_product ||
                 all_trig(t) ||
                 ( problemtype == TRIG_IDENTITY &&
                   pathlength >= 3 &&
                    ( path[pathlength-3] == '='  ||
                      (path[pathlength-5] == '=' && path[pathlength-3] == '+') ||
                      (path[pathlength-7] == '=' &&
                       path[pathlength-5] == '+' &&
                       path[pathlength-3] == '-')
                    )
                   // example:  2sin(x/2) cos(x/2) = sin(x),
                   // or with a minus sign
                 )
                )
              )
               { if(ARITY(t) >= 3 && equals(ARG(0,t),two))
                    { o[i] = reversedoublesin2; ++i;
                    }
                 else
                    { o[i] = reversedoublesin; ++i;
                    }
                 /* don't use in integrals, it blocks the Weierstrass substitution */
               }
             if(!limitflag && problemtype != DIFFERENTIATE && problemtype != IMPLICIT_DIFF)
               //  don't use it on (sin x)(cos y), as occurs in topic 142 problem 40 and 41
               //  but do use it on sin(2x)(sin 3y) as in Trig products (108)  problem 9
               {
                 o[i] = sinsin; ++i;
                 o[i] = sincosop; ++i;
                 if(get_nvariables() == 1)
                    { o[i] = cossin; ++i;}
               }
          }
       if(TRIGCONTAINS(trigflag,COS) &&
          !limitflag &&
          problemtype != DIFFERENTIATE &&
          problemtype != IMPLICIT_DIFF
         )
          { o[i] = coscos; ++i;
          }
     }
  if(difflag && sumflag > 1 &&
      ( path[pathlength-1] == DIFF ||  /* d/dx (1-cos x)(1 + cos x)  */
        (path[pathlength-1] == '/' && path[pathlength-3] == DIFF)
                                   /* d/dx (1/((1-cos x)(1+cos x)))  */
      )
     )
     { o[i] = difofsquares; ++i;
       o[i] = makedifofcubes; ++i;
       o[i] = makesumofcubes; ++i;
     }
  if(limitflag && TRIGFUNCTOR(path[pathlength-3]))
    {  // sin(a(x+h))  in differentiate from defn
       o[i] = distriblaw; ++i;
    }
  if(
     (sumflag && EXPANDFLAG && complexpolyflag == 0) ||
     complexpolyflag > 1
     /* (1 + sqrt 2)i  does not go into the following code and
        hence distriblaw does not get used on it, because
        complexpolyflag will be 1 in this case. */
    )
     { if(!limitflag) /* else it's already been added */
          { if(get_complex() && (problemtype != FACTOR || pathlength >= 2))
               { o[i] = multiplycomplexconjugates; ++i;
                 /* must precede difofsquares */
               }
            if(sumflag > 1 && (problemtype != FACTOR || pathlength >= 2))
               { o[i] = difofsquares; ++i;
                 o[i] = makedifofcubes; ++i;
                 o[i] = makesumofcubes; ++i;
               }
          }
       if(sumflag > 1 &&  (problemtype != FACTOR)) // commented out 12.9.24 || pathlength >= 2))
          { if(status(multiplyoutandsimp) >= KNOWN)
               { o[i] = multiplyoutandsimp; ++i;
               }
            else
               { o[i] = multiplyout; ++i;
               }
          }
       else if(!limitflag && !get_polyvalfactorflag())
        /*  leave  (sin x)(1 - cos h)/h  alone  in a limit */
        /*  and don't use distriblaw if content-factoring is turned on */
          { o[i] = distriblaw; ++i;
          }
       else
          { o[i] = distribandcancel; ++i;
          }
     }
  else if(
           ((sqrtflag > 1 || rootflag > 1 || absflag > 1 || fractexpflag > 1) && sumflag) &&
           /* example: (2 sqrt 7 + sqrt 2)(3 sqrt 5 - 2 sqrt 3) should be multiplied out */
           /* But, we DON't want to multiply out in (x-sqrt 3)(x+sqrt 3) < 0,
               especially if we just factored it. */
           ( !FACTORFLAG ||
             econstant(t)
           ) &&
           ! (inpowerflag && get_complex()) &&
           ! (pathlength == 3 && path[0] == '+' && get_complex() &&
              iscomplex(t) && !cancel(t,complexi,&cancelled,&trash)
              &&!iscomplex(trash)
             )  /* don't distribute in  x + (a+b)i  */
         )

     { if(sumflag > 1)
          { if(status(multiplyoutandsimp) >= KNOWN &&
               ( CALCULUS_TOPIC(topic) ||
                 (TRIG_TOPIC(topic) && topic >= _logarithms)
               )
              )
               { o[i] = multiplyoutandsimp; ++i;
               }
            else
               { o[i] = multiplyout; ++i;
               }
          }
       else
          { o[i] = distriblaw; ++i;
          }
       *nops = i;
       return;
     }
  else
     { if((sumflag == 1 && DISTRIB && /* and not in a denom in a sum of fractions */
          !(pathlength >= 5 && path[pathlength-2]==2 &&
            path[pathlength-3]== '/' && path[pathlength-5] == '+'
           ) &&
          !(pathlength >= 7 && path[pathlength-2]==2 &&
            path[pathlength-3]== '/' && path[pathlength-5] == '-' &&
            path[pathlength]-7 == '+'
           ) &&
           /* No point distributing just inside a logarithm */
          !(pathlength >= 3 && (path[pathlength-2] == LN || path[pathlength-2] == LOG)) &&
          /* Don't distribute a power of a sum over a sum, e.g. (a+b)^2(c+d).
             If you don't intend to expand everything this won't help anyway.
             If you do intend eventually to expand everything,
             you'll just content_factor it again before expanding.
             First the power should be expanded. */
          (!powerofsumflag || !sumflag) &&
          /* following stops (2k+1)pi i from being distributed */
          !(iflag == 1 && complexargflag == 1)
          )
          ||
          (get_infractionflag() && cfc() == DIFF &&
           topic != _logarithmic_differentiation
          )
          ||
          topic == _trig_product

         )
         /* the conditions in DISTRIB are carefully chosen
            so we never get contentfactor and distriblaw
            together, because they would loop.  The last part
            causes distriblaw to be used in fractions inside derivatives,
            except when we're studying logarithmic differentiation.
         */
          { o[i] = distriblaw; ++i;
          }
       else if(sumflag == 1 && ARITY(t)== 2 && RATIONALP(ARG(0,t))
               && !get_polyvalfactorflag() && !factorflag && !get_infractionflag()
              )
          { o[i] = distriblaw; ++i;
            /* Always distribute a rational fraction except when
               it would loop with contentfactor or when we are in
               a compound fraction. */
          }
       else
          { o[i] = distribandcancel; ++i;
          }
       if( inhibited(cancelop) /* after rationalizedenom for example */
           && !limitflag  /* else it's already been thrown in */
           && !inhibited(arithmetic)
           /* after findcommondenom, we don't want to multiply together
              factors in the denom before addfractions has been used.
              We recognized that situation by inhibited(arithmetic) */
           && (problemtype != FACTOR || pathlength >= 2)
         )
          { if(contains(t,'i'))
               { o[i] = multiplycomplexconjugates; ++i;
               }
            if(
               !(get_ringflag() & ALGINT) || /* otherwise we get a loop factoring (x-sqrt a)/(x^2-a) */
               (!contains(t,ROOT) && !contains(t,SQRT))
               /* but on (1+sin x)/(1-sin x)(1+sin x) we really need to do it! */
              )
               { if(get_complex())
                    { o[i] = multiplycomplexconjugates; ++i;
                      /* must precede difofsquares */
                    }
                 o[i] = difofsquares; ++i;
                 o[i] = makedifofcubes; ++i;
                 o[i] = makesumofcubes; ++i;
               }
          }
       else if(n == 2 && !(get_ringflag() & ALGINT) &&  (problemtype != FACTOR || pathlength >= 2))
            { term u = ARG(0,t);
              term v = ARG(1,t);
              int uflag = contains_sqrt(u);
              int vflag = contains_sqrt(v);
              if(NEGATIVE(u))
                 u = ARG(0,u);
              if(NEGATIVE(v))
                 v = ARG(0,v);
              if( uflag == SQRT || vflag == SQRT)
                  { if(get_complex())
                       { o[i] = multiplycomplexconjugates; ++i;
                         /* must precede difofsquares */
                        }
                    o[i] = difofsquares; ++i;
                  }
                /* Example (sqrt (ab) - b)(sqrt (ab) + b) / (a-b) =
                   ab-b^2 = b(a-b)/(a-b) = b
                   This has to be done with factorflag on!   But, we don't want
                   to loop.  What about   (1-sin sqrt 2)(1+sin sqrt 2) = 1-sin^2 sqrt 2?
                   That's why we use contains_sqrt(t) instead of contains(t,SQRT).
                */
              if( uflag == ROOT || vflag == ROOT)
                 { o[i] = makedifofcubes; ++i;
                   o[i] = makesumofcubes; ++i;
                 }
            }
     }

  /*                if(vectorflag)
                       { o[i] = vsimp; ++i;vectorflag = 0;}  */
                         /*  * is scalar multiplication of a vector */
                         /*       FINISH THIS */
  if(sqrtflag > 1)
     { o[i] = productofsqrts; ++i;
     }
  if(rootflag > 1)
     { o[i] = productofroots; ++i;
     }
  if(problemtype >= LIMITS && sqrtflag && radicalflag != 1)
     { o[i] = sqrtexp; ++i;  /*  x sqrt x => x^(3/2) */
     }
  if(problemtype >= LIMITS && rootflag && radicalflag != 1)
     { o[i] = rootexp; ++i;  /*  x sqrt x => x^(3/2) */
     }
  if((sqrtflag > 1 || rootflag > 1 || absflag > 1) && sumflag==1)
              /* sumflag == 1, not just sumflag != 0; we
                 don't want to use distriblaw on (x-sqrt a)(x-sqrt b) */
     { o[i] = distriblaw; ++i;  /*  sqrt x (sqrt x + 1) for example */
     }
  if(absflag > 1)
     { o[i] = multiplyabsval; ++i;
     }
  if(sigmaflag && EXPANDFLAG)
     { o[i] = productofsigmas; ++i;
     }
  if(fractintpowerflag >= 2)
     { o[i] = prodofpowers; ++i;
     }
  if(!vectorflag && !stop_orderfactors(t))
     { o[i] = orderfactors; ++i;
     }
  else if(constant(t) && /* see matrices.c for explanation of this restriction */
          topic != _gauss_jordan
         )
     { o[i] = multiplymatrices; ++i;
     }
  if(rootflag && ntrigflag && pathlength >= 3 && path[pathlength-3] == LIMIT)
     /* example,  lim(u->0+, root(3,1/u) sin u)  */
     { o[i] = pushunderoddroot; ++i;
     }
  if(iflag == 1 && pathlength >= 3 && path[pathlength-3] == LN)
     { o[i] = rectangulartopolar; ++i;
     }
  *nops = i;
}
/*_______________________________________________________________*/
static int not_quadratic(term t)
/* return 1 if t contains a power with exponent unequal to 2;
   0 if all exponents are equal to 2 (or there are no exponents)  */

{  unsigned short n = ARITY(t);
   int i;
   if(ATOMIC(t))
       return 0;
   if(FUNCTOR(t) == '^' && !equals(ARG(1,t),two))
       return 1;
   for(i=0;i<n;i++)
      { if(not_quadratic(ARG(i,t)))
            return 1;
      }
   return 0;
}

/*________________________________________________________________*/
static int essentially_linear(term t)
/* return 1 if t is, for example, 3(t-1) + t, a term composed
of sums and products with only linear terms */
{ int i,ct = 0;
  unsigned short f = FUNCTOR(t);
  unsigned short n = ARITY(t);
  int problemtype = get_problemtype();
  term u;
  if(ATOMIC(t) || constant(t) || (SOLVETYPE(problemtype) && econstant(t)))
      return 1;
  if(f == '*')
     { for(i=0;i<n;i++)
         { u = ARG(i,t);
           if(constant(u) || (SOLVETYPE(problemtype) && econstant(u)))
               continue;
           if(ct)     /* a previous term was nonconstant; that makes two */
              return 0;
           ++ct;
           if(!essentially_linear(u))
              return 0;
         }
       return 1;  /* a product of constant terms and at most one linear term */
    }
 if(f=='+')
    { for(i=0;i<n;i++)
         { u = ARG(i,t);
           if(!essentially_linear(u))
              return 0;
         }
      return 1;  /* a sum of linear terms */
    }
 if(f=='-')
    return essentially_linear(ARG(0,t));
 return 0;  /* only +,*,- are acceptable functors in nonconstant terms */
}
/*__________________________________________________________*/
static int cfc(void)
/* cfc = compound fraction in calculus.  Return DIFF or INTEGRAL
if we are in a compound fraction inside a diff or integral, according to 'path'. */
{  int i;
   int state =0;
   unsigned short const *path = get_path();
   int pathlength = get_pathlength();
   for(i=pathlength-1; i>=0; i-=2)
      { if(path[i] == '/')
            ++state;
        if(state > 1 && (path[i] == DIFF || path[i] == INTEGRAL))
            return path[i];
      }
   return 0;
}
/*__________________________________________________________*/
static int fc(void)
/* fc = fraction in calculus.  Return DIFF or INTEGRAL
if we are in a fraction inside a diff or integral, according to 'path'. */
{  int i;
   int state =0;
   unsigned short const *path = get_path();
   int pathlength = get_pathlength();
   for(i=pathlength-1; i>=0; i-=2)
      { if(path[i] == '/')
            ++state;
        if(state && (path[i] == DIFF || path[i] == INTEGRAL))
            return path[i];
      }
   return 0;
}

#undef HIEVENPOWER

/*______________________________________________________________________*/
static int no_factor_functor(unsigned short f)
/* return 1 if we shouldn't even content_factor args of f,
0 otherwise.  This is the the case for arctrig, hyperbolic, and special
functors, but not for log, and not for ordinary trig functors,
because in identities you sometimes have to content_factor before
using a double angle identity */
{ if(ACOS <= f && f <= ATAN)
     return 1;
  if(ASINH <= f && f <= ACOTH)
     return 1;
  return 0;
}
/*______________________________________________________________________*/
static int quadratic_in(term t, term x)
/* return 1 if t is quadratic in x */
{ POLYnomial p;
  int ans;
  int err = makepoly(t,x,&p);
  if(err || DEGREE(p) != 2)
     ans = 0;
  else
     ans = 1;
  if(!err)
     RELEASE(p);
  return ans;
}
/*______________________________________________________________________*/
static int tanflag(term t)
/* t is a product; return 1 if t contains arctan(tan x),
return 2 if it contains an indefinite integral or constant of
integration, return 0 otherwise.
*/
{ unsigned short n;
  int i;
  term u;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(FUNCTOR(u) == CONSTANTOFINTEGRATION)
          return 2;
       if(ATOMIC(u))
          continue;
       if(FUNCTOR(u) == ATAN && FUNCTOR(ARG(0,u)) == TAN)
          return 1;
       if(FUNCTOR(u) == INTEGRAL && ARITY(u) == 2)
          return 2;
     }
  return 0;
}
/*_________________________________________________________________________*/
static int comdenom_conditions(int problemtype, term t)
/* return 1 if common denoms should be used on sum t */
{ unsigned pathlength = get_pathlength();
  unsigned short const *path = get_path();
  if(get_infractionflag())
     return 1;
  if(SOLVETYPE(problemtype) &&
               /* so apartandcancel won't be used, which would
                  create a loop */
     attractible(t,get_eigenvariable())
    )
     return 1;
  /* use common denoms just inside arctrig functions, e.g.
     on 1 - (sqrt 2)/(1-sqrt 2).  Maybe the arg will simplify
     to 1/sqrt 2 or something else for which the arctrig
     function can be evaluated. */
  if(pathlength >= 3 &&
     ACOS <= path[pathlength-3] && path[pathlength-3] <= ATAN &&
     numerical(t)
    )
     return 1;
#if 0 //removed 4.5.96, screws up half angle 6
  if(pathlength >= 3 &&
     (path[pathlength-3] == TAN || path[pathlength-3] == COT) &&
     problemtype == TRIG_IDENTITY && ARITY(t) == 2 &&
     SIGNEDFRACTION(ARG(0,t)) && SIGNEDFRACTION(ARG(1,t)) &&
     iseven(NEGATIVE(ARG(0,t)) ? ARG(1,ARG(0,ARG(0,t))) : ARG(1,ARG(0,t))) &&
     iseven(NEGATIVE(ARG(1,t)) ? ARG(1,ARG(0,ARG(1,t))) : ARG(1,ARG(1,t)))
    )
     /* Example:  tan(pi/4 + theta/2); there is a formula for tan(u/2),
        but not for sin(u/2) or cos(u/2), so we only do this for tangent.
        And we only want to do it in VERY SPECIAL circumstances as
        it won't always work, e.g. on tan(pi/3 + theta/3), common denoms
        don't do any good at all.  The conditions stated here must match
        those in postops where apart is used, to avoid a loop.
     */
     return 1;
#endif
  if(pathlength >= 3 && path[pathlength-3] == '^' &&
     contains_fractional_exponents(t)
    )
     return 1;   /* example:  (x^(1/2) + 1/x^(1/2))^2  */
  if(algebraic_number(t))
     return 0;   /* example, 3/2 + (1/2) sqrt(17).  Leave these alone */
  if( problemtype == LINEAR_EQUATIONS &&
      seminumerical(t) /* simplify coefficients like sqrt 3 + 4/sqrt 3 */
    )
     return 1;
  if(pathlength >= 3 && path[pathlength-3] == '*')
     return 1;   // 5.30.13,  use common denominators inside products 
  return 0;
}

/*______________________________________________________________________*/
int immediate_comdenom(term t)
/* return 1 if we shouldn't try substitutions in solving t = 0 but
should proceed to putting t over a common denom.
  Example 1:  when t = a + b/c where c is "noxious" (i.e. a sum containing a sqrt)
  Example 2:  sqrt x  + b/sqrt x
  Example 3:  4/x + 9/sqrt x + 2
  Example 4:  sqrt x - 3/(sqrt(4x-2) + 6 sqrt x)
  Example 5:  (5x-6)^(1/5)  + x/(5x-6)^(4/5).  This goes much faster by
              common denominators than by substitution.
  Example 6:  All the denoms are multiples of the same quantity, as in
              (2+x)/(6x) - 3/(5x) - 2 = 0
*/

{ int i,err,k;
  unsigned short n;
  term x,temp,u,v,p,q,d;
  term sofar,c,s;
  int flag = 0;
  if(FUNCTOR(t) != '+')
    return 0;
  n = ARITY(t);
  x = get_eigenvariable();
  for(i=0;i<n;i++)
     { temp = ARG(i,t);
       if(NEGATIVE(temp))
          temp = ARG(0,temp);
       if(FRACTION(temp))
          { if(!flag && contains(ARG(1,temp),FUNCTOR(x)))
               { flag = 1;
                 if(FUNCTOR(ARG(1,temp)) == '*')
                    twoparts(ARG(1,temp),x,&c,&s);
                 else
                    { c = one;
                      s = ARG(1,temp);
                    }
                 sofar = s;
               }
            else if(flag == 1 && contains(ARG(1,temp),FUNCTOR(x)))
               { if(FUNCTOR(ARG(1,temp)) == '*')
                    twoparts(ARG(1,temp),x,&c,&s);
                 else
                    { c = one;
                      s = ARG(1,temp);
                    }
                 if(!equals(s,sofar))
                    flag = 2;
               }
            if(contains_sqrt2(temp))
               break;
            d = ARG(1,temp);
            if(FUNCTOR(d) == '+')
               { for(k=0;k<ARITY(d);k++)
                    { u = ARG(k,d);
                      if(contains_sqrt2(u))
                         break;
                      if(FUNCTOR(u) == '^' && RATIONALP(ARG(1,u)))
                         break;  /* handle Example 5 */
                    }
                 if(k<ARITY(d))
                    break;
               }
          }
     }
  if(flag == 1)
     return 1;  /* the condition mentioned in Example 6 holds */
  if(i==n)
     return 0;  /* there must be at least one fraction involving sqrt x
                   or root x in the sum, or failing that, as in Example 4,
                   one of the fractions must have a denom which is a sum
                   and one of the factors has a sqrt factor. */
  if(n > 2)
     { /* handle Example 3.  What's significant about that
          example is that the  numerator turns out to be quadratic in sqrt x.
       */
       subst(var0,sqrt1(x),t,&p);
       if(equals(p,t))
          return 0;   /* No sqrt was present in t */
       subst(make_power(var0,two),x,p,&q);  /* 4/var0^2 + 9/var0 + 2 in Example 3 */
       return contains(q,FUNCTOR(x)) ? 0 : 1;
     }
  u = ARG(0,t);
  v = ARG(1,t);
  if(SIGNEDFRACTION(u) && noxious(denom(u)))
     return 1;
  if(SIGNEDFRACTION(v) && noxious(denom(v)))
     return 1;
  if(!contains_sqrt2(u) && !contains_sqrt2(v))
     return 0;
  if(SIGNEDFRACTION(u) && !SIGNEDFRACTION(v))
     { temp = u;
       u = v;
       v = temp;
     }
  if(SIGNEDFRACTION(v) && !SIGNEDFRACTION(u))
     { d = denom(v);
       err = cancel(u,d,&p,&q);
       if(err && FUNCTOR(d) == '+')
          { term c,s,c2,s2;
            int count=0;
            ratpart2(u,&c,&s);
            if(FUNCTOR(s) == SQRT)
               { for(k=0;k<ARITY(d);k++)
                    { ratpart2(ARG(k,d),&c2,&s2);
                      if(FUNCTOR(s2) != SQRT)
                         return 0;
                      err = cancel(ARG(0,s),ARG(0,s2),&p,&q);
                      if(err || contains_sqrt2(q))
                         return 0;
                      if(!econstant(q))
                         ++count;
                      if(count > 1)
                         return 0;
                    }
               }
          }
       else if(err)
          return 0;
       if(!contains_sqrt2(q))
          return 1;   /*  sqrt x + 1/sqrt x   for example */
     }
  return 0;
}

/*_________________________________________________________________*/
static int stop_factorundersqrt(term t)
/* t is a sum.  Return 1 if there's no point factoring t when it
occurs under a square root.  Specifically return 0 if either the
content or principal part of t is not squarefree, when t is a
polynomial, or when t is not a polynomial, return 0 if the content
is not squarefree.
*/
{ term *atomlist;
  term x,y,c,s,q;
  POLYnomial p;
  int err,nvars;
  void  *savenode;
  if(FUNCTOR(t) != '+')
     return 1;  /* no point trying to factor a non-sum */
  err = content_factor(t,&c,&s);
  if(err)
     return 0;
  if(INTEGERP(c) && !nsquarefree(c))
     return 0;
  if(!contains(t,'^'))
     return 1;
  nvars = atomsin(t,&atomlist);
  if(nvars == 1)
     { x = atomlist[0];
       free2(atomlist);
       err = makepoly(t,x,&p);
       if(err)
          return 0;
       savenode = heapmax();
       q = squarefree(p,&c);
       if(ARITY(q) == 1)
          { reset_heap(savenode);
            return 1;   /* no multiple roots */
          }
       reset_heap(savenode);
       return 0;
     }
  if(nvars == 2)
     { x = atomlist[0];
       y = atomlist[1];
       free2(atomlist);
       err = homogeneous_poly(t,x,y,&p);
       if(err)
          return 0;  /* can't factor it anyway probably but no harm trying...*/
       savenode = heapmax();
       q = squarefree(p,&c);
       if(ARITY(q) == 1)
          { reset_heap(savenode);
            return 1;
          }
       reset_heap(savenode);
       return 0;
     }
  free2(atomlist);
  return 0;
}

/*_________________________________________________________________*/
static int stop_factorunderroot(term t)
/* t is a sum.  Return 1 if there's no point factoring t when it
occurs under a cube root.  Specifically return 0 if either the
content or principal part of t is not cubefree, when t is a
polynomial, or when t is not a polynomial, return 0 if the content
is not squarefree.
*/
{ term *atomlist;
  term x,y,c,s,q;
  POLYnomial p;
  int err,nvars;
  void  *savenode;
  if(FUNCTOR(t) != '+')
     return 1;  /* no point trying to factor a non-sum */
  err = content_factor(t,&c,&s);
  if(err)
     return 0;
  if(INTEGERP(c) && !rootfree(c,3))
     return 0;
  if(!contains(t,'^'))
     return 1;
  nvars = atomsin(t,&atomlist);
  if(nvars == 1)
     { x = atomlist[0];
       free2(atomlist);
       err = makepoly(t,x,&p);
       if(err)
          return 0;
       savenode = heapmax();
       q = squarefree(p,&c);
       if(ARITY(q) < 3)
          { reset_heap(savenode);
            return 1;   /* no multiple roots */
          }
       reset_heap(savenode);
       return 0;
     }
  if(nvars == 2)
     { x = atomlist[0];
       y = atomlist[1];
       free2(atomlist);
       err = homogeneous_poly(t,x,y,&p);
       if(err)
          return 0;  /* can't factor it anyway probably but no harm trying...*/
       savenode = heapmax();
       q = squarefree(p,&c);
       if(ARITY(q) == 1) /* ARITY(q) > 3  was considered.  Do we want
                            root(3,x^2 + 2x + 1) to become root(3,(x+1)^2)? Yes.
                            So don't put ARITY(q) > 3, which would prevent it.
                        */
          { reset_heap(savenode);
            return 1;
          }
       reset_heap(savenode);
       return 0;
     }
  free2(atomlist);
  return 0;
}

/*_________________________________________________________________________*/
int contains_arctrigs(term t)
/* Return 1 if t contains an arctrig functor, 0 if not */
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(ACOS <= f && f <= ATAN)
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_arctrigs(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*_________________________________________________________________________*/
static int all_trig(term t)
/* t is presumed to be a subterm of history(currentline).  Return 1
if t contains all the trig functions in history(currentline).  Return 0
if there are some trig functions in history(currentline) not included
in t. */
{ int currentline = get_activeline();
  term p,u;
  if(currentline < 0)
      return 0;
  u = history(get_activeline());
  subst(var0,t,u,&p);
  return !contains_trig(p);
}

/*______________________________________________________________*/
static int trig_in_denom(term t)
/* return 1 if t contains a trig function in a denominator, 0 if not. */
{ unsigned short n;
  int i;
  if(ATOMIC(t))
     return 0;
  if(FRACTION(t))
     { if(contains_trig(ARG(1,t)))
           return 1;
       return trig_in_denom(ARG(0,t));
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(trig_in_denom(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*______________________________________________________________*/
static int stop_complexnegexp(term t)
/* return 1 if t is going to content_factor with a content
of the form e^-u.  The name is a misnomer because the exponent
is not required to be complex.
*/
{ term c,s;
  int err = content_factor(t,&c,&s);
  if(err)
     return 0;
  if(FUNCTOR(c) == '^' &&
     equals(ARG(0,c),eulere) &&
     NEGATIVE(ARG(1,c)) &&
     !constant(ARG(1,c))
    )
     return 1;
  return 0;
}
/*______________________________________________________________________*/
static int reversesinsq_conditions(term t)
/* return 1 if we should rewrite 1-cos u as 2 sin^2(t/2), 0 if not, where
t is passed as a sum to which this rule might possibly be applicable.
This should be done only if afterwards, all the trig functions will have
argument u/2.  For example, in sin(u/2) = 1-cos u.  This equation can
only be solved otherwise by squaring and later on rejecting the spurious
solutions; the solution by reversesinsq is much nicer.  But, there's no
hope of making this decision without examining the whole current line.
*/
{ char buffer[DIMREASONBUFFER];
  term v, whole,temp;
  int err,k;
  unsigned short trigflag;
  int problemtype = get_problemtype();
  if((problemtype == LIMITS || problemtype == LHOPITAL) &&
     status(lhopital) >= LEARNING
    )
     return 0;  /* lim(x->0, (1-cos x)/x) should be done by L'Hopital */
  if(problemtype == DIFFERENTIATE)
     return 0;
  if(get_intflag() || get_difflag())
     return 0; /*  sin^2 is converted to 1-cos to integrate it;
                   so using reversesinsq will cause a loop.
                   Also, sin^2 will be harder to differentiate,
                   so don't use it in derivatives.  However,
                   it might be a good idea in a limit. */
  k = get_activeline();
  if(k < 0)
     return 0;
  whole = history(k);
  set_trigflag(whole,&trigflag);
  if(!(trigflag & (1 << 12)))
     return 0;  /* all args of trig functions are atomic */
  err = reversesinsq(t,zero,&temp,buffer);
  if(err)
     return 0;  /* it won't work anyway */
  subst(temp,t,whole,&v);
  if(different_trig_args(v))
     return 0;
  return 1;
}
/*__________________________________________________________________________*/
static int trig_arg_aux(term t, term *a, term *b)
/* return 0 if t contains no trig functions.
If *a is ILLEGAL, and all trig functions in t have the same arg,
put that arg in *a and return 1.
If *a is not ILLEGAL, and t contains a trig arg different from *a,
put that arg in *b and return 2.
If *a is not ILLEGAL, and t contains only trig args equal to *a,
return 1.
*/
{ unsigned short n,f;
  int i,k;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(TRIGFUNCTOR(f))
     { if(FUNCTOR(*a) == ILLEGAL)
          { *a = ARG(0,t);
            return 1;
          }
       if(!equals(*a,ARG(0,t)))
          { *b = ARG(0,t);
            return 2;
          }
       return 1;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { k = trig_arg_aux(ARG(i,t),a,b);
       if(k == 2)
          return 2;
     }
  if(FUNCTOR(*a) == ILLEGAL)
     return 0;
  return 1;
}
/*__________________________________________________________________________*/
static int different_trig_args(term t)
/* return 1 if t contains trig functions with different args.
Return 0 if all trig functions have the same args or there are
no trig functions.
*/
{ term a,b;
  int k;
  SETFUNCTOR(a,ILLEGAL,0);
  SETFUNCTOR(b,ILLEGAL,0);
  k = trig_arg_aux(t,&a,&b);
  return k > 1 ? 1 : 0;
}

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