Sindbad~EG File Manager

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

/* code to implement automode
M. Beeson, for Mathpert
original date 1.7.91
last modified 2.27.99
2.25.00 modified strip_protections for infinite sums
2.27.00 modified around line 832 for comparison test problems
2.28.00 added transitionop.
10.2.02 added braces in adjust_trigexpandflag because VS.NET appeared in the debugger to be 
incorrectly executing the next line as part of the loop.  Changed calloc/free to 
callocate/free2 in adjust_trigexpandflag,  because VS.NET crashes on free for mysterious
reasons, even if all intervening code is taken out.
6.22.04 added pop and line before it at lines 1289-1290
1.16.05  removed all lines mentioning #ifndef PRODUCTION  / #endif
         and passed argument to InitializeShowStep
2.20.05 removed argument in calls to InitializeShowStep
2.26.05 put #ifndef PRODUCTION back around autotesting,  so we can run without autotest.dll
        in the release version.
6.18.06 made setimproper exported
6.19.06 windows dependence removed from includes
1.29.06 added initial_focus and the code that calls it.
9.1.07 modified initial_focus
       added path[pathlength] = 0; at line 3970
10.21.10 modified get_activeline to never ever return a negative number.
4.29.13  at line 837 added some code for POWERSERIES
5.6.13  include stdef.h
        changed DEBUG_MATHPERT to DEBUG  where loop_warning is called.
        made hypertrigfunctor static.
        Deleted an unused variable in autosimp
5.9.13  made seriesflag, difflag, limitflag, etc. all static, and added get_limitflag()
5.10.13  corrected autosimp where seriesflag is set,  had t instead of p.
5.13.13  corrected autosimp where seriesflag is decremented, lines 1926--27.
5.31.13 modified determine_trigexpandflag to set bit 5 when only two trig functors occur.
6.2.13  added some series operations to context_sensitive
6.5.13  changed 14 to 16 in in_loop.
6.6.13  added code to stop when done in TESTCONVERGENCE.
6.6.13  added code involving ALREADY to get autosimp to leave series alone when marked ALREADY in series problems
        corrected contains_unconvergent_series, and the condition for calling it.
12.4.14  added code to prevent calling TestTermSelection on a graph document while autotesting.
12.8.14  more such code line 1915
2.24.24  removed include readinit
5.7.24  removed set_whichpath, which is never called.
7.25.24  added clear_error_buffer() at the end of one_step, to suppress
error messages generated in auto mode.
8.2.24   replaced loop_warning by report_err
9.27.24  replaced report_err by printf.
10.1.24  added _implicit_diff at line 924 to stop setting the problemtype to SIMPLIFY before
         the equation has been differentiated.
1.30.25  changed at_aux; now it copes with different constant args
2.16.25  deleted unused function adjust_trigexpandflag
*/

#include <assert.h>
#include <string.h>
#include <stdlib.h>   /* abs  */
#include <stddef.h>
#include <stdio.h>   /* printf */
#include <math.h> 
#include "globals.h"
#include "graphstr.h"
#include "display.h" 
#include "mpdoc.h"  /* PDOCDATA is passed to one_step */
#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 "automode.h"
#include "eqn.h"
#include "mtext.h"
#include "optable.h"
#include "nextline.h"
#include "symbols.h"
#include "nextline.h"  /* specialop       */
#include "cflags.h"    /* get_controldata */
#include "mplimits.h"    /* LIMITAND        */
#include "factor.h"    /* NFACTOROPS      */
#include "errbuf.h"
#include "autosimp.h"  /* set_pathlength etc. */
#include "pvalaux.h"   /* iseven           */
#include "cancel.h"
#include "debug.h"     /* DEBUG_MATHPERT, loop_warning */
#include "bigrect.h"   /* BIGRECT; sstep needs it */
#include "showstepfocus.h"  /* ShowStepFocus,InitializeShowStep  */
#include "tdefn.h"
#include "complex4.h"    /* is_polar_complex    */
#include "preops.h"     /* set_trigpolyflag    */
#include "deval.h"
#include "simpprod.h"   /* expandable          */
#include "islinear.h"   /* islinear            */
#include "preprod.h"    /* sqrt_exp_aux        */
#include "dcomplex.h"   /* needed by ceval     */
#include "ceval.h"      /* complexnumerical    */
#include "domain.h"     /* contains_defined_variables */
#include "binders.h"
#include "autosimp.h"
#include "autotest.h"
#include "activedoc.h" /* GetActiveDoc */
#include "series.h"

#define UP  1
#define DOWN 2
static void reset_limfractflag(void);
static int set_trig_aux(term,int *,term *,unsigned short *);
static int autosimp(term, term, int, term *, char*,operation *);
static void protect_exponents(term *t, int flag);
static void strip_order(term *);
static int strong_equals(term, term);
static int already_solved(term);
static int collectlogs_will_work(term t, term x);
static int mixed_roots(term,term);
static unsigned short inverse(unsigned short);
static int equal_by_renaming(term t, term s);
static int equal_mod_constofint(term t, term s);
static void adjust_radicalflag(term t);
static int different_roots(term t);
static int power_of_eigenvariable(term t);
static int select_arg(term t);
static int maxtrigargs(term t, term *ans);
static int contains_other_trig(term t);
static int contains_trig_fraction(term t);
static int count_logs(term t);
static int contains_expandable_denom(term t);
static int simple_enough(term t);
static int introduce_fractional_exponents(term t);
static int contains_same_integral(term t);
static int contains_hypertrig2(term t);
static int arf_aux2(term t);
static int transitionop(actualop code);
static int initial_focus(term, term *);
static int at_aux(term t, int nvariables, term *varlist, term *scratch);
/*__________________________________________________________*/
/* The following are for use by autosimp, to
keep track of the context of a particular simplification.
*/
#define MAXPATH 800
static unsigned short path[MAXPATH];
static int pathlength;

void set_pathlength(int k)
{ pathlength = k;
}

int get_pathlength(void)
{ return pathlength;
}

unsigned short * get_path(void)
{ return path;
}
/*__________________________________________________________________________*/
/* The following are for use by ShowStep.   When an operation used by
automode has a larger focus than ShowStep should select, it must call
set_pathtail and pass it a 0-terminated "string" of unsigned shorts,
giving the path to the ShowStep focus from the automode focus.  If the
operation ShowStep should select is different from the one automode uses,
that operation should be passed to SetShowStepOperation.
Normally the 'tail' will be short;
only in case of arithmetic or polyvalop might it be long, and then it doesn't
matter if we cut it off (and select a larger subterm).  So the tail will be
truncated at MAXTAIL.  In practice it will never exceed 10, let alone 100.
   If the operation is one that will be used in term selection mode
as a do-with, rather than a do-to operation, then the operation must call
SetShowStepArg(arg), passing the argument that automode has selected.
This should be done only if the arg is a subterm of the active line so
it can be selected in term selection mode.
*/

static unsigned short savepath[MAXPATH];
static unsigned short pathtail[MAXTAIL];
static unsigned short ShowStepPath[MAXPATH+MAXTAIL];
static actualop ShowStepOperation;
  /* the operation ShowStep should select on the Term Selection Menu */
static term ShowStepArg;
static term ShowStepArg1;
static term ShowStepArg2;

typedef struct
  { unsigned short *path;
    term arg;
    term arg1;
    term arg2;
    actualop op;
  } SaveShowStepStruct;

typedef struct ssl
  { SaveShowStepStruct data;
    struct ssl *next;
  } SaveShowStepStructList;

static SaveShowStepStructList *SaveShowStep;
/*_____________________________________________________________________*/
static int plength(unsigned short *p)
/* like strlen but for zero-terminated arrays of unsigned shorts */
{ unsigned short *marker = p;
  int count=0;
  if(!marker)
     return 0;
  while(*marker)
     { ++marker;
       ++count;
     }
  return count;
}

/*________________________________________________________________________*/
void SaveShowStepState(void)
/* temporarily store the ShowStep state so it can be retrieved
   later by RestoreShowStepState. Push a value onto the SaveShowStep list.
*/
{ SaveShowStepStruct s;
  SaveShowStepStructList *p = SaveShowStep;
  s.path = callocate(plength(pathtail)+1,sizeof(unsigned short));
  if(s.path == NULL)
     { nospace();
       return;
     }
  pathncopy(s.path,MAXTAIL,pathtail);
  s.arg = ShowStepArg;
  s.arg1 = ShowStepArg1;
  s.arg2 = ShowStepArg2;
  s.op = ShowStepOperation;
  ResetShowStep();
  pathtail[0] = 0;
  SaveShowStep = (SaveShowStepStructList *) mallocate(sizeof(SaveShowStepStructList));
  if(SaveShowStep == NULL)
     { nospace();
       return;
     }
  SaveShowStep->data = s;
  SaveShowStep->next = p;
}
/*________________________________________________________________________*/
void RestoreShowStepState(void)
/* Restore the values of static globals saved by SaveShowStepState by
popping a value off the SaveShowStep list.
*/
{ SaveShowStepStructList *p = SaveShowStep;
  SaveShowStepStruct s;
  if(SaveShowStep == NULL)
     return;
  s = SaveShowStep->data;
  SaveShowStep = SaveShowStep->next;
  pathncopy(pathtail,MAXTAIL,s.path);
  free2(s.path);
  ShowStepArg = s.arg;
  ShowStepArg1 = s.arg1;
  ShowStepArg2 = s.arg2;
  ShowStepOperation = s.op;
  free2(p);
}
/*________________________________________________________________________*/
void save_path(void)
{ pathcopy(savepath,path);
}

/*________________________________________________________________________*/
unsigned short * GetShowStepPath(void)
/* savepath holds the path to the automode focus;
to this we must append pathtail.  The path in savepath may
end in either a functor or an arity. If it ends in a functor,
that functor should be dropped.  Pathtail begins with
the functor of the automode focus and ends with an arity,
(so it is of even length). This function returns a path which
ends in an arity, so the length is even and the null terminator
comes on an even index.
*/
{ unsigned short *marker;
  int length;
  pathcopy(ShowStepPath,savepath);
  marker = ShowStepPath;
  while(*marker)
     ++marker;
  length = (int) (marker-ShowStepPath);
  if(length & 1)
     *(marker-1) = 0;
  pathcat(ShowStepPath,pathtail);
  return ShowStepPath;
}
/*_______________________________________________________*/
void set_pathtail(unsigned short *tail)
/* pathtail holds what must be appended to the automode focus to reach
the ShowStep focus, and 'code' is used to set the operation
ShowStep should select, i.e. the one the user could apply to the
ShowStep focus to imitate the AutoStep result.
   At exit, the argument tail should begin with the functor of the automode
focus and follow it with the arg number of the first arg, starting
from 1, and then alternate functors and arg numbers until the desired
ShowStep focus is reached, ending in an arg number, so it is of
even length, but then it must terminate in a 0, so including the
zero it will be of odd length.
*/
   { if(tail != NULL)
        pathncopy(pathtail,MAXTAIL,tail);
     else
        pathtail[0] = 0;  /* reset */
   }
/*_______________________________________________________*/
unsigned short * get_pathtail(void)
   { return pathtail;
   }
/*_______________________________________________________*/
void SetShowStepOperation(actualop code)
   { ShowStepOperation = code;
   }
/*_______________________________________________________*/
actualop GetShowStepOperation(void)
   { return ShowStepOperation;
   }
/*_______________________________________________________*/
void SetShowStepArg(term t)
/* ShowStepArg holds the arg used by ShowStepOperation when it is
   a do-with rather than a do-to operation, for example add something
   to both sides of an equation.  It will be set to an ILLEGAL term
   by show_step before show_step calls cogitate.
*/
   { ShowStepArg = t;
   }
/*_______________________________________________________*/
void ResetShowStep(void)
/* called by ssolve to undo any SetShowStepOperation
and SetShowStepArg calls made by factoring or equation
solving operations called by during ssolve.   Should it
also be called by lpt?  At present it is not.  So, if
lpt calls any operations except through ssolve, it may
leave spurious ShowStepArgs or ShowStepOperations.
*/
{ term arg1,arg2;
  SETFUNCTOR(arg1,ILLEGAL,0);
  SETFUNCTOR(arg2,ILLEGAL,0);
  SetShowStepOperation(NULL);
  SetShowStepArg(arg1);
  SetShowStepArgs(arg1,arg2);
  pathtail[0] = 0;
}

/*_______________________________________________________*/
void SetShowStepArgs(term t, term s)
/* ShowStepArgs holds the index arg and the normal arg
   used by ShowStepOperation when it needs an index arg,
   for example add s to both sides of equation t.
   Both StepArg1 and ShowStepArg2 will be set to ILLEGAL terms
   by show_step before show_step calls cogitate.
*/
{ ShowStepArg1 = t;
  ShowStepArg2 = s;
}

/*_______________________________________________________*/
term GetShowStepArg(void)
{ return ShowStepArg;
}
/*_______________________________________________________*/
void GetShowStepArgs(term *a, term *b)
{ *a = ShowStepArg1;
  *b = ShowStepArg2;
}


/*________________________________________________________________*/
void pathcopy(unsigned short *dest, unsigned short *source)
/* like strcpy but for unsigned shorts */
{ unsigned short *marker = source;
  while(*marker)
     { *dest = *marker;
       ++dest;
       ++marker;
     }
  *dest = 0;  /* add the terminating null */
}
/*________________________________________________________________*/
void pathcat(unsigned short *dest, unsigned short *source)
/* like strcat but for unsigned shorts */
{ unsigned short *marker;
  while(*dest)
     ++dest;
  /* Now copy source */
  marker = source;
  while(*marker)
     { *dest = *marker;
       ++dest;
       ++marker;
     }
  *dest = 0;  /* add the terminating null */
}

/*________________________________________________________________*/
void pathncopy(unsigned short *dest, int n, unsigned short *source)
/* like strncpy but for unsigned shorts */
{ unsigned short *marker = source;
  int counter = 0;
  while(*marker && counter < n-1)
     { *dest = *marker;
       ++dest;
       ++marker;
       ++counter;
     }
  *dest = 0;  /* add the terminating null */
}

/*_________________________________________________________*/
/* push and pop adjust the record of the path to the current term */
/* path is a string of the functors and arguments between t and p
when autosimp is called.  Arg numbers are recorded starting from 1,
not from 0.   When dir==DOWN, path does not include FUNCTOR(p);
and functors are followed an arg number, starting from 1, to
tell which arg we took from there (but no other functors carry arg numbers).
When dir == UP, the path DOES include FUNCTOR(p), but terminates
there and is not followed by an arg number. */

void push(unsigned short f)
{ if(pathlength == MAXPATH-1)  /* no more room */
     { pathlength = -1;         /* signal not to use path any more */
       return;
     }
  path[pathlength] = f;
  ++pathlength;
  path[pathlength] = '\0';
}

void pop(void)
{ if(pathlength < 0)  /* path was exhausted */
    return;
  path[pathlength-1] = '\0';
  if(pathlength==0)
     assert(0);
  --pathlength;
}
/*____________________________________________________________*/
static int is_monomial(term t)
/* return 1 if t is a monomial, i.e. an atom, a constant
power of an atom, a constant times one of those, or a constant. */
{ unsigned short n;
  int i;
  term u;
  if(constant(t))
     return 1;
  if(ISATOM(t))
     return 1;
  if(FUNCTOR(t) == '^' && constant(ARG(1,t)) && ISATOM(ARG(0,t)))
     return 1;
  if(FUNCTOR(t) == '*')
     { n = ARITY(t);
       for(i=0;i<n; i++)
          { u = ARG(i,t);
            if(constant(u))
                continue;
            if(i < n-1)
               return 0;
            if(ISATOM(u))
               return 1;
            if(FUNCTOR(u) == '^' && is_monomial(u))
               return 1;
          }
     }
  return 0;
}

/*____________________________________________________________*/
static int monomial_logs(term t)
/* Return 1 if the argument of every log in t is a monomial,
or if t has no logs. */
{ unsigned short n;
  int i;
  unsigned short f =  FUNCTOR(t);
  if(ATOMIC(t))
     return 1;
  if(f == LOG || f == LN)
     return is_monomial(ARG(0,t));
  if(f == LOGB)
     return is_monomial(ARG(1,t));
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(!monomial_logs(ARG(i,t)))
          return 0;
     }
  return 1;
}
/*____________________________________________________________*/
static int collectlogs_will_work(term t, term x)
/* Return 1 if t is an equation or sum, and each summand
is a constant part times a log, or is a constant.  Thus
it will return 1 on log 2 + 3 log x, but 0 on x + log x.
If there are no logs let it return 1.  Called only when
solving equations; x is the variable being solved for.
*/
{ unsigned short n;
  int i;
  term c,s;
  unsigned short g;
  unsigned short f =  FUNCTOR(t);
  if(f == '=' || f == '+')
     { n = ARITY(t);
       for(i=0;i<n;i++)
          { if(!collectlogs_will_work(ARG(i,t),x))
               return 0;
          }
       return 1;
     }
  if(f == LOG || f == LN || f == LOGB)
     return 1;
  if(f == '*')
     { twoparts(t,x,&c,&s);
       if(ONE(s))
          return 1;
       if(ISATOM(s))
          return 0;
       g = FUNCTOR(s);
       if(g == LN || g == LOG || g == LOGB)
          return 1;
     }
  if(f == '-')
     return collectlogs_will_work(ARG(0,t),x);
  if(!contains(t,FUNCTOR(x)))
     return 1;
  return 0;
}


/*____________________________________________________________*/
static int kinds_of_logs(term t)
/* return 0,1,2,or 3, the number of functors LN, LOG, LOGB
occurring in t */

{ unsigned short f[3];
  int k,i;
  f[0] = LN;
  f[1] = LOG;
  f[2] = LOGB;
  k = 0;
  for(i=0;i<3;i++)
     { if(contains(t,f[i]))
          ++k;
     }
  return k;
}
/*____________________________________________________________*/
static void nonconstantlogs(term t, term x,int *n, int *outside)
/* return in *n the number of LN, LOG, or LOGB terms with
nonconstant argument.  Return in *outside the number of
occurrences of x outside of any LN, LOG, or LOGB in t.
x is presumed to be the eigenvariable.
*/
{ unsigned short f = FUNCTOR(t);
  unsigned short m;
  int i;
  int count,count2,a,b;
  if(f == LN || f == LOG)
     { *outside = 0;
       *n = constant(ARG(0,t)) ? 0 : 1;
       return;
     }
  if(f == LOGB)
     { *outside = 0;
       *n = constant(ARG(1,t)) ? 0 : 1;
       return;
     }
  if(equals(t,x))
     { *outside = 1;
       *n = 0;
       return;
     }
  m = ARITY(t);
  count = count2 = 0;
  for(i=0;i<m;i++)
     { nonconstantlogs(ARG(i,t),x,&a,&b);
       count += a;
       count2 +=b;
     }
  *n = count;
  *outside = count2;
}

/*____________________________________________________________*/
static void determine_logcollectflag(term t, int problemtype)
/* set the global variable logcollectflag correctly for
the problem and problemtype.  Namely, to 1 if we are solving
equations, unless the arguments of all logs are monomials,
and only one kind of logs occurs, i.e. not both LOG and LN.
Or, also set it to zero if only ONE log term occurs in
t and x occurs both inside and outside this log term.
If there are no logs, of course it doesn't matter how you
set logcollectflag.
*/
{ int k;
  int n, outside;
  if(!SOLVETYPE(problemtype))
     return;   /* leaving logcollectflag as set by determine_radicalflag */
  k = kinds_of_logs(t);
  if(k != 1)
     { set_logcollectflag(1);
       return;
     }
  if(collectlogs_will_work(t,get_eigenvariable()))
     { set_logcollectflag(1);
       return;
     }
  if(monomial_logs(t))
     { set_logcollectflag(0);
       return;
     }
  nonconstantlogs(t, get_eigenvariable(), &n,&outside);
  if(n==1 && outside)
     set_logcollectflag(0);
  else
     set_logcollectflag(1);
  return;
}

/*____________________________________________________________*/
/* Some variables have to be restored by undo */
/* See cflags.c for discussion and declaration of these variables:
factorflag, checksolutionsflag, comdenomflag, expandflag. */


/* The following variables are reset for each line and so do not
have to be reset by undo. */

static int whichpass;    /* for identities, equations, indexed sums,
                     and induction proofs, if we don't
                     solve it, autosimp is called repeatedly with
                     different parameters.  This counts the 'passes'
                     through autosimp.  It is initialized by one-step
                     and is used only by auto mode.  */
                     

int get_whichpass(void)
{ return whichpass;
}

/* trigexpandflag is initialized like this:
If bit 0 is 1, not all trig functions have the
same argument (or problemtype > LIMITS)
If bit 1 is 1, only ONE trig function occurs;
if bit 2 is 1, only tan and cot occur;
If bit 3 is 1, all trig functions occur raised to even powers;
If bit 4 is 1, only tan, cot, and sec occur;
If bit 5 is 1,  only two trig functions occur
If bit 8 is 1, only csc and cot occur.
If bit 12 is 1, only sin and cos occur.
Used like this:
If bit 0 is 1, then automode tries to reduce all trig expressions to atomic
arguments, using double angle, sum, and half-angle formulas.
If bit 2 is 1, cot => 1/tan will be used.
If bit 3 is 1 and bit 0 is 0, make a substitution, e.g. u = sin x.
If bit 4 is 1, tan and sec will not be rewritten to sin and cos.
If bit 8 is 1, cot and csc will not be rewritten.
bits 4 and 12 are used in autoeqn to control eliminatesinsq, eliminatetansq, etc.
Problemtype TRIGEXPAND causes set_control_flags to set bit 0 to 1
*/

int inpowerflag = 0;     /* incremented when we pass into an exponent,
                            decremented when we come out.  */
static term lastbase = {ILLEGAL,0,0,0}; /* save the base of a power so it's
                            available while processing the exponent. */

int innumflag = 0;      /*  are we in a numerator? */
int indenomflag = 0;    /*  are we in a denominator? */
int inlogflag = 0;      /*  are we inside a logarithm? */
int insqrtflag = 0;     /*  are we inside a sqrt?  */
int inrootflag = 0;     /*  are we inside a root?  */
int infractexpflag = 0; /*  are we inside the base of a fractional exponent? */
/*______________________________________________________________________*/
int summandflag = 0;   /*  true while working on summands of a sum. Set
                           when we pass + on the way down, unchanged
                           passing -, left ^,  or * going down, set to zero
                           passing anything else going down, or at atomic terms.
                           Therefore doesn't have to be set coming up.
                        */
/* infractionflag is now maintained locally in polyval.c,
   because polyval has to use it.  It is incremented when we pass into a
   fraction, decremented when we come out, so we can tell
   if we are in a fraction or not.  However, if we pass into a
   trig functor or logarithm, it is saved for
   restoration afterwards and reset to zero, so
   that  in a/sin(u), when processing u we have
   infractionflag =0, so that e.g. common denoms
   will not be used on u as they would be on a/u,
   since common denoms are used when either
   comdenomflag or infractionflag is nonzero.  */

static int limitflag = 0;    /* set when passing into a limit term */
static int difflag = 0;      /* set when passing into a derivative */
static int intflag = 0;      /* set when passing into an integral  */
static int indexsumflag = 0;  /* set when passing into an indexed sum */
static int indexproductflag = 0;  /* set when passing into an indexed product */
static int seriesflag = 0;  /* set when passing into an infinite series */
static int limfractflag = 0;  /* this is a real hack:  when we are inside both
                            a limit and a fraction, this tells us by being
                            1 or -1 whether the limit or the fraction was
                            entered last (i.e. is innermost).  We need this to
                            control secrule, tanrule, etc. properly so they
                            work in fractions of limits but not  in limits of
                            fractions.  */
static actualop lastcode;
/*___________________________________________________________*/
/* The following functions allow polyvalop (and postops.c, preops.c)  to
access these flags: */
int get_innumflag(void)
{ return innumflag;
}

int get_indenomflag(void)
{ return indenomflag;
}

int get_intflag(void)
{ return intflag;
}

int get_indexsumflag(void)
{ return indexsumflag;
}

 
int get_limitflag(void)
{ return limitflag;
}

int get_limfractflag(void)
{ return limfractflag;
}

int get_inpowerflag(void)
{ return inpowerflag;
}

int get_insqrtflag(void)
{ return insqrtflag;
}

int get_inlogflag(void)
{ return inlogflag;
}
int get_inrootflag(void)
{ return inrootflag;
}

int get_infractexpflag(void)
{ return infractexpflag;
}

int get_seriesflag(void)
{ return seriesflag;
}

int get_difflag(void)
{ return difflag;
}

term get_lastbase(void)
{ return lastbase;
}
/*____________________________________________________________________*/
static void outaux(term t, term *next)
/* called below; make *next a copy of t except if selected_equation
is nonzero, put t in only as one arg of *next, copying the other args
from history(currentline). If selected_equation is negative,
mark that arg using SET_SELECTED, so it will be the only one displayed.
*/

{ int i,k;
  int currentline = get_currentline();
  unsigned short n = ARITY(history(currentline));
  int selected_equation = get_selected_equation();
  if(selected_equation)
     { k = abs(selected_equation);
       *next = make_term(FUNCTOR(history(currentline)),n);
       for(i=0;i<n;i++)
          { if(i != k-1)
               { copy(ARG(i,history(currentline)),ARGPTR(*next) + i);
                 erasecolors(ARGPTR(*next) + i);
               }
            else
               { if(selected_equation < 0)
                    SET_SELECTED(t);  /* prevent display of other args */
                 ARGREP(*next,i,t);
               }
          }
     }
  else
     *next = t;
}

/*____________________________________________________________________*/
 int contains_unconvergent_series(term t)
{ int i;
  unsigned short f = FUNCTOR(t);
  unsigned short n = ARITY(t);
  if(ATOMIC(t))
     return 0;
  if(f == SUM  && (ISINFINITE(ARG(3,t)) || ISINFINITE(ARG(2,t))))
     return CONVERGENT(t) ? 0 : 1;
     // no need to examine inside the sum
  if(f == SUM)
     return 0;   // a finite sum.  No need to look inside
  for(i=0;i<n;i++)
     { if(contains_unconvergent_series(ARG(i,t)))
          return 1;
     }
  return 0;
}
 

/*____________________________________________________________________*/

int one_step(PDOCDATA pDocData,
                      term *next,  char *reason, operation *op)
/* pDocData points to the current document. */                       
/* select an operator and apply it, producing the result *next */
/* searches for a 'focus' (node at which to apply an op) like exec_aux,
but also must search for an op to apply */
/* Must return *next in fresh space, disjoint from history(currentline),
so that undo can destroy history(currentline) */
/* Return zero for success (one step taken) and 1 if history(currentline)
can't be simplified by automode */
/* Normally works on history(currentline), but in case that was obtained
by an operator satisfying 'specialop',  backs up to the last line not
obtained this way to get its input. */
/* If flag is nonzero, select only WELLKNOWN operators */

{ term t,u,p;
  int i,err;
  int nloops = 0;
  int stopflag = 0;
  int nvariables = get_nvariables();
  varinf *varinfo = get_varinfo();
  unsigned short f;
  int currentline = get_currentline();
  int activeline = currentline;
  int probtype = get_problemtype();
  int currenttopic = get_currenttopic();
  int selected_equation = get_selected_equation();
  int save_radicalflag;
  int saveproblemtype,expanddenomflag,calcflag=0;
  #if 0
  int autotestflag;
  int saveeigen = get_eigenindex();
  #ifndef PRODUCTION
  autotestflag = autotesting();  /* are we autotesting? */
  #else
  autotestflag = 0;
  #endif 
  if(autotestflag)
     { /* yes, we are autotesting */
       --autotestflag;
       /* Now autotestflag tells whether to check the Term Selection menu */
       original_probtype = probtype;
     }
#endif
  InitializeShowStep();   /* this has to be done whether or not we are
     autotesting ShowStep, to prevent old values of pathtail from accidentally
     influencing what one_step does.  Those values are checked when an operator
     succeeds and might apply a second time somewhere to the right; but if
     pathtail is set, the second application will be rejected, which would be
     unjustified if the pathtail value causing the rejection is old; moreover,
          this can make the autotest solution different from the automode solution if
     we don't call InitializeShowStep here. */
  set_trigpolyflag(0);    /* it's set, for trig identities and equations, when
     pre_ops handles the '=' sign.  We set it zero here so it won't accidentally
     have a nonzero value on problemtypes other than TRIG_IDENTITY. */
  set_hflag(0);           /*  similarly for hflag */
  clear_error_buffer();
  clear_comment_buffer();
  activeline = get_activeline();
  if(activeline < currentline)
     set_currentline(activeline);
  reset_maxscope();  /* nextline.c */
  u = history(activeline);
  if(selected_equation < 0 &&
     (FUNCTOR(u) == OR || (FUNCTOR(u) == AND && !interval_as_and(u))) &&
     ARITY(u) >= abs(selected_equation)
    )
     u = ARG(abs(selected_equation)-1,u);
  else if(selected_equation)
     /* even if we were working on a certain equation last line,
        some may have been rejected, and it may even be that u is
        now only a single equation rather than an OR of equations.
     */
     set_selected_equation(0);
  copy(u,&t);
  whichpass = 0;
  erasecolors(&t);
  protect_exponents(&t,0);
  limitflag = limfractflag = insqrtflag = inpowerflag = infractexpflag =
      innumflag = indenomflag = difflag = intflag = indexsumflag =
      indexproductflag = seriesflag = inlogflag = 0;
  set_infractionflag(0);
  determine_logcollectflag(t,probtype);
  f = FUNCTOR(t);
  inrootflag = 0;
  determine_trigexpandflag(t);
  for(i=0;i<nvariables;i++)
     SETFUNCTOR(varinfo[i].locus,ILLEGAL,0);
  path[0] = '\0';
  pathlength = 0;
  if(probtype == TRIG_IDENTITY && f == '=' &&
     equals(ARG(0,t),ARG(1,t))   /* we're done already, don't simplify
                                    the args */
    )
     return 1;
  if(probtype == TESTCONVERGENCE && contains(t,SUM) && !contains_unconvergent_series(t))
     { if (!get_pending())  // nothing left on pending stack;  
           /* stop when we're done! */
           return 1;
     }
  saveproblemtype = probtype;
  /* Adjust the problemtype to SIMPLIFY in calculus when the calculus is
          already done.  The true problemtype will be restored before returning. */
  if(probtype >= LIMITS &&
     probtype != POWERSERIES &&  // in POWERSERIES we want to not reset the problemtype
     probtype != MINMAX &&
     !((probtype == ADDSERIES || probtype == TESTCONVERGENCE || probtype == POWERSERIES) && contains(t,SUM)) &&
     !contains_calc(t) && !get_pending()
    )
     { if(probtype == TESTCONVERGENCE &&
          FUNCTOR(t) == LE
         )
          { probtype = INEQUALITIES;
            set_problemtype(INEQUALITIES);
            /* this happens on topic _comparison_test */
          }
       else if(currenttopic != _comparison_test && currenttopic != _implicit_diff)
          { set_problemtype(SIMPLIFY);
            probtype = SIMPLIFY;
            calcflag = 1;  /* so we can stop excess simplification */
          }
     }
  if(probtype == ADDSERIES && !contains(t,SUM))
     { set_problemtype(SIMPLIFY);
       probtype = SIMPLIFY;
       calcflag = 1;
     }
  if(probtype==INTEGRATION && !contains(t,INTEGRAL) &&
     contains(t,LIMIT) && !get_pending()
    )
     { /* an improper integral in which the integration is done but the
          limits still need to be evaluated. */
       set_problemtype(LIMITS);
       probtype = LIMITS;
       calcflag = 1;
     }
  if(FUNCTOR(t) == '=' &&
     get_problemtype() == INTEGRATION &&
     contains(t,INTEGRAL) &&
     contains_same_integral(t)
    )
     /* an integration by parts problem after equatetoproblem has been
        used; the same integral occurs on both sides. */
     set_problemtype(SOLVE_EQUATION);

  if(SOLVETYPE(probtype) && probtype != MINMAX &&
     (f == OR || f == '=' || f == LE || f == '<' || f == GE || f == '>' || f == MULTIPLICITY) &&
     get_selected_equation() == 0  /* otherwise we stop when the selected eqn is solved */
    )
     { if(already_solved(t) &&
          /* Next line says for inequalities, either the solution has been
             marked CHECKED by explicitdomain, or there are no assumptions
             which could modify the solution by explicitdomain. */
          ((f != LE && f != '<' && f != GE && f != '>') || (CHECKED(t) || get_nextassumption() == 0)) &&
          (!get_checksolutionsflag() || CHECKED(t)) &&
          ! contains_defined_variables(t)  /* don't stop if t
            still contains let-defined variables, they
            must be unwound */
         )
          { set_problemtype(saveproblemtype);
            return 1;
          }
       /* If t is an OR and only some of the equations are already solved,
          already_solved PROTECTs them, but returns 0 so we can go on
          to solve the rest.  (checkroot will work on the OR, so the
          protection doesn't stop it.) */
       /* Even if all the equations are solved, if checksolutionsflag
          is nonzero we have to go on, in order to get checkroot called. */
     }
  save_radicalflag = get_radicalflag();
  if(save_radicalflag == 0 || save_radicalflag == -2)
     adjust_radicalflag(t);  
  if(whichpass == 0 && 
     (probtype == LIMITS || probtype == INTEGRATION || probtype == DIFFERENTIATE) &&
     probtype != RELATED_RATES &&
     currenttopic != _logarithmic_differentiation  &&
     !initial_focus(t,&p)
    )
     /* Select an initial focus: start with the leftmost integral, derivative, or limit, if one occurs,
        but include the coefficient of that term if it occurs in a product.  */  
     { err = autosimp(t,p,DOWN,next,reason,op);  /* select and use one operator */
       pathlength = 0;
       path[0] = 0;
     }
  else
     err = autosimp(t,t,DOWN,next,reason,op);  /* select and use one operator */
  set_radicalflag(save_radicalflag);
  while(err == 2)   /* op produced a loop */
    /* We test for immediate loops when an operator is applied, but
       autosimp can return 2 for a loop created when exec_aux makes a
       SECOND application of the operator. Such loops get handled here. */

     { actualop saveit[4];
       if(nloops > 3)
          /* Four DIFFERENT operators all produce a loop?? Inconceivable! */
          { err = 1;  /* But if it should happen, give up.
                         MathXpert's done with this problem. */
            break;
          }
       saveit[nloops] = lastcode;
       ++nloops;
       inhibit(lastcode);  /* don't choose the same operator again */
       err = autosimp(t,t,DOWN, next,reason,op);
       if(err != 2)
          { int j;
            for(j=0;j<nloops;j++)
               release(saveit[j]);
          }
     }
  if(err &&
     (strip_protections(&t) || inhibited(arithmetic))
    )
     { /* no more operators apply, but maybe t contains protected terms;
          these should now be unprotected and t should be sent through again.
          Even if the same protected terms get created again, this doesn't
          cause a loop if autosimp still does nothing.

          Protected equations, however, remain
          protected.  I think this only happens for equations created by
          trigeqn_aux, such as x = pi_term/2 + 2k pi.  We don't want commondenom
          used on such equations.  Also, numerical expressions
          remain protected, as the purpose of protecting them was to prevent
          useless attempts to simplify them.
       */

        path[0] = '\0';
        pathlength = 0;
        if(inhibited(arithmetic))
           kill_inhibitions();  /* limrecip leaves arithmetic and polyvalop inhibited
                                   when one of the limits is zero. We can end up with -0
                                   which does not simplify, without this line. */
        err = autosimp(t,t,DOWN,next,reason,op);
     }
  if(err)   /* no more operators are applicable */
     { if((SOLVETYPE(probtype) || probtype == LINEAR_EQUATIONS) && get_selected_equation() < 0)
          { /* finished solving the selected equation  */
            /* so don't just give up, call showalleqns */
            /* This can't be done in postops since showalleqns works
            on the toplevel OR, but when an equation is selected,
            automode never touches that toplevel OR, at least until
            this code. */
            copy(history(activeline),&t);
            /* necessary to make a copy so showalleqns doesn't UNSELECT
            the args of history(activeline) */
            err = showalleqns(t,zero,next,(char *) reason);
            assert(!err);
            assert(get_selected_equation()==0);
            /* not necessary to call outaux since we already made a copy */
            set_problemtype(saveproblemtype);
            return 0;
          }
       if(
          ( /* an unsolved identity or induction problem */

           (probtype == INDUCTION || probtype == TRIG_IDENTITY)
           && (
                (FUNCTOR(t) == '=' && !equals(ARG(0,t),ARG(1,t)))
                || (FUNCTOR(t) != '=' && !equals(t,trueterm))
              )
          ) ||
          FUNCTOR(t) == SUM  || /* an unevaluated indexed sum */
          ( currenttopic == _polar_form && !is_polar_complex(t))
         )
          { if(probtype < LIMITS &&
                !get_comdenomflag() /* turn on common denominators and try again */
                              /* but don't work so hard simplifying the result
                                 of a derivative or integral!  it usually isn't
                                 worth it to put a sum of fractions resulting
                                 from a sum or integral over a common denom,
                                 because nothing simplifies anyway. */
              )
               { set_comdenomflag(1);
                 path[0] = '\0';
                 pathlength = 0;
                 whichpass = 1;
                 err = autosimp(t,t,DOWN,next,reason,op);
               }
            if(err && !get_factorflag() && get_problemtype() != TRIG_IDENTITY)
               { /* Factoring rarely helps with identities, it just tends to
                    make loops, which by loop detection are averted but then
                    produces solutions terminated due to excess length.
                 */
                 set_factorflag(0x111);  /* factor everything */
                 set_expandflag(0);
                 set_comdenomflag(1);
                 path[0] = '\0';
                 pathlength = 0;
                 whichpass = 2;
                 err = autosimp(t,t,DOWN,next,reason,op);
               }
            if(err && probtype == TRIG_IDENTITY && contains_hypertrig(t))
               { whichpass = 2;  /* this allows sinhdef, coshdef, tanhdef to be used */
                 if(contains_hypertrig2(t))
                    { inhibit(sinhdef);
                      inhibit(coshdef);
                      err = autosimp(t,t,DOWN,next,reason,op);
                      release(sinhdef);
                      release(coshdef);
                    }
                 else
                    err = autosimp(t,t,DOWN,next,reason,op);
               }
            if(err && !get_expandflag())
               { set_factorflag(0);
                 set_polyvalfactorflag(0); /* don't even contentfactor */
                 set_expandflag(0x0111);
                 path[0] = '\0';
                 pathlength = 0;
                 whichpass = 3;
                 err = autosimp(t,t,DOWN,next,reason,op);
               }
            if(err)
               { strip_order(&t);
                 whichpass = 4; /* this gets crossmultiply used at last */
                 set_comdenomflag(0);
                 set_expandflag(0);
                 set_factorflag(0);
                 path[0] = '\0';
                 pathlength = 0;
                 err = autosimp(t,t,DOWN,next,reason,op);
               }
          }
       if(
          (
            (
              (probtype == LIMITS || probtype == LHOPITAL || probtype == DIFFERENTIATE_FROM_DEFN) &&
               contains(t,LIMIT)
            )
            ||
            ( probtype == DIFFERENTIATE && contains(t,DIFF) )  /* can happen in higher-order diff */   
            ||
            ( probtype == INTEGRATION && (contains(t,INTEGRAL) || contains(t,DIFF)))
              /* In integration by substitution, you can have derivatives only when working out the 
                 derivative of the substitution. */
          )  
          &&
          whichpass < 2
         )
          { /* an as-yet-unsolved limit or differentiation problem */
            while(whichpass < 2)
               { ++whichpass;
                 err = autosimp(t,t,DOWN,next,reason,op);
                 /* try again, this time using quotienttopower, and rewriting
                   tan, sec, csc, cot in terms of sin and cos (if whichpass == 1)
                   or expressing hypertrig functions in terms of exponentials
                   (if whichpass == 2) or trying leading terms (if whichpass == 2)
                 */
                 if(!err)
                    break;
               }
          }
       if(currenttopic == _comparison_test && probtype == SIMPLIFY)
          { set_problemtype(TESTCONVERGENCE);
            probtype = TESTCONVERGENCE;
            /* after the second series has been evaluated, we need to
               switch the problem type back so that statefinalbound1
               or statefinalbound2 can be used.
            */
          }
       if(probtype == SIMPLIFY ||
          (probtype >= LIMITS && !contains_calc(t))
         )
          { if(simple_enough(t))
               stopflag = 1;
          }
       expanddenomflag = contains_expandable_denom(t);
       if(probtype == SIMPLIFY && !stopflag &&
          get_comdenomflag() &&
          !expanddenomflag
         )
          { set_comdenomflag(0);
            whichpass = 1;
            /* example:  (x^2+x+1)/x.  Now it will go to x+1+1/x */
            err = autosimp(t,t,DOWN,next,reason,op);
          }
       if(err && probtype == SIMPLIFY &&
          !calcflag &&  /* don't expand in the aftermath of a calculus problem */
          (expanddenomflag || !get_comdenomflag()) &&
          !get_expandflag() &&
          !stopflag &&
          !contains_defined_variables(t)
         )
          { set_factorflag(0);
            set_polyvalfactorflag(0); /* don't even contentfactor */
            set_expandflag(0x0111);
            path[0] = '\0';
            pathlength = 0;
            whichpass = 3;  /* skipping whichpass = 2 */
            err = autosimp(t,t,DOWN,next,reason,op);
          }
       if(err && probtype == RELATED_RATES)
           /* ssolve will be used now */
          { whichpass = 1;
            err = autosimp(t,t,DOWN,next,reason,op);
          }
       if(err && probtype == TESTCONVERGENCE)
           /* finishintegraltest etc. will be used now */
          { whichpass = 1;
            err = autosimp(t,t,DOWN,next,reason,op);
          }
     }
  if (err && get_nextdefn() && !get_pending())   /* let_defns are in effect, so unwind them */
     { err = unwinddefinition(t,zero,next,(char *) reason);
       if(!err)
          { code_to_op(unwinddefinition,op);
            if(activeline < currentline)
               set_currentline(currentline);
            savepath[0] = 0;
            ShowStepFocus(op);
#if 0
            if(autotestflag == 1)
               TestTermSelection(*op,saveeigen,original_probtype);
#endif
            set_problemtype(saveproblemtype);
            return 0;
          }
     }
  f = FUNCTOR(t);
  if(err &&   /* and definitions are already unwound */
     SOLVETYPE(probtype) &&
     probtype != MINMAX &&
     (f == OR || INEQUALITY(f))
    )
      { int saveproblemtype = get_problemtype();
        term p,x;
        int savefactorflag = get_factorflag();
        int savecomdenomflag = get_comdenomflag();
        x = get_eigenvariable();
        set_problemtype(SIMPLIFY);  /* so 'apart' will be used */
        set_comdenomflag(0);  /* normally under SIMPLIFY it is 1 */
        set_factorflag(0);
        if(INEQUALITY(FUNCTOR(t)))
           { if(
                (equals(ARG(0,t),x) &&
                 !contains(ARG(1,t),FUNCTOR(x))
                ) ||
                (equals(ARG(1,t),x) &&
                 !contains(ARG(0,t),FUNCTOR(x))
                )
               )
                err = autosimp(t,t,DOWN,next,reason,op);
           }
        else if (FUNCTOR(t) == OR)  /* several equations */
           { push(OR);
             for(i=0;i<ARITY(t);i++)
                { p = ARG(i,t);
                  if(!INEQUALITY(FUNCTOR(p)))
                     continue;
                  if( (equals(ARG(0,p),x) &&
                       !contains(ARG(1,p),FUNCTOR(x))
                      ) ||
                       (equals(ARG(1,p),x) &&
                        !contains(ARG(0,p),FUNCTOR(x))
                       )
                    )
                     {  push((unsigned short)(i+1));
                        err = autosimp(t,p,DOWN,next,reason,op);
                        if(!err)
                           break;
                        pop();
                     }
                }
             if(i == ARITY(t))
                pop();
           }
        set_problemtype(saveproblemtype);
        set_factorflag(savefactorflag);
        set_comdenomflag(savecomdenomflag);
      }
  if(err &&
     SOLVETYPE(probtype) &&
     probtype != MINMAX &&
     (f == OR || f == '=') &&
     contains_existentials(t) &&
     !contains_defined_variables(t)
    )
     { whichpass = 1;
       /* this will get explicitparams used */
       path[0] = '\0';
       pathlength = 0;
       err = autosimp(t,t,DOWN,next,reason,op);
     }
  if(activeline < currentline)
     set_currentline(currentline);
  if(err && contains_double(t))
     { /* evaluate numerical values to decimals */
       path[0] = '\0';
       pathlength = 0;
       whichpass = 5;
       err = autosimp(t,t,DOWN,next,reason,op);
     }
  if(err)  /* and no definitions to unwind, and solutions of
              equations have been simplified using 'apart' */
     { destroy_term(t);  /* made by copy */
       set_problemtype(saveproblemtype);
       return err;
     }
  set_problemtype(saveproblemtype);
  outaux(*next,next);
  clear_error_buffer();  // suppress error messages generated in auto mode.
  clear_comment_buffer();
  return 0;
}
/*____________________________________________________________________*/
#define MAXOPS 50
/* max number of operators ever returned by pre_ops or post_ops*/

static int autosimp(term t, term p, int dir, term *next,  char *reason,operation *op)
/* simplify t to *next, by finding and applying one operator somewhere in
the subexpression p of t (possibly at p itself).  Return the operator in *op.
The parameter dir is either UP or DOWN:
UP means the args of p have already been processed,
DOWN means they haven't been yet.  */
/* Return 0 for success, 1 for failure */

/* Precondition assumed on 'path' : when dir == DOWN, FUNCTOR(p) is
not on the path, but functors nearer the root than p are followed by arg numbers,
starting from 1 rather than 0, e.g. '/' is followed by 1 or 2; when
dir == UP, FUNCTOR(p) is on the path, except for atomic p,
but is not followed by an arg number.  Thus pathlength is even when DIR is down 
but could be odd or even going UP, according as p is not or is atomic.  
Postcondition:  path is unchanged by autosimp regardless of whether
dir is UP or DOWN.
*/

{  actualop oplist[MAXOPS];  /* possible operators to try at this node */
   actualop code;
   varinf *varinfo = get_varinfo();
   term x,savelocus;
   int savej,firstarg;
   int nops;             /* dimension of dynamic array oplist */
   int i,k,err,I;
   unsigned short j;
   term temp;
   char localbuf[80];
   int checkit;
   term q,arg;
   short savenextassumption;
   int savenvariables, saveeigenindex, saveeigen2, savenextdefn, savelimitflag;
   int savecomdenomflag = get_comdenomflag();
   int savefactorflag = get_factorflag();
   int saveexpandflag = get_expandflag();
   int savetrigexpandflag;
   int save_logcollectflag;
   int saveorderflag;
   int save_subflag;
   int savelogcollectflag = get_logcollectflag();
   int saveinfractionflag;
   int save_ringflag = get_ringflag();
   term savelastbase;
   int save_radicalflag = get_radicalflag();
   int save_fraction_factorflag;
   term savemaxscope;
   int probtype = get_problemtype();
   int savepolyvalfactorflag = get_polyvalfactorflag();
   void  *savenode;
   term *varlist;
   int nvars;
   unsigned short f = FUNCTOR(p);
   unsigned short n = ARITY(p);
   // int autotestflag;
   // int saveeigen = get_eigenindex();
#if 0
#ifndef PRODUCTION    // this allows us to not include autotest.dll with the Release version
   autotestflag = autotesting();  /* are we autotesting? */
#else
   autotestflag = 0;
#endif         
   if(autotestflag)
      { /* yes, we are autotesting */
        --autotestflag;
        /* Now autotestflag tells whether to check the Term Selection menu */
      }
#endif
   if(probtype == INDUCTION && get_nexttheorem() > 0 && get_pending() == NULL)
      return 1;   /* we're done, don't simplify the induction hypothesis */
   if(probtype == TESTCONVERGENCE 
      && (CONVERGENT(t) || DIVERGENT(t)) 
      &&  get_pending() == NULL 
      && get_currentline() > 2
     )
      return 1;  /* we're done,  don't go on simplifying the series or the bound */
   if(SERIESTYPE(probtype) && FUNCTOR(p) == SUM && ALREADY(p))
      return 1;  /* series are marked ALREADY to tell automode to leave them alone */ 

   if(PROTECTED(p))
      return 1;   /* don't touch protected terms at all; but these protections get removed
                     on subsequent passes, so the protection is temporary.  */
   if(f == '^' &&
      PROTECTED(ARG(0,p)) &&
      !FRACTION(ARG(1,p)) &&
      /* don't touch non-fractional powers of protected bases */
      /* because invisible_sub makes terms like (x^2)^2 where
         x^2 is protected, and we don't want to make it x^4.
      */
                !complexexpression(ARG(0,p))
      /* Except, we do need to simplify (a+bi)^2, and a+bi can be left
         protected by surdsimp. */
     )
      return 1;

   SETFUNCTOR(arg,ILLEGAL,0);  /* mark it as a dummy arg */
   if(dir==DOWN)
      pre_ops(p,oplist,&nops);
   else
      post_ops(p,&arg,oplist,&nops);
   assert(nops <= MAXOPS);
   savenextassumption = get_nextassumption();
   saveeigenindex = get_eigenindex();
   if(BINDING2(p))
      { nvars = get_nvariables();
        x = BOUNDVAR(p);
        setlocus(x,&savelocus,&savej,p);
        fillbinders(p);
        varlist = get_varlist();
        for(k=0;k<nvars;k++)
           { if(equals(varlist[k],x))
                { set_eigenvariable(k);
                  break;
                }
           }
      }
   else if(FUNCTOR(p) == INTEGRAL)
      /* an indefinite integral--definite integrals fell under the previous clause */
      { x = ARG(1,p);  /* the variable of integration */
        varlist = get_varlist();
        nvars = get_nvariables();
        for(k=0;k<nvars;k++)
           { if(equals(varlist[k],x))
                { set_eigenvariable(k);
                  break;
                }
           }
      }
   saveeigen2 = get_eigenindex();
   savenvariables = get_nvariables();  /* after fillbinders */
   savenextdefn = get_nextdefn();
   saveorderflag = get_orderflag();
   savenode = heapmax();
   for(i=0;i<nops;i++)
      { code = oplist[i];
        /* check if code is inhibited--we don't want to even try inhibited ops */
        if(inhibited(code) == 0)  /* it's not inhibited */
         /* then apply code to p and put the result in q, using arg if needed */
           { err =  (* code)(p,arg,&q,(char *) reason);
             if(err)
                { reset_heap(savenode);
                  set_nextassumption(savenextassumption);
                  /* remove any assumptions generated by the failed operator */
                  set_nvariables(savenvariables);  /* also any new variables */
                  set_eigenvariable(saveeigen2);
                  set_nextdefn(savenextdefn);
                }
             if(err==0)  /* the operator works; success at node p  */
                { term saveit = get_maxscope();  /* possibly used a few lines below */
                  lastcode = code;
                  code_to_op(code,op);
                  pathcopy(savepath,path);
                  ShowStepFocus(op);
                  if(FUNCTOR(p) == INTEGRAL && ARITY(p) == 4 && IMPROPER(p))
                     { if(FUNCTOR(q) == INTEGRAL)
                          SETIMPROPER(q);
                       else if(FUNCTOR(q) != LIMIT)
                          setimproper(&q);
                     }
                  err = nextline(code,arg,q,p,t,next);
                  if(err) /* for example, operator applied to half an interval but
                             not to the other half */
                     { 
                       reset_heap(savenode);
                       set_nextassumption(savenextassumption);
                      /* remove any assumptions generated by the failed operator */
                       set_nvariables(savenvariables);  /* also any new variables */
                       set_eigenvariable(saveeigen2);
                       set_nextdefn(savenextdefn);
                       InitializeShowStep();  /* reset ShowStepArg etc to illegal terms,
                              otherwise ShowStep gets confused on the next operation chosen. */
                       continue;
                     }

                  if(!specialop(arg,code))
                     err = in_loop(*next,*op);
                  else
                     err = 0;
                  if(err == 1)
                     { set_maxscope(saveit);
                       reset_heap(savenode);
                       set_nextassumption(savenextassumption);
                       /* remove any assumptions generated by the failed operator */
                       set_nvariables(savenvariables);  /* also any new variables */
                       set_eigenvariable(saveeigen2);
                       set_nextdefn(savenextdefn);
                       inhibit(code);  // 12.9.24
                       InitializeShowStep();  /* reset ShowStepArg etc to illegal terms,
                              otherwise ShowStep gets confused on the next operation chosen. */
                       continue;  /* back to the top of the for-loop, just
                          pretend the operator that causes a loop failed.
                          Some other operator hopefully will do better. */
                     }
#if 0
                  if(autotestflag == 1)
                      { // This code can be called on a graph document, from fill_in_singularities,
                        // and we don't want to call TestTermSelection then, as it will crash since 
                        // a graph document has no papyrus.
                        PDOCDATA pDocData = GetActiveDoc();
                        if(!GRAPHTYPE(pDocData->problemtype))
                            TestTermSelection(*op,saveeigen,original_probtype);
                     }
#endif
                  if(!specialop(arg,code) && !transitionop(code))
                     update_assumptions(p,q,next);
                  
                  if(*reason == '\0') /* reason string must be returned */
                     assert(0);
                  if(BINDING2(p))
                     { varinfo[savej].locus = savelocus;
                       releasebinders();
                       set_eigenvariable(saveeigenindex);
                     }
                  return 0;
                }
             else if(  (FUNCTOR(p) == '=' || FUNCTOR(p) == OR) &&
                      (void  *) code == (void  *) checkroot &&
                      err == 1
                    )
                set_checked();
             else if(err && FUNCTOR(p) == INTEGRAL &&
                     ((void  *) code == (void  *) choosesubstitution ||
                      (void  *) code == (void  *) intsub
                     )
                    )
                set_prime(p);
             if( FRACTION(p) && (void  *) code == (void  *) cancelgcd)
                set_prime(p);
           }


          /* If we get here, op doesn't apply at node p, or can't be used,
             so go back to the top of the loop and try the next op. */
      }
          /* If we get here, none of the ops returned by post_ops or pre_ops worked */
   if(BINDING2(p))
      { varinfo[savej].locus = savelocus;
        releasebinders();
        set_eigenvariable(saveeigenindex);
      }
   if(dir == UP)
      return 1;  /* failure */
   if(ATOMIC(p))  /* and dir == DOWN necessarily */
      { checkit = pathlength;
        err = autosimp(t,p,UP,next,reason,op);
        assert(checkit == pathlength);  /* monitor postcondition */
        /* since p is atomic, don't push FUNCTOR(p) onto the path */
        return err;
      }
   if(f == '/')
      { increment_infractionflag();  /* keep track of nested compound fractions */
        if(limitflag && !intflag)
           /* in improper integrals we don't want limfractflag = -1
              as it blocks the use of tan = sin/cos */
           limfractflag = -1;
      }
   else if(trigfunctor(f))  /* includes arctrig functors */
      { saveinfractionflag = get_infractionflag();
        set_infractionflag(0);
        set_comdenomflag(0);
        /* don't use common denoms inside sin, cos etc.
                          example: leave sin(x + n pi/2) alone */
        set_factorflag(0);   /* also don't try to factor inside sin, cos */
        set_polyvalfactorflag(0);
      }
   else if(f == LOGB || f == LOG || f == LN)
      { if(get_logcollectflag())
           { set_factorflag(0);  /* turn off factoring inside logs while
                                 trying to combine log terms.  In
                                 case of logs of quotients, cancelgcd
                                 will still find common factors. */
             set_comdenomflag(0);  /* also don't use common denoms when
                                     trying to collect logs, e.g.
                                     ln(1 + h/x) shouldn't go to ln((x+h)/x) */
           }
        saveinfractionflag = get_infractionflag();
        set_infractionflag(0);
        set_factorflag(0);   /* also don't try to factor inside sin, cos */
        set_polyvalfactorflag(0);
        ++inlogflag;
      }
   else if(f == LIMIT)
      { ++limitflag;
        savetrigexpandflag = get_trigexpandflag();
        save_subflag = get_substitutionflag();
        set_substitutionflag(INVISIBLESUBS);
          /* don't introduce new variables when factoring inside limits */
        temp = LIMITAND(p);
            determine_trigexpandflag(temp);
        set_logcollectflag(1);
        if(get_infractionflag())
           limfractflag = 1;
        if(mixed_roots(temp,ARG(0,ARG(0,p))))
           set_radicalflag(-1);   /* eliminate roots e.g. in sqrt(x)/root(3,x) */
      }
   else if(f == DIFF)
      ++difflag;
   else if(f == INTEGRAL || f == SUM || f == PRODUCT)
      { if(f == INTEGRAL)
           { ++intflag;
             savelimitflag = limitflag;
             limitflag = 0;
           }
        else if(f == PRODUCT)
           ++indexproductflag;
        else if(f == SUM &&
                (equals(ARG(3,p),infinity) || equals(ARG(2,p),minusinfinity))
               )
           ++seriesflag;
        else
           ++indexsumflag;
        save_subflag = get_substitutionflag();
        set_substitutionflag(INVISIBLESUBS);
         /* don't introduce new variables when factoring inside integrals */
        set_polyvalfactorflag(0); /* don't content-factor inside integrals or sums  */
        set_expandflag(0x0001);   /* expand numerators, but not denominators */
        set_factorflag(get_factorflag() &0xfffe);  /* set bit 0 to 0 so we for sure are not
                                  factoring numerators */
        savetrigexpandflag = get_trigexpandflag();
        determine_trigexpandflag(ARG(0,p));  // the integrand
      }
   else if(f == '*')
      { /* we would prefer to use distriblaw if it's going to work,
           otherwise turn on common denoms */
        int i,k,count=0;
         /* count the sums among the factors of p */
         /* Also count fractions with sums in numerator or denominator,
            also if they are factors of the numerator or denominator. */
        term v,w;
        for(i=0;i<n;i++)
           { v = ARG(i,p);
             if(FUNCTOR(v)=='+')
                ++count;
             if(FRACTION(v))
                { for(k=0;k<2;k++)
                     { w = ARG(k,v);
                       if(FUNCTOR(w) == '+')
                          { ++count;
                            break;
                          }
                       if(FUNCTOR(w) == '*' && contains_at_toplevel(w,'+'))
                          { ++count;
                                                                         break;
                          }
                     }
                }
           }
        if( count > 1 &&
            (probtype < LIMITS || !contains_calc(t))
          )
             /*  Then work HARD to simplify a product of sums;
                 try to reduce it to a rational function. But
                 don't do this in calculus problems.  And don't
                 do it if there's only ONE sum (count == 1)  */

          set_comdenomflag(1);
      }
   if(BINDING2(p))
      { x = BOUNDVAR(p);
        setlocus(x,&savelocus,&savej,p);
        fillbinders(p);
        savemaxscope = get_maxscope();
        set_maxscope(p);
        varlist = get_varlist();
        for(k=0;k<nvars;k++)
           { if(equals(varlist[k],x))
                { set_eigenvariable(k);
                  break;
                }
           }
      }
   else if(FUNCTOR(p) == INTEGRAL)
      { x = ARG(1,p);  /* the variable of integration */
        varlist = get_varlist();
        nvars = get_nvariables();
        for(k=0;k<nvars;k++)
           { if(equals(varlist[k],x))
                { set_eigenvariable(k);
                  break;
                }
           }
      }
   push(f);
   if(path[0] != FUNCTOR(t))
      assert(0);
   for(I=0;I<n;I++)  /* go down to the daughter nodes */
      { if(I > 0)
           pop();  /* pop the previous argument number */
        if(f == '/')
           i = I ? 0 : 1;   /* Take the denominator of a fraction first */
        else if (f == '*' && pathlength >= 1 &&
                 path[pathlength-3] == INTEGRAL &&
                 FUNCTOR(ARG(n-1,p)) == DIFF
                )
           /* an integral of a product with an unevaluated derivative. */
           /* Example:  integral(1/(sec x tan x) d(sec x,x),x);
                                  we don't want sec = 1/cos applied */
           { if(I==0)
                i = n-1;   /* simplify the derivative first */
             else
                i = I-1;
           }
        else if (f == '+' && n == 2 &&
                 NEGATIVE(ARG(1,p)) &&
                 FUNCTOR(ARG(0,ARG(1,p))) == INTEGRAL &&
                 FUNCTOR(ARG(0,ARG(0,ARG(1,p)))) == '*' &&
                 FUNCTOR(
                         ARG(
                             ARITY(ARG(0,ARG(0,ARG(1,p))))-1,
                             ARG(0,ARG(0,ARG(1,p)))
                            )
                        ) == DIFF
                )
            i = I ? 0 : 1;  /* in integration by parts, simplify the summand
                               which is an integral of a derivative first.
                            */
        else if(f == '*' && FUNCTOR(ARG((unsigned short)(n-1),p)) == INTEGRAL)
           { if(I == 0)
                i = n-1;  /* simplify the integral first */
             else i = I-1;
           }
        else if(f == INTEGRAL && ARITY(p) == 4)
           { /* take the limits before the integrand */
                                 switch(I)
                { case 0:
                     i=2;
                     break;
                  case 1:
                     i=3;
                     break;
                  case 2:
                     i=0;
                     break;
                  case 3:
                     push(4);   /* arg number will get popped later so must be there */
                     continue;  /* don't bother with the variable of integration */
                }
           }
        else if((f == '=' || f == '*' || f == '+') &&
                get_problemtype() == TRIG_IDENTITY
               )
           /* simplify the arg containing sin(6x) before the arg that
              contains only sin(2x); maybe everything will turn out to be
              a function of sin 2x */
           { if(I==0)
                firstarg = i = select_arg(p);
             else
                i = (firstarg + I) % n;
           }
        else
           i = I;
        push((unsigned short)(i+1));  /* new argument number */
        if(f == '/')
           { if(i==0)
                { --indenomflag;  /* we WERE in the denom */
                  ++innumflag;
                  save_fraction_factorflag = get_factorflag();
                  if(power_of_eigenvariable(ARG(1,p)))
                     set_factorflag(0);  /* don't factor the numerator of (1-x^2)/x */
                }
             else
                ++indenomflag;
           }
        else if(f == SQRT)
           ++insqrtflag;
        else if(f == ROOT)
           { if(i==1)
                ++inrootflag;
           }
        else if(f=='^')
           { if(i == 0)
                { if(contains(ARG(1,p),'/'))
                     ++infractexpflag;
                }
             if(i > 0)
                {
                  if(contains(ARG(1,p),'/'))
                                                        --infractexpflag;
                  savelastbase = lastbase;
                  lastbase = ARG(0,p);
                  ++inpowerflag;
                  set_orderflag(DESCENDING);
                  save_logcollectflag = get_logcollectflag();
                  if(count_logs(ARG(1,p)) > 1)
                     /* two or more log terms in a sum in the exponent */
                     set_logcollectflag(1);  /* collect logs in exponents */
                  else
                     set_logcollectflag(0);
                  save_ringflag = get_ringflag();
                  set_ringflag( save_ringflag & ~RATRING);
                  /* tell it not to create rational coefficients,
                     use integer coefficients */
                }
           }
        else if(f == '=' && i==0 &&
                SOLVETYPE(probtype) &&
                probtype != LINEAR_EQUATION &&
                probtype != LINEAR_EQUATIONS
               )
           /* turn on factoring if the right side of an equation is zero,
              or if the right side is constant and the left side is a
              perfect square, so we can do completethesquare problems.
            */
           set_factorflags(p);  /* factor everywhere */
                  else if((f == '<' || f == LE || f == '>' || f == GE) && ZERO(ARG(i ? 0 : 1,p)))
           /* turn on factoring if either side of an inequality is zero */
           set_factorflags(p);  /* turn factoring on or off as appropriate. */
        else if(f == OR)
           { if(i==0)
                savetrigexpandflag = get_trigexpandflag();
             determine_trigexpandflag(ARG(i,p));
           }
        assert(dir == DOWN);
        checkit = pathlength;
        err = autosimp(t,ARG(i,p),dir,next,reason,op);
        assert(pathlength == checkit);  /* check the postcondition */
        if(err==0)
           { pop();  /* the arg number */
             if(f == '^')
                set_ringflag(save_ringflag);
             assert(pathlength >= 1 && path[pathlength-1] == f);
             pop();     /* pop f off the stack so postcondition
                           that path not be altered will hold */

             if( !contextsensitive(lastcode) && f != '^'
                 &&  f != AND && f != OR
               )
                  /* purpose of this last line is to make all operators
                     associated to '=' or '<' or LE  apply only to ONE
                     equation, in case there are several, without
                     having to list them separately as contextsensitive.
                                                */

            /* Now search whether this same operator can apply somewhere else
            further to the right in *next.  This is a bit tricky because
            (1) substituting the result of op in t may have changed the
            args of p, if there were other occurrences of the operand in
            the other args of p; and
            (2) the substitution may have involved a flattening.  In
            that case the remaining args of p are still subterms of
            *next, albeit with different indices, unless (1) applies.
            (3) Since autosimp takes denominators of fractions before
            numerators, we have to be sure to go into the numerator
            if we were in the denom.
            */

                { int nextarg, loopbound;
                  if(FRACTION(p) && i==1)
                     /* This takes care of (3) above */
                     { nextarg = 0;
                       loopbound = 1;
                     }
                  else
                     { nextarg = i+1;
                       loopbound = ARITY(p);
                     }
                  if(pathtail[0] || ShowStepOperation ||
                     FUNCTOR(ShowStepArg) != ILLEGAL ||
                     FUNCTOR(ShowStepArg1) != ILLEGAL ||
                     FUNCTOR(ShowStepArg2) != ILLEGAL
                    )
                     { /* Now things get too complicated for ShowStep.  The
                          first application may have called set_pathtail,
                          or SetShowStepOperation, or SetShowStepArg, etc.
                          The second application may set a different
                          pathtail, or even a different ShowStepOperation.
                          Then the information from the first application
                          would be wiped out and we will get an assertion
                          failure in get_focus when the path does not match
                          the original term. Therefore we just don't try
                          for a second application.  Also, even if the
                          first application doesn't tinker with the ShowStep
                          state, the second application may, which may also
                          screw up get_focus.  So we have to check for that
                          in the next loop and kill the second application
                          and make sure the ShowStep state is not disturbed.
                         */
                        loopbound = 0;  /* this will kill the attempt */
                     }
                  for(j=nextarg;j<loopbound;j++)
                     { SETFUNCTOR(arg,ILLEGAL,0);
                       SaveShowStepState();
                       push(f);
                       push((unsigned short)(j+1));
                       err = exec_aux(*next,ARG(j,p),lastcode,arg,(char *) reason,&temp,localbuf);
                       pop();
                       pop();
                       if(err)
                          RestoreShowStepState();
                       else if(ShowStepOperation ||  // pathtail[0] ||
                          FUNCTOR(ShowStepArg) != ILLEGAL ||
                          FUNCTOR(ShowStepArg1) != ILLEGAL ||
                          FUNCTOR(ShowStepArg2) != ILLEGAL
                         )
                          { err = 1;
                            RestoreShowStepState();
                          }
                        /* lastcode = optable[op->men][op->choice-1], but it's
                           faster not to look it up but to use the static variable lastcode */
                       if(!err)
                          { if(in_loop(temp,*op))
                                /* a second application of op created a
                                   duplicate of an earlier line. */
                               { err = 2;  /* tell one_step about the loop */
                                 if(BINDING2(p))
                                    { varinfo[savej].locus = savelocus;
                                      releasebinders();
                                      set_maxscope(savemaxscope);
                                    }
                                 RestoreShowStepState();
                                 goto out;
                               }
                            *next = temp;
                            ShowStepFocus(op);
                            /* add a node to focus  for Show Step for the additional application
                               of the operator. */
#if 0
                            if(autotestflag == 1)
                              { PDOCDATA pDocData = GetActiveDoc();
                                if (!GRAPHTYPE(pDocData->problemtype))
                                     TestTermSelection(*op,saveeigen,original_probtype);
                              }
#endif
                          }
                      if(!err)   /* else RestoreShowStepState has already been called above */
                         RestoreShowStepState();
                     }
                }
             if(BINDING2(p))
                { varinfo[savej].locus = savelocus;
                  releasebinders();
                  set_maxscope(savemaxscope);
                  if(equals(get_eigenvariable(),BOUNDVAR(p)))
                     set_eigenvariable(saveeigenindex);
                  /* we don't want to leave the eigenvariable set to the
                     bound variable, e.g. in a series with another variable.
                     But we don't reset nvariables since a new variable may
                     have been created. */
                }
             err = 0;
             goto out;
           }
                  else  /* nothing could be done to ARG(i,p) */
           { if(FUNCTOR(ARG(i,p)) == INTEGRAL)
                SETCANTFACTOR(ARG(i,p)); /* Mark it as an impossible integral */
           }
      }
   if(f=='/')
      { decrement_infractionflag();
        --innumflag;  /* the denom was done first, so we're coming out
                         of the numerator now. */
        reset_limfractflag();
        set_factorflag(save_fraction_factorflag);
      }
   else if(f == '^')
      { --inpowerflag;
        set_orderflag(saveorderflag);
        lastbase = savelastbase;
        set_ringflag(save_ringflag);
        set_logcollectflag(save_logcollectflag);
      }
   else if(f == SQRT)
      --insqrtflag;
   else if(f == ROOT)
      --inrootflag;
   else if(f == OR)
      set_trigexpandflag(savetrigexpandflag);
   else if(f == LIMIT)
      { --limitflag;
        reset_limfractflag();
        set_trigexpandflag(savetrigexpandflag);
        set_radicalflag(save_radicalflag);
        set_substitutionflag(save_subflag);
      }
   else if(f == DIFF)
      --difflag;
   else if(f == INTEGRAL || f == SUM || f == PRODUCT)
      { if(f == INTEGRAL)
           { --intflag;
             limitflag = savelimitflag;
           }
        else if(f == PRODUCT)
           --indexproductflag;
        else if(f == SUM && ARITY(p) == 4 &&
                (equals(ARG(3,p),infinity) || equals(ARG(2,p),minusinfinity))
               )
           --seriesflag;
        else
           --indexsumflag;
        set_expandflag(saveexpandflag);
        set_factorflag(savefactorflag);
        set_polyvalfactorflag(savepolyvalfactorflag);
        set_trigexpandflag(savetrigexpandflag);
        set_substitutionflag(save_subflag);
      }
   else if(trigfunctor(f))
      set_infractionflag(saveinfractionflag);
   else if( f == LOG || f == LN || f == LOGB)
      { set_infractionflag(saveinfractionflag);
        --inlogflag;
      }
   if(BINDING2(p))
      { varinfo[savej].locus = savelocus;
        releasebinders();
        set_maxscope(savemaxscope);
        set_eigenvariable(saveeigenindex);
      }
   pop();  /* remove the arg number */
           /* but leave f itself on the path while going up */
   checkit = pathlength;
   err = autosimp(t,p,UP,next,reason,op);  /* last chance */
   assert(pathlength == checkit);  /* monitor postcondition */
   pop();  /* get f off the path, restoring the path to its original condition */
   out:
      /* restore original values of flags */
      set_comdenomflag(savecomdenomflag);
      set_factorflag(savefactorflag);
      set_logcollectflag(savelogcollectflag);
      set_polyvalfactorflag(savepolyvalfactorflag);
      set_expandflag(saveexpandflag);
      set_radicalflag(save_radicalflag);
      set_ringflag(save_ringflag);
      return err;
}

/*_____________________________________________________________________*/
int trigfunctor(unsigned short f)
/* return 1 if f is a trig functor (including arctrig functions,
   but not including log, ln, logb, exp) */

{ if(f >= ACOS && f <= ATAN)
          return 1;
  if(f == COS || f == SIN || f==TAN || f == CSC || f == SEC || f == COT)
     return 1;
  return 0;
}
/*_____________________________________________________________________*/
static int hypertrigfunctor(unsigned short f)
/* return 1 if f is a trig functor (including arctrig functions,
   but not including log, ln, logb, exp) */

{ if(f == ASINH || f == ACOSH || f == ACOTH || f == ASECH || f == ACSCH || f == ACOTH)
     return 1;
  if(f == COSH || f == SINH || f==TANH || f == CSCH || f == SECH || f == COTH)
     return 1;
  return 0;
}
/*____________________________________________________________________*/
int hypertrigindex(unsigned short f)
{ switch(f)
     { case ASINH: return 0;
       case ACOSH: return 1;
       case ATANH: return 2;
       case ACSCH: return 3;
       case ASECH: return 4;
       case ACOTH: return 5;
       case SINH:  return 6;
       case COSH:  return 7;
       case TANH:  return 8;
       case CSCH:  return 9;
       case SECH:  return 10;
       case COTH:  return 11;
     }
  return -1;
}
/*______________________________________________________________________*/
int hyperindicates(unsigned short flag, unsigned short f)
/* return 1 if the bit corresponding to hypertrigindex(f) is set in flag.
Return 0 if not, or if f isn't a hypertrig or inverse hypertrig function.
*/
{ int k = hypertrigindex(f);
  if(k<0)
     return 0;
  return  flag & (1 << k);
}
/*________________________________________________________________________*/
#define NLOGOPS  22
/* logops lists the context-sensitive operators involving logarithms */
static actualop logops[NLOGOPS] =
  { collectlogs, collectlogs2, logofpower, logofproduct,
    logofquotient, logofreciprocal, factornumberinlog, factoroutbase,
    attractlogs, lnofpower, lnofproduct,lnofreciprocal,
    lnofquotient, collectlns, collectlns2, attractlns,
    logbofproduct,logbofreciprocal, logbofquotient, logbofpower,
    collectlogb, collectlogb2
   };

/*________________________________________________________________________*/
int contextsensitive(actualop c)
/* return 1 if c should NOT be applied to other places in the current line
once it has been found to work in one place. */
{  int i;
   aflag arithflag = get_arithflag();
   if (
         ((void *) c == (void *) arithmetic && !arithflag.negexp) ||
         (void *) c == (void *) apartandcancel ||
         (void *) c == (void *) apart ||
         (void *) c == (void *) orderterms ||
         (void *) c == (void *) distriblaw ||
         (void *) c == (void *) multiplyifcancels ||
         (void *) c == (void *) multiplyout ||
         (void *) c == (void *) multiplyoutandsimp ||
         (void *) c == (void *) multiplyoutundersqrt ||
         (void *) c == (void *) findcommondenom ||
         (void *) c == (void *) commondenom ||
         (void *) c == (void *) commondenom2 ||
         (void *) c == (void *) commondenomandsimp ||
         (void *) c == (void *) commondenomandsimp2 ||
         (void *) c == (void *) commondenominfraction ||
         (void *) c == (void *) sqrtofquotient ||
            /* because there might still be a pending cancellation under the second sqrt */
         (void *) c == (void *) binomialtheorem ||
         (void *) c == (void *) rationalizedenom ||
         (void *) c == (void *) rationalizenum ||
         (void *) c == (void *) factorinteger ||
         (void *) c == (void *) factorcomplexinteger ||
         (void *) c == (void *) complexfactorsofinteger ||
         (void *) c == (void *) eliminatenegexpnum ||
         (void *) c == (void *) eliminatenegexp1 ||
            /* integration by substitution, e.g. sin 2x + sin 3x  */
         (void *) c == (void *) intsub ||
         (void *) c == (void *) intsinsq ||
         (void *) c == (void *) intcossq ||
         (void *) c == (void *) intsubsin ||
         (void *) c == (void *) intsubcos ||
         (void *) c == (void *) intsubtan ||
         (void *) c == (void *) intsubsec ||
         (void *) c == (void *) trigsubsin ||
         (void *) c == (void *) trigsubsec ||
         (void *) c == (void *) trigsubtan ||
         (void *) c == (void *) trigsubsinh ||
         (void *) c == (void *) trigsubcosh ||
         (void *) c == (void *) trigsubtanh ||
         (void *) c == (void *) yourtrigsub ||
         (void *) c == (void *) choosesubstitution ||
         (void *) c == (void *) trysubstitution ||
         (void *) c == (void *) integratebyparts ||
         (void *) c == (void *) autointegratebyparts ||
         (void *) c == (void *) lhopital ||
         (void *) c == (void *) partialfractionsop ||
         (void *) c == (void *) secrule2 ||
         (void *) c == (void *) tantocot2 ||
         (void *) c == (void *) cottotan2 ||
         (void *) c == (void *) sintocos2 ||
         (void *) c == (void *) costosin2 ||
         (void *) c == (void *) sumleadingterm ||
         (void *) c == (void *) rectangulartopolar ||
         (void *) c == (void *) limleadingterms ||

         /* double angle formulas are contextsensitive
            because otherwise when cos(8x) is worked on,
            occurrences of cos(2x) will also be worked on,
            when you want to leave cos(2x) alone and work
            everything down to functions of 2x. */

         (void *) c == (void *) doublesin ||
         (void *) c == (void *) doublecos1 ||
         (void *) c == (void *) doublecos2 ||
         (void *) c == (void *) doublecos3 ||
         (void *) c == (void *) doubletan ||
         (void *) c == (void *) doublecot ||
         (void *) c == (void *) doublesinh ||
         (void *) c == (void *) doublecosh ||
         (void *) c == (void *) triplesin ||
         (void *) c == (void *) triplecos ||
         (void *) c == (void *) makesubstitution ||
         (void *) c == (void *) cottosincos ||
         (void *) c == (void *) tanrule || /* because we don't want it
                                           applying to tan (u/2) further
                                           to the right, since a half-angle
                                           formula should be used on that. */
         (void *) c == (void *) sinsum ||
         (void *) c == (void *) cossum ||
         (void *) c == (void *) tansum ||
         (void *) c == (void *) sinhsum ||
         (void *) c == (void *) coshsum ||  /* example, in differentiating
                                               cosh(3x-1) from definition,
                                               we need to apply it only when
                                               the term contains the limit variable */
         /* we don't want the following operators applied accidentally to terms
            with real exponents. */
         (void *) c == (void *) complexsin ||
         (void *) c == (void *) complexcos ||
         (void *) c == (void *) complexexponential ||
         (void *) c == (void *) complexexponential2 ||
         (void *) c == (void *) sinhalfperiod1 ||
         (void *) c == (void *) coshalfperiod1 ||
         (void *) c == (void *) sinhalfperiod2 ||
         (void *) c == (void *) coshalfperiod2 ||
         (void *) c == (void *) limlinear ||  /* because in a product of limits,
                                   the second one may have the form ln(1+u)/u
                                   and limlinear can destroy that form */
         (void *) c == (void *) reversepowertopower1 ||
                                /* it's used on 2^(2n) x^(6n) and we want
                                   it to only apply to the first part. */
         (void *) c == (void *) introducelninexponent ||
         (void *) c ==  (void *) seriesevenandodd  ||
         (void *) c ==  (void *) seriesaddindex ||
         (void *) c ==  (void *) seriessubindex
      )
         return 1;
  for(i=0;i<NFACTOROPS;i++)
     { if( (void  *) c == (void  *) factorops(i) )
          return 1;
     }
  for(i=0;i<NLOGOPS; i++)
     { if( (void *) c == (void *) logops[i])
         return 1;
     }
#if 0
  if(get_polyvalnegexpflag()==2)
     {
#endif
       if(
          (void *) c == (void *) eliminateconstnegexp ||
          (void *) c == (void *) eliminateconstnegexpnum ||
          (void *) c == (void *) eliminatenegexp ||
          (void *) c == (void *) introducenegexp ||
          (void *) c == (void *) introducenegexp1
         )
          return 1;
#if 0
     }
  /* ShowStep can't get these flags right so just make these
     operations ALWAYS contextsensitive, so ShowStep matches AutoStep */
  if(get_radicalflag() < 0)
     {
#endif
       if(
          (void *) c == (void *) exponenttosqrt ||
          (void *) c == (void *) exponenttoroot
         )
          return 1;
#if 0
     }
  if(!(get_expandflag() & 0x0100))
      /* for example in common denom problems, we want to expand in
         numerators but not in denominators */
     {
#endif
       if(
            (void  *) c == (void  *) distriblaw ||
            (void  *) c == (void  *) expand ||
            (void  *) c == (void  *) multiplyout ||
            (void  *) c == (void  *) multiplyoutandsimp ||
            (void  *) c == (void  *) difofsquares ||
            (void  *) c == (void  *) squareofsum ||
            (void  *) c == (void  *) squareofdif
          )
            return 1;
#if 0
     }
#endif
  return 0;
}
/*__________________________________________________________________________*/
int strip_protections(term *t)
/* UNPROTECT *t and all its subterms except as noted below.
Return the number of protected subterms encounted and unprotected--
hence zero means there were none.
   Exceptions:
    (1) protected integrals are created in iterated
integration by parts.  If the integral can't be done, we don't
want to unprotect it as that will just cause a loop.
    (2) protected equations are created by trigeqn_aux, and
should not be unprotected, so that common denom isn't used on
them.  Example:  x = pi/2 + 2pi k
    (3) complexnumerical terms, which were protected in order
to prevent long useless struggles to simplify them.  However,
powers are excepted from this, because protect_exponents has
protected some numerical powers in the expectation that
strip_protections would remove those protections.
    (4) in MINMAX, also unprotect equations, so that
eliminateparameter will get used.
    (5) protected infinite series are not stripped--they get
protected when the comparison test completes.
*/

{  int count = 0;
   unsigned short n;
   unsigned short f = FUNCTOR(*t);
   int i;
   if(PROTECTED(*t) && f != INTEGRAL && f != '=' &&
      f != '<' && f != NE && f != LE && f != '>' && f != GE &&
      (f != SUM || !ISINFINITE(ARG(3,*t))) &&
      (!complexnumerical(*t) || FUNCTOR(*t) == '^')
     )
      { UNPROTECT(*t);
        ++count;
      }
   if(f == '=' && PROTECTED(*t) && get_problemtype() == MINMAX)
      { UNPROTECT(*t);
        ++count;
      }
   if(ATOMIC(*t))
      return count;
   n = ARITY(*t);
   for(i=0;i<n;i++)
      { count += strip_protections(ARGPTR(*t) + i);
      }
   return count;
}
/*____________________________________________________________*/
void set_trigflag(term t, unsigned short *flag)
/* traverse t and set the bits of *flag corresponding to trig
and arctrig functors encountered. (There are 12 of these.)
Set bit 12 if a non-atomic arg of one of these functions is
encountered. Don't go into args of trig functions.
*/

{ int i;
  unsigned short n;
  unsigned short g;
  unsigned short index;
  if(ATOMIC(t))
     return;  /* without altering *flag */
  n = ARITY(t);
  for(i=0;i<n;i++)
    { g = FUNCTOR(ARG(i,t));
      if(trigfunctor(g))
         { index = (unsigned short) TRIGINDEX(g);
           assert(index < 16);
           *flag  |= (unsigned short)(1 << index);  /* set the bit corresponding to this functor */
           if(!ATOMIC(ARG(0,ARG(i,t))))
              *flag |= 0x1000;  /* set bit 12, non-atomic arg */
         }
      if(FUNCTOR(t) == INTEGRAL)
         continue;
      else
         set_trigflag(ARG(i,t),flag);
    }
}
/*____________________________________________________________*/
void set_hypertrigflag(term t, unsigned short *flag)
/* traverse t and set the bits of *flag corresponding to hypertrig
and arctrig functors encountered. (There are 12 of these.)
Set bit 12 if a non-atomic arg of one of these functions is
encountered. Don't go into args of hypertrig functions.
*/

{ int i;
  unsigned short n;
  unsigned short g;
  unsigned short index;
  if(ATOMIC(t))
     return;  /* without altering *flag */
  n = ARITY(t);
  for(i=0;i<n;i++)
    { g = FUNCTOR(ARG(i,t));
      if(hypertrigfunctor(g))
         { index = hypertrigindex(g);
           assert(index < 16);
           *flag  |= (unsigned short)(1 << index);  /* set the bit corresponding to this functor */
           if(!ATOMIC(ARG(0,ARG(i,t))))
              *flag |= 0x1000;  /* set bit 12, non-atomic arg */
         }
      else
         set_hypertrigflag(ARG(i,t),flag);
    }
}


/*_____________________________________________________________________*/
static int oddpowerflag;  /* used below */
void determine_trigexpandflag(term t)
/*
Traverse t and determine whether all trig functions have the
same argument.  If not, then set bit 0 of trigexpandflag;
UNLESS all the different trig args have no variable in common
(e.g. tan x and tan h, or tan 4x and cos u), or unless
problemtype > LIMITS, in which case DO NOT set bit 0.

-- If only tan and sec and csc appear set bit 4;
-- if only cot and csc appear, set bit 8;
-- if only sin and cos appear, set bit 12;
-- If only tan and cot and possibly sec^2 appear, set bit 2.

In this 'traversal',  do NOT go into integrals;
otherwise e.g. we will be expanding sin(4x) + integral(sin u,du)
when u = 4x.

--   If only ONE trig function occurs, set bit 1  (even if that
trig function has different args; that can be checked by looking at bit 0.)
--   If only TWO trig functions occur, set bit 5.
--   If all trig functions have atomic args and occur to even powers,
set bit 3.
*/

{ term var;
  int trigexpandflag=0;
  int k = 0;
  unsigned short trigflag=0;
  unsigned short f = FUNCTOR(t);
  set_trigflag(t,&trigflag);
  int probtype = get_problemtype();
  oddpowerflag = 0;
  if(trigfunctor(f))
     { trigflag |= (unsigned short) (1<<TRIGINDEX(f));  /* set the bit corresponding to this functor */
       if(!ATOMIC(ARG(0,t)))
           oddpowerflag |= (1 << 12);
     }
  set_trig_aux(t,&k,&var,&trigflag);

  //  trigflag has the information about what trig functions occur in t.
  //  So now set trigexpandflag per the spec.
   
 // if(trigflag && ((oddpowerflag & (1 << 12)) == 0))
   //  trigexpandflag = 0;
   /* all trig args atomic, even though
                             not all identical; don't set bit 0. */
   /* Now bit 0 is almost set according to the specs; trigexpandflag
      is now either 1 or 0.  But, trigexpandflag is still 1 if
      there are different, nonatomic args with no variables in
      common, e.g. sin (4x) and cos u.  We need to make sure it is
      zero in such a case.
    */
  int nvariables = get_nvariables();
  term *varlist = get_varlist();
  term scratch[MAXVARIABLES+1];
  for(k=0;k<nvariables+1;k++)
     SETFUNCTOR(scratch[k], ILLEGAL, 0);  // at_aux requires this initialization
  int differentargs = at_aux(t,nvariables,varlist, scratch);
  if(differentargs)
     { // there are trig functions in t with different args and common variables
       // so per the spec, set bit 0
       trigexpandflag |= 1;
     }
  if(probtype == TRIG_EXPAND)
     trigexpandflag |= 1;  /* set bit 0 to 1 */
  if(probtype > LIMITS && probtype != DIFFERENTIATE_FROM_DEFN)
     { if(!intflag  && !(trigexpandflag & 1))
          trigexpandflag = 0;
        /* don't expand sin(3t) in calculus, if all functions in the integrand
        are expressed in terms of 3t, but do expand it in integral(sin(3t)/sin t,t)
        for example.  Also, we do need to expand sin(x+h)-sin(x) in taking limits;
        hence the condition is probtype > LIMITS, not >= LIMITS */
     }
 
  if(!TRIGCONTAINS(trigflag,COT) && !TRIGCONTAINS(trigflag,CSC) &&
     !TRIGCONTAINS(trigflag,TAN) && !TRIGCONTAINS(trigflag,SEC)
     )
     { // only SIN and COS appear
       trigexpandflag |= (1 << 12);  /* set bit 12 */
     }
  if(!TRIGCONTAINS(trigflag,SIN) && !TRIGCONTAINS(trigflag,COS) && !TRIGCONTAINS(trigflag,COT))
     { // only TAN, CSC, and SEC appear
       trigexpandflag |= (1 << 4);  /* set bit 4 */
     }
  if(!TRIGCONTAINS(trigflag,SIN) && !TRIGCONTAINS(trigflag,COS) &&
     !TRIGCONTAINS(trigflag,SEC) && !TRIGCONTAINS(trigflag,TAN)
    )
     {   //  only COT and CSC appear
       trigexpandflag |= (1 << 8);    /* set bit 8 */
     }
  if(!TRIGCONTAINS(trigflag,SIN) &&  !TRIGCONTAINS(trigflag,COS) &&
     !TRIGCONTAINS(trigflag,CSC)  &&
     !(TRIGCONTAINS(trigflag,SEC) && (oddpowerflag & (1 << TRIGINDEX(SEC))))
    )
     {   /* only COT and TAN  and possible SEC^2 occur               */
         trigexpandflag |= (1 << 2);  /* set bit 2 */
     }
  if(nbits(trigflag) == 2)
     {  // exactly two trig functions occur, set bit 5
        trigexpandflag |= (1 << 5);
     }
  if(nbits(trigflag) == 1)
     {  // exactly one trig function, set bit 1
        trigexpandflag |= (1 << 1);
     }
  if(oddpowerflag == 0 && (trigexpandflag & 1) == 0)
     { // If all trig functions have atomic args and occur to even powers, set bit 3.
       // bit zero is set if there are trig functions with different args
       // (involving a common variable)
        trigexpandflag |= (1 << 3);
     }
  /* if you get here, t contains only inverse trig functors */
  set_trigexpandflag(trigexpandflag);
}

/*____________________________________________________________________*/
static int set_trig_aux(term t, int *k, term *var, unsigned short *flag)
/* traverse term t (but skip integrals).
Set bits in *flag corresponding to
trig functors encountered, as in set_trigflag.  If *k > 0 it means
*var is the arg of a trig functor previously encountered.  If a
different arg of a trig functor is encountered, return 1.
Otherwise return 0.  If *k==0, it means we haven't encountered a
trig functor above this call, so set *var to the arg if we do
encounter one, and set *k = 1.
   If a trig function with a non-atomic arg, or one which does not
occur raised to an even power, is encountered, set a bit in
oddpowerflag; namely the bit corresponding to the trig function
which is encountered to an odd power, or bit 12 for a non-atomic arg.
This variable is used by set_trig_expandflag to set bit 2 of
trigexpandflag.
 */

{ int i;
  unsigned short n;
  term u,arg;
  int ans=0;
  int ans2;
  unsigned short f,g;
  if(ATOMIC(t))
     return 0;  /* without altering any parameters passed by reference */
  n = ARITY(t);
  f = FUNCTOR(t);
  if(n==1)
     { u = ARG(0,t);
       g = FUNCTOR(u);
     }
  if( f == '^' && TRIGFUNCTOR(g) &&  // 1.30.25
      (!INTEGERP(ARG(1,t)) || ISODD(ARG(1,t)))
    )
      oddpowerflag |= (1 << TRIGINDEX(g));
  if(f == INTEGRAL)
     return 0;   /* don't traverse into integrals */
  if(f == SG)   /* don't go into sg(...) either */
     return 0;
  for(i=0;i<n;i++)
    { u = ARG(i,t);
      g = FUNCTOR(u);
      if(f == '*' && g == SQRT)
         { u = ARG(0,u);
           g = FUNCTOR(u);
         }
      if(f == '*' && g == ROOT)
         { u = ARG(1,u);
           g = FUNCTOR(u);
         }
      if(f == '*' && g == '^')
         {
           u = ARG(0,u);
           g = FUNCTOR(u);
         }
      if(f == '*' && NEGATIVE(u))
         { u = ARG(0,u);
           g = FUNCTOR(u);
         }
      if(ATOMIC(u))
         continue;
        /* Next lines make sure we don't count
           (1/tan t)^2  as an odd power of tan t */
      if(f == '^' && INTEGERP(ARG(1,t)) && ISEVEN(ARG(1,t)) &&
         g == '/' &&
         (constant(ARG(0,u)) || trigfunctor(FUNCTOR(ARG(0,u)))) &&
         (constant(ARG(1,u)) || trigfunctor(FUNCTOR(ARG(1,u))))
        )
         {  /* don't set oddpowerflag */
           ans = set_trig_aux(ARG(0,u),k,var,flag);
           ans2 = set_trig_aux(ARG(1,u),k,var,flag);
           return (ans || ans2);
         }
      if(trigfunctor(g))
         {
           *flag  |= (unsigned short)(1<<TRIGINDEX(g));  /* set the bit corresponding to this functor */
           arg = ARG(0,u);
           if(!ATOMIC(arg))
               oddpowerflag |= (1 << 12);
           if(*k == 0)
              { *k = 1;
                *var = arg;
              }
           else if(!equals(arg,*var) && FUNCTOR(arg) != inverse(g))
              ans = 1;
         }
      else
         { ans2 = set_trig_aux(u,k,var,flag);
           if(ans2)
              ans = 1;
         }
    }
  return ans;
}
/*_______________________________________________________*/
static void reset_limfractflag(void)
/* use path and pathlength to set limfractflag correctly */
/* when this is called, path[pathlength-2] is the LIMIT or '/' just
being exited */
{  int i = pathlength-2;
   int lflag=0,fflag = 0;
   assert(path[i] == LIMIT || path[i] == '/');
   i-=2;
   for(;i>=0;i-=2)  /* i is already initialized */
      { if(path[i] == LIMIT)
           { lflag = 1;
             if(fflag)
                { limfractflag = -1;  /* fraction innermost */
                   return;
                }
           }
        else if(path[i] == '/')
           { fflag = 1;
             if(lflag)
                { limfractflag = 1;   /* limit innermost */
                   return;
                }
           }
      }
   limfractflag = 0;
}
/*________________________________________________________________*/
static void protect_exponents(term *t, int flag)
/*  prevent arithmetic and polyvalop from working on
2^100  inside SQRT by PROTECTing numerical powers in SQRTs and ROOTs
everywhere in *t.   After one pass of simplification, if any of these
still survive, strip_protections will be called.  Thus for example
we can simplify sqrt(2^100).  Call it with flag = 0;  flag = 1 is used
in the recursive call when we are inside a ROOT or SQRT. */

{ unsigned short n,f;
  int i;
  if(ATOMIC(*t))
     return;
  f = FUNCTOR(*t);
  if(flag && numerical(*t) && f == '^')
     { PROTECT(*t);
       return;
     }
  n = ARITY(*t);
  if(f == SQRT || f == ROOT)
     flag = 1;
  else if(f != '*' && f != '/' )
     flag = 0;   /* so we don't protect 4^2 in sqrt(4^2 + 1)  */
  for(i=0;i<n;i++)
     protect_exponents(ARGPTR(*t)+i,flag);
  return;
}
/*______________________________________________________________*/
void set_checked(void)
/* set all solved equations in the current line to CHECKED.
   Do NOT use set_history, as that will make a new permcopy;
   see the comments on historyp in prover.c.
*/
{ int i;
  int currentline = get_currentline();
  term *u = historyp(currentline);
  term x = get_eigenvariable();
  term v;
  if(FUNCTOR(*u) == '=' &&
     ISATOM(ARG(0,*u)) && FUNCTOR(ARG(0,*u)) == FUNCTOR(x) &&
     !contains(ARG(1,*u),FUNCTOR(x))
    )
     { SETCHECKED(*u);
       return;
     }
  if(FUNCTOR(*u) != OR)
     return;
  for(i=0;i<ARITY(*u);i++)
     { v = ARG(i,*u);
       if(FUNCTOR(v) == '=' &&
          ISATOM(ARG(0,v)) && FUNCTOR(ARG(0,v)) == FUNCTOR(x) &&
          !contains(ARG(1,v),FUNCTOR(x))
         )
          SETCHECKED(ARG(i,*u));
     }
}

/*______________________________________________________________*/
static void set_prime_aux(term p, term u)
/* set the PRIME bit recursively on all subterms of u that equal p */
{ unsigned short n;
  int i;
  if(equals(p,u))
     SETPRIME(u);
  if(ATOMIC(u))
     return;
  n = ARITY(u);
  for(i=0;i<n;i++)
    set_prime_aux(p,ARG(i,u));
}
/*______________________________________________________________*/
void set_prime(term p)
/* call SETPRIME on all integrals equal to p in the current line;
the PRIME bit applied to an integral means integration by substitution
has been tried already. */

{ int currentline = get_currentline();
  term u = history(currentline);
  if(equals(p,u))
     { /* In this case we need to set the PRIME bit in history[currentline],
          so we have to get at that term itself, not just a copy of it. */

       term *q = historyp(currentline);
       set_prime_aux(p,*q);
     }
  set_prime_aux(p,u);
}
/*_________________________________________________________*/
static void strip_order(term *t)
/* remove ORDERED labels from *t and all its subterms */
{ int i;
  unsigned short n;
  if(ATOMIC(*t))
     return;
  if(FUNCTOR(*t) == '+' || FUNCTOR(*t) == '*')
     UNSETORDERED(*t);
  n = ARITY(*t);
  for(i=0;i<n;i++)
     strip_order(ARGPTR(*t)+i);
}
/*_________________________________________________________*/
/* Loop detection for Mathpert, by M. Beeson */

int in_loop(term next,operation op)
/* Attempt to detect unanticipated looping */
/* Return nonzero if a loop is detected, zero if not */
/* strong_equals checks the info-field too for the bit that says
   'already-factored', so that if the second occurrence is labelled
    PRIME while the first isn't, we don't call that a loop. */
/* Still, if we have tried a substitution (when factoring) and the
   resulting expression could after all not be factored, when the
   definition is unwound, we get an old line back, while still not knowing
   the polynomial to be irreducible.  We check for that situation
   by seeing if a let_defn has been made in the meantime (and the
   defined variable is NOT in the loop formula); if
   so, return 2 instead of 1.  Return value 1 indicates a true
   loop with no improvement at all.  Return value 2 indicates
   an apparent loop with a let_definition having been made in between,
   e.g. trying to factor by a substitution that failed.  In that
   case the continuation will try something else.
*/

{ int i,j,letflag=0;
  int currentline = get_currentline();
  int problemtype = get_problemtype();
  int nextdefn = get_nextdefn();
  controldata p;
  defn d;
  term old;
     /* Here we must trap all operators that can recover a
        previous line and can be used in automode:  */
  if(op.men == integrate_by_substitution && op.choice==5)  /* showcallingproblem */
      return 0;
  if(op.men == advanced_equations && op.choice ==10) /* showalleqns -- can be
               used in auto mode if user has selected an equation first */
     return 0;
  if(op.men == several_linear_equations && op.choice == 3) /* lineupvars */
     return 0;
  if(op.men == cubic_equations && op.choice == 3)  /* showcallingcubic */
     return 0;
  if(op.men == series_convergence_tests && 8<= op.choice && op.choice <= 16)
     /* finishintegraltest,...,finishcondensationtest */
     return 0;
  get_controldata(&p);
  for(i=1;i<=p.autosteps+1;i++)
     /* autosteps+1 because autosteps hasn't been incremented for the
        current step yet. */
     { old = history(currentline-i+1);
       if(problemtype == TESTCONVERGENCE && i > 1 && FUNCTOR(old) == SUM)
          return 0;  // don't look back into the calculations for a different convergence test
       for(j=0;j<nextdefn;j++)
          { d = get_defn(j);
            if( currentline-i+1 < d.line &&
                d.line <= currentline + 1 &&
                   /* currentline + 1, to include definitions just made;
                   remember currentline hasn't been incremented yet,
                   so definitions just made are labelled with a
                   line number 1 more than currentline
                   */
                   /* Now we know there was a let_definition */
                   /* But does the variable defined occur in the
                      current line?  We need to detect loops like
                      x^2 = 3, u = x, x^2 = 3, u=x etc. (even though
                      this one is stopped otherwise) */

                (
                  (ISATOM(d.left) &&! contains(next,FUNCTOR(d.left))) ||
                  (FUNCTOR(d.left) == AND &&
                   !contains(next,FUNCTOR(ARG(0,d.left))) &&
                   !contains(next,FUNCTOR(ARG(1,d.left)))
                  )
                )
              )
                {  letflag = 1;
                   break;
                }
          }
       if(strong_equals(next,old))
          { if(letflag)
               return 2;
            printf("%s\n","loop warning, autosimp.c\n");
            return 1;
          }
       if(equal_mod_constofint(next,old))
          /* they differ only by renaming a constant of integration */
          {
            printf("%s\n","loop warning, autosimp.c\n");
            return 1;
          }
       if(equal_by_renaming(next,old) && letflag)
          /* this line will (would) duplicate a previous one except that
             a variable in the old line has been replaced by a new variable
             that was introduced since the old line.  (It would be wrong
             to call it a loop simply because the new line differs from the
             old one by a renamed variable, e.g. an equation 2u=1 can reduce
             to 2x=1 after unwinding the definition of u.) Strictly speaking
             the code here does not check that the renamed variable is the one
             that was defined since the line 'old' so this code might reject
             as a loop a genuine step.  */
          {
#ifdef _DEBUG
            loop_warning();
#endif
            return 1;
          }
     }
  return 0;
}

/*___________________________________________________________________*/
static int strong_equals(term a, term b)
/* Return 1 if a and b are equal terms counting the PRIME bit in the
info field, but not for numbers.  It is NOT symmetric as it
returns 1 if b is prime and a is not, but 0 if a is prime and b is
not, when a and be are equal.  This allows for one line of the
solution to be changed from not prime to prime without it being a loop,
but if it's prime and the next line is not prime but equal, that
does count as a loop.
*/
{ int i;
  if(ATOMIC(a) || ATOMIC(b))
     return equals(a,b);
  if(ARITY(a) != ARITY(b))
     return 0;
  if(FUNCTOR(a) != FUNCTOR(b))
     return 0;
  for(i=0;i<ARITY(a);i++)
    { if( !strong_equals(ARG(i,a),ARG(i,b)) )
         return 0;
    }
  if(!FRACTION(a) && PRIME(a) && !PRIME(b))
     return 0;
  return 1;
}
/*_______________________________________________________________*/
unsigned nbits(unsigned short x)
/* return the number of nonzero bits in x */
{ int i;
  unsigned ans = 0;
  unsigned short mask = 1;
  for(i=0;i<8*sizeof(unsigned short);i++,mask = (unsigned short)(mask<<1))
     ans +=  ((x & mask) >> i);
  return ans;
}
/*_______________________________________________________________*/
static int already_solved(term t)
/* Presumes t is an equation or inequality or an OR of such, or
a MULTIPLICITY term.
Presumes SOLVETYPE(problemtype).
If t is already solved, return 1; else return 0.
If t is an OR and only some of the equations are already solved,
PROTECT those equations, and return 0; but return 1 if all are
already solved.  Does NOT check solutions in original equation.
For inequalities, protect only the sides of the inequalities, not
the inequalities themselves, or you block explicitdomain.
*/

{ unsigned short f = FUNCTOR(t);
  unsigned short n,g;
  term u,d,x,w;
  int i,count,err;
  char buffer[DIMREASONBUFFER];
  if(ATOMIC(t))
     return 1;   /* t can be 'false' or 'true' */
  if(f == OR || f == AND)
     { n = ARITY(t);
       count = 0;
       for(i=0;i<n;i++)
          { if(already_solved(ARG(i,t)))
               { g = FUNCTOR(ARG(i,t));
                 if(g == '=')
                    PROTECT(ARG(i,t));
                    /* which WILL affect the calling environment */
                 else if(g == '<' || g == LE || g == '>' || g == GE)
                    PROTECT(ARG(1,ARG(i,t)));
                 ++count;
               }
          }
       if(count != n)
          return 0;
       /* but if count == n, you still can have  a < b and a = b among the
          disjuncts, and they need to be combined before you count it solved.
          Moreover you can still have other intervals that need combining,
          such as [x <= -2, x <= -3].
       */
       if(contains(t,'<') || contains(t,LE) || contains(t,'>') || contains(t, GE))
          { err = combineintervals(t,zero,&w,buffer);
            if(!err)
               return 0;
          }
       return 1;
     }
  if(f == MULTIPLICITY)
     return already_solved(ARG(0,t));
  x = get_eigenvariable();
  if(equals(ARG(0,t),x) && NUMBER(ARG(1,t)))
     { u = ARG(1,t);
       if(NEGATIVE(u))
          u = ARG(0,u);
       if(RATIONALP(u))
          { /* check if u and v are in lowest terms */
            gcd(ARG(0,u),ARG(1,u),&d);
            return ONE(d) ? 1 : 0;
          }
     }
  if( equals(ARG(0,t),x) &&
      (NUMBER(ARG(1,t)) || ALREADY(ARG(1,t))) &&
      !contains(ARG(1,t),FUNCTOR(x)) &&
      mvpoly2(ARG(1,t))
     )
      /*  x = c is possibly finished when c has been through polyval and
          doesn't contain x.  Don't thrash around factoring c etc.
          But, maybe it's not really finished:  x = 10^(-log 5) for
          example will pass polyval but isn't finished.  Hence the
          last clause requiring the right side to be an mvpoly2 to avoid
          giving up too soon. */
        return 1;
   if((f == '<' || f == LE || f == '>' || f == GE) &&
      equals(x,ARG(1,t)) &&
      (NUMBER(ARG(0,t)) && ALREADY(ARG(0,t))) &&
      !contains(ARG(0,t),FUNCTOR(x))
     )
      return 1;  /* similarly, but for  c < x  */
   if(get_problemtype() == IMPLICIT_DIFF &&
      ALREADY(ARG(1,t)) &&
      !contains(ARG(1,t),DIFF) &&
      FUNCTOR(ARG(0,t)) == DIFF &&
      ISATOM(ARG(0,ARG(0,t))) &&
      equals(x,ARG(1,ARG(0,t)))
     )
      return 1;
   return 0;
}

/*__________________________________________________________________*/
int get_activeline(void)
/* get the last line not produced by a specialop.  E.g. if
we are repeatedly numerically evaluating a sum we'll retrieve
the line of the sum
*/
{ controldata d;
  int activeline = get_currentline();
  if(activeline <= 0) 
      return 0; // one_step calls history with this argument, so it better NEVER be negative.
  get_controldata(&d);
  if(activeline==1 && d.opseq[activeline].choice == 0)
     return 0;  // this happens when computing singularities of a graph.
  if(d.autosteps == 0)
     { while(activeline > 0 && specialop(zero,access_optable(d.opseq[activeline].men)[d.opseq[activeline].choice-1]))
          --activeline;
     }
  return activeline;
}

#undef UP
/*________________________________________________________________________*/
static int at_aux(term t, int nvariables, term *varlist, term *scratch)
/* varlist is the varlist, of dimension nvariables; scratch is
workspace of the same dimension.  Search t for args of trig functions.
Put each arg in scratch at the position corresponding to the first
variable it contains.  If there is already a DIFFERENT term there then return 1.
(Initially, at the top-level call, scratch is filled with ILLEGAL terms.)
Otherwise if you finish, return 0.
*/
{ int i;
  unsigned short n;
  term u;
  unsigned short f = FUNCTOR(t);
  if(ATOMIC(t))
     return 0;
  if(TRIGFUNCTOR(f))
     { u = ARG(0,t);
       for(i=0;i<=nvariables;i++)
          { if(i == nvariables || contains(u,FUNCTOR(varlist[i])))
              // i == nvariables means t is constant
               { if(FUNCTOR(scratch[i]) == ILLEGAL)
                    { scratch[i] = u;
                      break;
                    }
                 if(!equals(u,scratch[i]))
                    return 1;
               }
          }
   
       return 0;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(at_aux(ARG(i,t),nvariables,varlist,scratch))
          return 1;
     }
  return 0;
}

/*___________________________________________________________________*/
static int mixed_roots(term t, term x)
/* If t contains any two of ROOTs, SQRTs, and constant powers of x,
return nonzero, specifically with bit 0 set for SQRT, bit 4
set for ROOT, bit 8 set for constant powers.  Don't even
look inside nonconstant exponents.
*/
{ unsigned short n,f;
  int i,ans,m;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == SQRT)
     return contains(ARG(0,t),FUNCTOR(x)) ? 1 : 0;
  if(f == ROOT)
     return contains(ARG(1,t),FUNCTOR(x)) ? 0x10: 0;
  if(f == '^' && !contains(ARG(1,t),FUNCTOR(x)))
     return contains(ARG(0,t),FUNCTOR(x)) ? 0x0100: 0;
  if(f == '^')
     return mixed_roots(ARG(0,t),x);
  n = ARITY(t);
  ans = 0;
  for(i=0;i<n;i++)
     { m = mixed_roots(ARG(i,t),x);
       if(ans == 0 && m != 0)
          { ans = m;
            continue;
          }
       if(m != ans && m != 0 && ans != 0)
          return (m | ans);
     }
  return 0;
}
/*______________________________________________________________*/
static unsigned short inverse(unsigned short f)
/* return TAN if f == ATAN  */
{ switch (f)
    { case TAN: return ATAN;
      case SIN: return ASIN;
      case COS: return ACOS;
      case COT: return ACOT;
      case SEC: return ASEC;
      case CSC: return ACSC;
      case ATAN: return TAN;
      case ACOT: return COT;
      case ASIN: return SIN;
      case ACOS: return COS;
      case ASEC: return SEC;
      case ACSC: return CSC;
    }
  assert(0);
  return 0;
}
/*___________________________________________________________*/
static int equal_mod_constofint(term t, term s)
/* Return 1 if t and s are equal when all constants of
integration are identified, 0 otherwise.
*/
{ unsigned short n;
  int i;
  if(ATOMIC(t) && ATOMIC(s))
     return equals(t,s);
  if(FUNCTOR(t) == CONSTANTOFINTEGRATION &&
     FUNCTOR(s) == CONSTANTOFINTEGRATION
    )
     return 1;
  if(FUNCTOR(s) != FUNCTOR(t))
     return 0;
  if(ARITY(t) != ARITY(s))
     return 0;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(!equal_mod_constofint(ARG(i,t),ARG(i,s)))
          return 0;
     }
  return 1;
}


/*___________________________________________________________*/
static int equal_mod_atoms(term t, term s)
/* return 1 if t and s have the same structure tree, i.e.
identifying all atoms with each other, and all objects with
each other.
*/
{ unsigned short n;
  int i;
  if(ISATOM(t) && ISATOM(s))
     return 1;
  if(OBJECT(t) && OBJECT(s))
     return equals(t,s);
  if(ATOMIC(t) || ATOMIC(s))
     return 0;
  if(FUNCTOR(t) != FUNCTOR(s))
     return 0;
  if(ARITY(t) != ARITY(s))
     return 0;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(!equal_mod_atoms(ARG(i,t),ARG(i,s)))
          return 0;
     }
  return 1;
}


/*________________________________________________________*/
static int equal_by_renaming(term t, term s)
/* return 1 if there is a renaming of variables which
makes t equal to s.  That is, s contains one or two variables
NOT in t, t contains the same number of variables NOT in s,
and renaming those variables makes them equal.  Thus for
example x+y and y+x are not equal by renaming.
   It is too difficult and not necessary anyway to
catch the case when t and s differ by THREE or more
renamed variables.  The purpose of this function is to
stop infinite regresses arising by making the same
substitution over and over with different letters.
Usually only one new variable is involved.
*/
{ term *atomlist1, *atomlist2;
  int *scratchpad1, *scratchpad2;
  int n,i,count1=0,count2=0;
  term u,v,p,q,temp,temp2;
  if(!equal_mod_atoms(t,s))
     return 0;   /* and be done with it quickly */
  n = atomsin(t,&atomlist1);
  if(n != atomsin(s,&atomlist2))
     { free2(atomlist1);
       free2(atomlist2);
       return 0;
     }
  /* Now find the variables in s but not in t */
  scratchpad1 = callocate(n,sizeof(int));
  scratchpad2 = callocate(n,sizeof(int));
  for(i=0;i<n;i++)
     { if(!contains(t,FUNCTOR(atomlist2[i])))
          { scratchpad2[i] = 1;
            if(!count1)
               v = atomlist2[i];
            else
               q = atomlist2[i];
            ++count1;
          }
     }
  for(i=0;i<n;i++)
     { if(!contains(s,FUNCTOR(atomlist1[i])))
          { scratchpad2[i] = 1;
            if(!count2)
               u = atomlist1[i];
            else
               p = atomlist1[i];
            ++count2;
          }
     }
  free2(atomlist1);
  free2(atomlist2);
  free2(scratchpad1);
  free2(scratchpad2);
  if(count1 != count2)
     return 0;
  if(count1 == 0)
     return 0;
  if(count1 == 1)
     { subst(v,u,t,&temp);
       return equals(temp,s);
     }
  if(count1 == 2)
     { subst(v,u,t,&temp);
       subst(q,p,temp,&temp2);
       if(equals(temp2,s))
          return 1;
       /* now try the other order of renaming */
       subst(v,p,t,&temp);
       subst(q,u,temp,&temp2);
       return equals(temp2,s);
     }
  return 0;
}
/*____________________________________________________________________*/
static int arf_aux(term t)
/* return 1 if t contains a root of a power or a power of a root
other than:
   an even power of a square root,
   a multiple-of-nth power of an nth-root,
   a square root of an integer power,  ( so sqrt(2^3) is OK )
   nth-root of an nth power.
*/
{ int i,err;
  unsigned short n,f;
  term u,p,q,index;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  n = ARITY(t);
  if(f == SQRT)
     { u = ARG(0,t);
       if(FUNCTOR(u) == '^' && !isinteger(ARG(1,u)))
          return 1;
       return 0;
     }
  if(f == ROOT)
     { index = ARG(0,t);
       u = ARG(1,t);
       if(FUNCTOR(u) == '^' && !equals(ARG(1,u),index))
          { err = cancel(ARG(1,u),index,&p,&q);
            if(err)
               return 1;
            if(!isinteger(q))
               return 1;
          }
       return 0;
     }
  else if(f == '^' && FUNCTOR(ARG(0,t)) == SQRT && !iseven(ARG(1,t)))
     return 1;
  else if(f == '^' && FUNCTOR(ARG(0,t)) == ROOT)
     { index = ARG(0,ARG(0,t));
       err = cancel(ARG(1,t),index,&p,&q);
       if(err || !isinteger(q))
           return 1;
     }
  for(i=0;i<n;i++)
     { if(arf_aux(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*____________________________________________________________________*/
static void adjust_radicalflag(term t)
/* if t contains powers of roots, roots of powers, or
roots with different indices but the same argument in a product,
set radicalflag to -1. But, not if the topic is cubic equations.
   Also, if t is an equation and the eigenvariable occurs
only once, as in root(5,x^2) = 5, do nothing.  Similarly
if t is an OR and each argument is collected.
   If t contains no fractional exponents except 1/2, and no ROOT
terms, set radicalflag to 1, so e.g. (2^(1/2)-1)/2^(1/2) will get
the fractional exponents changed to sqrts.
*/
{ unsigned short f;
  int i;
  unsigned short n;
  int currenttopic = get_currenttopic();
  if(currenttopic == _cubic_one_root ||
     currenttopic == _complex_cubics
    )
     return;  /* without doing anything */
  f = FUNCTOR(t);
  if((f == '=' || f == MULTIPLICITY) && collected(t) <= 1)
     return;
  if(FUNCTOR(t) == OR)
     { n = ARITY(t);
       for(i=0;i<n;i++)
          { if(collected(ARG(i,t)) > 1)
              break;
          }
       if(i==n)
          return;
     }
  if(arf_aux(t) || different_roots(t)|| introduce_fractional_exponents(t))
     { set_radicalflag(-1);
       return;
     }
  if(arf_aux2(t))
     { set_radicalflag(1);
       return;
     }
}
/*______________________________________________________________*/
static int arf_aux2(term t)
/* If t contains no fractional exponent other than 1/2,
and no ROOT, and no nested exponents, and no DIFF, INTEGRAL, LIMIT,
EVAL, or SUM, return 1.  Return 0 otherwise.
*/
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 1;
  f = FUNCTOR(t);
  if(f == INTEGRAL || f == DIFF || f == LIMIT || f == EVAL || f == SUM)
     return 0;
  if(f == '^')
     { if(ONEHALF(ARG(1,t)))
          return 1;
       if(contains(ARG(1,t),'/') || contains(ARG(1,t),'^'))
          return 0;
       if(contains(ARG(0,t),'^'))
          return 0;
       return 1;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(!arf_aux2(ARG(i,t)))
          return 0;
     }
  return 1;
}




/*_____________________________________________________________________*/
#if 0 // No longer used
static int nindices(term t, term *a, term *b)
/* return the number of distinct indices of ROOT or SQRT
subterms in t, if this is 0, 1, or return 2 for "two or more".
If the number of such subterms is at least 2, then *a and
*b are instantiated to the indices; if it's 1, then *a is
instantiated.  SQRT counts as index two.
*/

{ int i,r,retval;
  term p,q,temp;
  unsigned short f,n;
  if(ATOMIC(t))
     return 0;
  retval = 0;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { r = nindices(ARG(i,t),&p,&q);
       if(r == 0)
          continue;
       if(retval == 0 && r == 1)
          { *a = p;
            retval = 1;
          }
       else if(retval == 1 && r == 1 && !equals(p,*a))
          { *b = p;
            return 2;
          }
       else if(retval == 1 && r == 2 && equals(p,*a) && !equals(q,*a))
          { *b = q;
            return 2;
          }
       else if(retval == 0 && r == 2)
          { *a = p;
            *b = q;
            return 2;
          }
     }
  f = FUNCTOR(t);
  if(f == ROOT || f == SQRT)
     { temp = f == ROOT ? ARG(0,t) : two;
       if(retval)
          { if(equals(*a,temp))
               return 1;
            *b = temp;
            return 2;
          }
       *a = temp;
       return 1;
     }
  return retval;
}
#endif
/*__________________________________________________________________________*/
static int different_roots(term t)
/* return 1 if t contains a product or quotient
with two root or sqrt subterms with
different indices, but the same arg, counting SQRT as index two.
The return value 1 will be used to indicate that sqrts or roots
should be converted to fractional exponents.
*/
{ term a;
  unsigned short n;
  int i;
  if(ATOMIC(t))
     return 0;
  if(FUNCTOR(t) == '*')
     return sqrt_exp_aux(t);
  /* examples: in sqrt((x+1)(x+2))/sqrt(x+1), we DO NOT need fractional
     exponents.  But if num or denom is a product, such as
     sqrt((x+1)(x+2))/ (x sqrt(x+1)), it is probably better to use
     fractional exponents than change x to x^2, put it under the sqrt,
     pull the sqrt out of the fraction, and then get x^2 back out from
     under the sqrt. */
  if(FUNCTOR(t) == '/' &&
     (FUNCTOR(ARG(0,t)) == '*' || FUNCTOR(ARG(1,t)) == '*')
    )
     { SETFUNCTOR(t,'*',2);
       a = topflatten(t);
       return sqrt_exp_aux(a);
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(different_roots(ARG(i,t)))
          return 1;
     }
  return 0;
}

/*_______________________________________________________________________*/
static int power_of_eigenvariable(term t)
/* return 1 if t is the eigenvariable, or a power of the
eigenvariable, or a constant multiple thereof.
Return 0 otherwise.
*/
{ term x = get_eigenvariable();
  term c,s;
  if(equals(t,x))
     return 1;
  if(FUNCTOR(t) == '^' && equals(ARG(0,t),x))
     return 1;
  if(FUNCTOR(t) == '*')
     { ratpart2(t,&c,&s);
       if(!ONE(c))
          return power_of_eigenvariable(s);
       return 0;
     }
  return 0;
}
/*_______________________________________________________________________*/
static int select_arg(term t)
/* t is an equation, product, or sum.  Determine which arg of t to simplify
 first, and return the arg number.
 */
{ term coef;
  double z,zold;
  int i,err,ans;
  int trigexpandflag = get_trigexpandflag();
  unsigned short n;
  unsigned short trigflag;
  n = ARITY(t);
  zold = 0.0;
  ans = 0;
  /* does any arg contain compound fractions?  If so take that one first. */
  /* This seems pretty inefficient, indeed making term traveral quadratic
  instead of linear in the size of the term.  But it looks good! */
  for(i=0;i<n;i++)
     { if(contains_compound_fractions(ARG(i,t)))
          return i;
     }
  if(!(trigexpandflag & 1))  /* all trig args the same */
     return 0;   /* just use leftmost-first */
  /* First simplify fractions whose num and denom both contain trig functions */
  /* Example:  tan((x+y)/2)tan((x-y)/2) = -(cos x - cos y)/(cos x + cos y),
     do NOT simplify the left side first just because it contains TAN. */
  for(i=0;i<n;i++)
     { if(contains_trig_fraction(ARG(i,t)))
          return i;
     }
  set_trigflag(t,&trigflag);
  if(
     (INDICATES(trigflag,SIN) || INDICATES(trigflag,COS)) &&
     !(trigexpandflag & (1 << 12))  /* not only COS and SIN occur in the
             current line, but they do occur in this very term. */
    )
     { for(i=0;i<n;i++)
          { if(contains_other_trig(ARG(i,t)))
               return i;
          }
     }
  /* Once everything is in terms of SIN or COS, choose the arg with
     the maximum trigarg, i.e. work on sin 6x before sin 2x, etc. */
  /* 1.25.25.  That scheme gives wrong solutions on
     trig identity problem 2,  namely (cos(x)-cos(5x)) (cos(x)+cos(5x)) = sin(4x) sin(6x),
     where the right side has the max trigarg, but the left side should be simplified
     using two factor args and the right side should NOT be hit with sinsin.
     So I added the next few lines before going with maxtrigargs.
  */
  if(FUNCTOR(t) == '=')
     { // then pick the side that contains '+' if only one does
       if(contains(ARG(0,t),'+') && !contains(ARG(1,t),'+'))
          return 0;
       if(contains(ARG(1,t),'+') && !contains(ARG(0,t),'+'))
          return 1;
     }
  for(i=0;i<n;i++)
     { coef = zero;
       err = maxtrigargs(ARG(i,t),&coef);
       if(err == 1)
          return 0;
       if(err == 2)
          continue;
       deval(coef,&z);
       z = fabs(z);
       if(z != BADVAL && z - VERYSMALL > zold)
         { zold = z;
           ans = i;
         }
     }
  return ans;
}
/*__________________________________________________________________*/
static int maxtrigargs(term t, term *ans)
/* Compute the max coefficient of the args of all the trig functions in t
and the initial value of *ans, where the coefficient of each trigarg
is its 'ratpart2' if it's a product, and the max of the coefficients if
it's a sum.  So the 'coefficient' in sin(2x +y) is 2.
In that case, put the max coefficient in *ans, and return 0.  If some trig args
don't have that form, return 1.  If there are no trig functions in t,
return2.  It is
assumed that x is a variable.  Initially, *ans should be zero when this is
called by other functions.  (In recursive calls it will take other values
used for the computation.)
*/
{ unsigned short n,f;
  int i,err,retval;
  double z,z2;
  term c,s,u,v;
  if(ATOMIC(t))
     return 2;
  f = FUNCTOR(t);
  if(TRIGFUNCTOR(f))
     { u = ARG(0,t);
       if(NEGATIVE(u))
          u  = ARG(0,u);
       if(ISATOM(u))
          { deval(*ans,&z);
            if(z == BADVAL)
               return 1;
            if(z < 1.0 + VERYSMALL)
               *ans = one;
            /* else *ans is unchanged */
            return 0;
          }
       if(FUNCTOR(u) == '+')
          { n = ARITY(u);
            z = 0.0;
            *ans = zero;
            for(i=0;i<n;i++)
               { v = ARG(i,u);
                 if(NEGATIVE(v))
                     v = ARG(0,v);
                 if(ISATOM(v) && z < 1.0)
                    { z = 1.0;
                      *ans = one;
                    }
                 if(FRACTION(v) || FUNCTOR(v) == '*')
                    { ratpart2(v,&c,&s);
                      deval(c,&z2);
                      z2 = fabs(z2);
                      if(z2 != BADVAL || z2 > z)
                        { z = z2;
                          *ans = c;
                        }
                    }
               }
            return z == 0.0 ? 1 : 0;
          }
       if(FUNCTOR(u) == '/' || FUNCTOR(u) == '*')
          { ratpart2(u,&c,&s);
            if(NEGATIVE(c))
               c = ARG(0,c);
            if(seminumerical(c))
               { deval(c,&z);
                 if(z == BADVAL)
                    return 1;
                 deval(*ans,&z2);
                 if(z2 == BADVAL)
                    return 1;
                 if(z > z2)
                    *ans = c;
                 return 0;
               }
            return 1;
          }
       return 1;
     }
  n = ARITY(t);
  retval = 2;
  for(i=0;i<n;i++)
     { err = maxtrigargs(ARG(i,t),ans);
       if(err == 1)
          return 1;
       if(err == 0)
          retval = 0;
     }
  return retval;
}

/*_________________________________________________________________________*/
static int contains_other_trig(term t)
/* return 1 if t contains COT, TAN, CSC, or SEC.  Return 0 otherwise. */
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == COT || f == TAN || f == SEC || f == CSC)
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_other_trig(ARG(i,t)))
           return 1;
     }
  return 0;
}

/*_________________________________________________________________________*/
static int contains_trig_fraction(term t)
/* Return 1 if t contains a fraction whose numerator and denom both
contain sums of trig functions or products of trig functions,
possibly with coefficients.
  Return 0 otherwise.
Assumes t does not contain compound fractions.
*/

{ term u,v,c,s;
  int i,j,k;
  unsigned short f,n,m;
  if(ATOMIC(t))
     return 0;
  if(FRACTION(t))
     { for(i=0;i<2;i++)
          { u = ARG(i,t);
            if(FUNCTOR(u) == '+')
               { n = ARITY(u);
                 for(j=0;j<n;j++)
                    { v = ARG(j,u);
                      if(NEGATIVE(v))
                         v = ARG(0,v);
                      if(FUNCTOR(v) == '*')
                         { ratpart2(v,&c,&s);
                           v = s;
                         }
                      if(FUNCTOR(v) == '*')
                         { m = ARITY(v);
                           for(k=0;k<m;k++)
                              { f = FUNCTOR(ARG(k,v));
                                if(!TRIGFUNCTOR(f))
                                   return 0;
                              }
                           continue;
                         }
                      f = FUNCTOR(v);
                      if(!TRIGFUNCTOR(f))
                         return 0;
                    }
               }
          }
       return 1;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_trig_fraction(ARG(i,t)))
          return 1;
     }
  return 0;
}

/*________________________________________________________________________*/
int contains_hypertrig(term t)
/* return 1 if t contains a hypertrig function, 0 if not */
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == SINH || f == COSH || f == TANH || f == COTH || f == SECH || f == CSCH)
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_hypertrig(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*________________________________________________________________________*/
static int contains_hypertrig2(term t)
/* return 1 if t contains TANH, COTH,SECH, or CSCH; 0 if not */
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == TANH || f == COTH || f == SECH || f == CSCH)
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_hypertrig2(ARG(i,t)))
          return 1;
     }
  return 0;
}
/*________________________________________________________________________*/
static int count_logs(term t)
/* return the number of LN, LOG, LOGB occurrences in t.  Don't
count nested occurrences.  Count a log of a product double so
we don't expand it again.
*/
{ unsigned short n,f;
  int i,count,k;
  if(NEGATIVE(t))
     t = ARG(0,t);
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == LN || f == LOG)
     { if(FUNCTOR(ARG(0,t)) == '*')
          return 2;
       return 1;
     }
  if(f == LOGB)
     { if(FUNCTOR(ARG(1,t)) == '*')
          return 2;
       return 1;
     }
  n = ARITY(t);
  if(f == '*' || f == '/')
     { /* return the max of count_logs(ARG(i,t)) */
       count = 0;
       for(i=0;i<n;i++)
          { k = count_logs(ARG(i,t));
            if(k > count)
               count = k;
          }
       return count;
     }
  if(f == '+')
     { /* return the total of count_logs(ARG(i,t)) */
       count = 0;
       for(i=0;i<n;i++)
          count += count_logs(ARG(i,t));
       return count;
     }
  if(f == '^')
     return count_logs(ARG(0,t));
  return 0;   /* e.g., on  sin(log x))  */
}

/*__________________________________________________________________*/
static int contains_expandable_denom(term t)
/* return 1 if t contains a fraction whose denom is a power
of a sum or contains a factor which is a (low) power of a sum.
*/
{ unsigned short n;
  int i;
  term u;
  if(NEGATIVE(t))
     t = ARG(0,t);
  if(ATOMIC(t))
     return 0;
  if(FRACTION(t))
     { u = ARG(1,t);
       if((FUNCTOR(u) == '*' || FUNCTOR(u) == '^') &&
           expandable(ARG(1,t))
          )
         return 1;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_expandable_denom(ARG(i,t)))
          return 1;
     }
  return 0;
}

/*________________________________________________________________________*/
static int simple_enough(term t)
/* return 1 if t is simple enough that automode should not
go on trying to simplify it, under problemtype SIMPLIFY.
Return 0 otherwise.  Assumes it's already been through one pass
of autosimp.  Pay no heed to defined variables, they will be
unwound anyway.
*/

{ unsigned short f = FUNCTOR(t);
  unsigned short n = ARITY(t);
  int i,nvars;
  term *atomlist;
  term u;
  if(NEGATIVE(t))
     { t = ARG(0,t);
       f = FUNCTOR(t);
     }
  if(f == '^' && INTEGERP(ARG(1,t)))
     t = ARG(0,t);
  if(ATOMIC(t))
     return 1;
  if(n == 1 && simple_enough(ARG(0,t)))
     return 1;  /* it's been through one pass of autosimp */
  if(f == '=')
     { /* problemtype is set to SIMPLIFY temporarily to simplify solutions
          of equations */
       return simple_enough(ARG(0,t)) && simple_enough(ARG(1,t));
     }
  if(f == '*')
     { for(i=0;i<n;i++)
          { u = ARG(i,t);
            if((i == 0 && RATIONALP(u)) || OBJECT(u))
               continue;
            if(FUNCTOR(u) == '^' && INTEGERP(ARG(1,u)))
               u = ARG(0,u);
            if(ISATOM(u))
               continue;
            if(is_linear(u)==0) /* is_linear returns 0 if u is linear */
               continue;
            if(ARITY(u) == 1 && simple_enough(ARG(0,u)))
               continue;
            if((FUNCTOR(u) == LOGB  || FUNCTOR(u) == ROOT) && simple_enough(ARG(1,u)))
               continue;
            nvars = variablesin(u,&atomlist);
            if(nvars == 1 && ispolyin(u,atomlist[0]))
               { free2(atomlist);
                 continue;
               }
            free2(atomlist);
            return 0;
          }
       return 1;
     }
  if(f == '+' || f == '/')
     { for(i=0;i<n;i++)
          { if(!simple_enough(ARG(i,t)))
                return 0;
          }
       return 1;
     }
  return 0;
}

/*______________________________________________________________________*/
int contains_calc(term t)
/* return 1 if t contains LIMIT, DIFF, or INTEGRAL */
{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == LIMIT || f == DIFF || f == INTEGRAL)
     return 1;
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(contains_calc(ARG(i,t)))
          return 1;
     }
  return 0;
}


/*_________________________________________________________________________*/
term part(term t, unsigned short *path)
/* return the subterm of t located by path.  The
members of path are [functor, argnumber+1], and
path terminates with a NULL.  Example,
part(sin(x^2 + 1), [SIN,1,+,2]) = one.
If the functors disagree or the path "runs out"
of t, then an ILLEGAL term is returned.
   To be as described, path must be of even length;
that is, the terminating null is at path[k] for an odd k.
It is assumed that path is a "string" of unsigned shorts,
with path[k] a valid reference until the terminating 0.
*/
{ term ans;
  start:  /* label for tail recursion */
  if(path[0] == 0 || path[1] == 0)
     /* In principle path[1] can't be zero, path must be of even length */
     return t;
  if(ATOMIC(t) || FUNCTOR(t) != path[0] || path[1] > ARITY(t))
     { SETFUNCTOR(ans,ILLEGAL,0);
       return ans;
     }
   /*  return part(ARG(path[1]-1,t),path+2);
       following code optimizes this tail recursion */
  t = ARG(path[1]-1,t);
  path += 2;
  goto start;
}
/*______________________________________________________________________*/
static int introduce_fractional_exponents(term t)
/* t is an expression containing ROOT and/or SQRT, but no
fractional exponents. Return 1 if it's sufficiently complicated
that we should go to fractional exponents to simplify it.
Return 0 otherwise.
Example:  Return 1 on sqrt ( root(6, x^8 y^14)).
*/
{ unsigned short n,f,m;
  int i,err;
  term u,v,index,p,q,w;
  int currenttopic = get_currenttopic();
  char buffer[DIMREASONBUFFER];
  if(ATOMIC(t))
     return 0;
  if(currenttopic == _cubic_one_root ||
     currenttopic == _complex_cubics
    )
     return 0;   /* no matter what, don't use fractional exponents on
                    cubic equations. */
  if(seminumerical(t))
     return 0;   /* leave numerical expressions involving roots alone */
  if(status(sqrtexp)==UNKNOWN)
     return 0;   /* it's set this way throughout Algebra 1 */
  n = ARITY(t);
  f = FUNCTOR(t);
  if(f == ROOT && contains_sqrt(ARG(1,t)))
      return 1;  /* nested roots */
  if(f == SQRT && contains_sqrt(ARG(0,t)))
      return 1;  /* more nested roots */
  if(f != ROOT && f != SQRT)
     { for(i=0;i<n;i++)
          { if(introduce_fractional_exponents(ARG(i,t)))
               return 1;
          }
       return 0;
     }
  u = f == ROOT ? ARG(1,t) : ARG(0,t);
  index = f == ROOT ? ARG(0,t) : two;
  if(FUNCTOR(u) == '*' && collectterms(u,zero,&w,buffer))
     { /* does t contain a power whose exponent is not congruent to
          0 or 1 mod index?  */
       m = ARITY(u);
       for(i=0;i<m;i++)
          { v = ARG(i,u);
            if(FUNCTOR(v) != '^')
               continue;
            if(ISINTEGER(ARG(1,v)) && ISINTEGER(index))
               { long kk = INTDATA(ARG(1,v));
                 long mm = INTDATA(index);
                 long nn = kk % mm;
                 if(nn != 0 && nn != 1)
                    return 1;
                 else
                    return 0;
               }
            err = cancel(ARG(1,v),index,&p,&q);
            if(err || !isinteger(q))
               { /* it doesn't cancel completely */
                 polyval(sum(ARG(1,v),minusone),&w);
                 err = cancel(w,index,&p,&q);
                 if(err || !isinteger(q))
                    return 1;
               }
          }
     }
  return 0;
}
/*_____________________________________________________________*/
static int csi_aux(term t, term *p)
/* if FUNCTOR(p) is ILLEGAL, find the first integral in t
and make that the value of *p.  Continue searching t (or start
if FUNCTOR(p) was already not ILLEGAL) and stop only when an
integral is found that is not equal to a constant times *p.
If such is found, return 1.  If no integral is found different
from *p, or no first integral is ever found, return 0.
*/
{ unsigned short n;
  int i,err;
  term x,c,s,q;
  if(ATOMIC(t))
     return 0;
  n = ARITY(t);
  if(FUNCTOR(t) == INTEGRAL)
     { x = ARG(1,t);  /* variable of integration */
       twoparts(ARG(0,t),x,&c,&s);
       if(!ONE(c))
          { if(n == 2)
               q = integral(s,x);
            else
               q = definite_integral(s,x,ARG(2,t),ARG(3,t));
          }
       else
          q = t;
       if(FUNCTOR(*p) == ILLEGAL)
          { *p = q;
            return 0;
          }
       if(equals(q,*p))
          return 0;
       return 1;
     }
  for(i=0;i<n;i++)
     { err = csi_aux(ARG(i,t),p);
       if(err)
          return 1;
     }
  return 0;
}
/*_____________________________________________________________*/
static int contains_same_integral(term t)
/* return 0 if t contains more than one different integral,
counting integrands different only by a constant factor as the same.
Return 1 if it contains only copies of the same integral (or
no integral at all).
*/
{ term p;
  int err;
  SETFUNCTOR(p,ILLEGAL,0);
  err = csi_aux(t,&p);
  return !err;
}

/*____________________________________________________________*/
void setimproper(term *t)
/* set the IMPROPER bit on all definite integrals contained in t */
{ unsigned short n = ARITY(*t);
  int i;
  if(ATOMIC(*t))
     return;
  if(FUNCTOR(*t) == INTEGRAL && n == 4)
     { SETIMPROPER(*t);
       return;
     }
  for(i=0;i<n;i++)
     setimproper(ARGPTR(*t) + i);
}
/*_____________________________________________________________*/
static int transitionop(actualop code)
/* return 1 if code is an operation used to shift
between subcalculations, such as the convergence test
operations.  In this case, update_assumptions does not
replace occurrences of the old line by occurrences of
the new line in the assumption list.
*/
{ operation op;
  code_to_op(code,&op);
  if(op.men == series_convergence_tests || op.men == series_convergence2)
     return 1;
  if(op.men == minima_and_maxima || op.men == related_rates)
     { if((void  *) code == (void  *) derivop)
          return 0;
       else
          return 1;
     }
  if(op.men == integrate_by_substitution)
     { if((void  *) code == (void  *) derivop)
          return 0;
       if((void  *) code == (void  *) intsub)
          return 0;
       if((void  *) code == (void  *) autointsub)
          return 0;
       return 1;
     }
  if((void  *) code == (void  *) difeqn)
     return 1;
  if((void  *) code == (void  *) equatetoproblem)
     return 1;
  return 0;
}
/*__________________________________________________________*/
static int initial_focus(term t,term *ans)
/* Place in *ans the leftmost derivative, integral, or limit in t,
or product or negation containing a derivative, integral, or limit,
that is a proper subterm of t,  and return 0, UNLESS there are some 
simplifications that should be performed before that derivative, integral, or limit is worked on,
in which case return nonzero.   Return values more than 1 are used only in recursive
calls; calling function should expect 0 or 1.
Also set 'path' and 'pathlength' to the path to p, not including the functor of p itself.
Return 1 if there is no such subterm of t.   Ensure that path[pathlength] == 0 on return. 
   Also return 1 if you encounter a double negation or an unflattened sum as these should 
be simplified first, or a product a(b+c) where b or c contains an integral, limit, or derivative,
as those should be multiplied out first.
*/
{ int i,n,j,err;
  unsigned short f;
  term u;
  if(ATOMIC(t))
      return 1;
  f = FUNCTOR(t); 
  n = ARITY(t);
   // see changes 1829, 1830 for the reasons for this next 'if' statement
  if(f == '*' && contains_calc(t))
     { /* check for an undistributed product in which the summands don't contain another '+' */
       int failflag = 0;
       for(i=0;i<n;i++)
          { u = ARG(i,t);
            if(FUNCTOR(u) == '+' && contains_calc(u))
                failflag = 1;  // force failure unless we find a summand containing '+' but not LIMIT, DIFF, or INTEGRAL
            if(FUNCTOR(u) != '+')
                continue;
            for(j=0;j<ARITY(u);j++)
               { if(contains_calc(ARG(j,u)))
                    continue;
                 if(contains(ARG(j,u),'+'))
                    { failflag = 0;
                      break;
                    }
               }
            if(j < ARITY(u))
               break;
          }
       if(failflag)
          return 2;
    }
  if(f == DIFF || f == INTEGRAL || f == LIMIT || 
     ( f == '*' && contains_calc(t)) 
     )
     { *ans = t;
       path[pathlength] = 0;
       return 0;
     }
  if(NEGATIVE(t) && NEGATIVE(ARG(0,t)))
      return 2;  // go ahead and simplify first
  if(f == '+')
     { for(i=0;i<n;i++)
          if(FUNCTOR(ARG(i,t)) == '+')   
              return 2;  // first flatten the expression
     }      
  path[pathlength]= f; 
  ++pathlength;
      
  for(i=0;i<n;i++)
     { path[pathlength] = i+1;
       ++pathlength;
       err = initial_focus(ARG(i,t),ans);
       if(!err)
           { path[pathlength] = 0;
             return 0;
           }
       if(err > 1)
           { pathlength-=2;
             path[pathlength] = 0;   // 9.1.07 
             return err;
           }
       --pathlength;
     }
  --pathlength;
  path[pathlength] = 0;
  return 1;
}  


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