Sindbad~EG File Manager

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

/* M. Beeson, for Windows Mathpert */
/* Execute a given operator, if possible, making changes in the
data structures stored in the document's heap (and referenced in the DLL's).
/*
7.19.94 original date
1.13.99 last modified
12.30.99 copied a block of code from silent_step into finish_exec, just after the call to
constants_of_integration.
2.3.00  modified embed_comment
3.8.00 added GetTransform
1.18.06 changed handles to pointers etc.
1.18.06 changed access to papyrus to use document instead of GetWindowLong
1.19.06 made execute, finish_exec, wget_arg, wget_two_args, wget_indexarg all take PDOCDATA instead of HWND.
*/
#define AUTOMODE_DLL
#include <string.h>
#include <assert.h>
#include <stdlib.h>    /* itoa                 */
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "document.h"
#include "checkarg.h"   /* typedef 'condition' */
#include "execute.h"
#include "getarg.h"
#include "cflags.h"
#include "prover.h"     /* interval_as_and     */
#include "exec.h"       /* exec                */
#include "integral.h"   /* constants_of_integration */
#include "operator.h"   /* names of menus      */
#include "parser.h"
#include "automode.h"   /* one_step            */
#include "eqn.h"        /* delete_false        */
#include "pvalaux.h"    /* topflatten          */
#include "optable.h"
#include "factor.h"     /* mark_irreducibles   */
#include "cflags.h"     /* get_comment_buffer, get_error_buffer */
#include "bigrect.h"
#include "errbuf.h"
#include "docdata.h"
#include "getprob.h"    /* get_vars_from_user  */
#include "probtype.h"
#include "ops.h"        /* changesigns         */
#include "calc.h"       /* difeqn              */
#include "mpmem.h"     /* save_and_reset       */
#include "autosimp.h"  /* GetShowStepOperation */
#include "trig.h"
#include "activedoc.h" /* GetActiveDoc */

static int integrationop(operation op);
static int totalfailures;  /* counts the total number of failed
                              operators in this Mathpert session */
static void improve(term *t);
static void adjust_eigenvariable(term t);
static void adjust_papyrus_for_comment(PDOCDATA);
static int contains_integral(term t, term *integrand, term *x);
/*____________________________________________________________________*/
MEXPORT_AUTOMODE int execute(PDOCDATA pDocData, operation op)
/* Execute the given operator on history(currentline), or
more precisely on history(activeline), which usually is
history(currentline), but sometimes is not, as documented
under exec() in autosimp.c.

Returns nonzero if it fails, 0 if it succeeds. Return value 3 means,
do not display an error message; it's used when the user has pressed
Cancel in some dialog box to back out of executing this operator.
Return value 2 signifies that an operator failed on purpose, but
a new line of solution should still be generated.

If it succeeds (or fails on purpose)  update currentline
and history(currentline).  Calls needs_arg, get_arg, get_index_arg
as required, and then exec.  Does not do any display of the results
or error messages, but requires hwnd (which will be the handle of
the document window) in order to run the wget_arg and wget_index_arg
dialogs, and to get the highlight color.

It also clears the comment and error buffers at the beginning
in case something irrelevant was there.
*/
{ term arg,u;
  controldata p;
  char reason[DIMREASONBUFFER];
  int currentline,promptnumber;
  int err;
  char prompt[128];
  condition c;
  term t,next;
  currentline = get_activeline();
  t = history(currentline);
  clear_error_buffer();  /* make sure no irrelevant error messages are there */
  clear_comment_buffer();
  get_controldata(&p);
  if(
     (FUNCTOR(t) == OR || (FUNCTOR(t) == AND && !interval_as_and(t)))
     && p.selected_equation >= 0  /* negative means only one is
                                 visible so work on that one */
     && needs_index_arg(op)
    )
     { /* Are these equations or inequalities?  We need to set the
          prompt correctly before inquiring which one to work on.  */
       unsigned n = ARITY(t);
       unsigned i,nequations=0;
       unsigned f;
       if(get_problemtype() == RELATED_RATES)
          { term s = t;
            if(FUNCTOR(s) == OR)
               { SETFUNCTOR(s,AND,ARITY(s));
                 s = topflatten(s);
                 n = ARITY(s);
                 nequations = n;
                 /* in related rates there are two (sets of) equations
                    connected by an OR;  this is the total number of
                    equations. */
               }
          }
       else
          { for(i=0;i<n;i++)
               { u = ARG(i,t);
                 if(FUNCTOR(u) == MULTIPLICITY)
                    u = ARG(0,u);
                 f = FUNCTOR(u);
                 if(f == '=')
                    ++nequations;
                 else if(f == AND)
                    { if(!interval_as_and(u))
                         break;
                    }
                 else if(f != LE && f != GE && f != NE && f != '<' && f != '>')
                    break;
               }
            if(i<n)
                assert(0);
              /* an AND or OR must have each arg an equation or inequality;
                 or a MULTIPLICITY term whose 0th arg is an equation or inequality */
          }
       if(nequations == n)
          { promptnumber = 36;
            /* english(36) is Work on which equation? */
          }
       else if(nequations == 0)
          { promptnumber = 503;
            /* Work on which inequality? */
          }
       else
          { promptnumber = 504;
            /* Work on which equation or inequality? */
          }
       p.selected_equation = wget_indexarg(pDocData,n,promptnumber);
       if(p.selected_equation == 0)
          return 1;  /* user pressed Cancel */
       set_selected_equation(p.selected_equation);
     }
  if(p.selected_equation &&
     (FUNCTOR(t) == OR || (FUNCTOR(t) == AND && !interval_as_and(t))) &&
     ARITY(t) >= abs(p.selected_equation)
    )
     t = ARG(abs(p.selected_equation)-1,t);
  else if(p.selected_equation)  /* and the arity is too small or t isn't an OR or AND */
     /* 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);   /* p.selected_equation is only local */
  c = needs_two_args(op,&promptnumber);
  if(c)
     { err = wget_two_args(pDocData,c,&arg,promptnumber);
       if(err)         /* User backed out of dialog */
          return 3;    /* 3 says, don't generate an error message */
     }
  else
     { c = needs_arg(op, prompt);
       if(c)
          { err = wget_arg(pDocData,c,&arg,prompt);
            if(err)
               { /* User backed out of the GetArg dialog */
                 return 3;  /* 3 says, don't generate an error message */
               }
          }
       else
          SETFUNCTOR(arg,ILLEGAL,0); /* mark it as a dummy arg */
     }
  err = exec(op,arg,&next,reason);
  return finish_exec(pDocData,err,op,next,reason);
}

/*__________________________________________________________*/
static int integrationop(operation op)
/* return 1 if we should sometimes introduce a constant of integration
when op is used.  This is called from top.c.   Zero is returned for
operators that have an integral in the output so that a constant
of integration is never needed, as well as for operators that don't
integrate at all. */

/* Any new operators that transform integrals without eliminating them
should be added to this function */

{  if(op.men < basic_integration)
      return 0;
   if(op.men > series_manipulations)
      return 0;
   if(op.men >= series_geom1 && op.men < series_manipulations)
      return 0;
   switch(op.men)
     { case basic_integration:
          switch(op.choice)
             { case 4:
               case 5:
               case 6:
               case 7:  return 0;
               default: return 1;
             }
       case integrate_by_substitution:
          return 0;
       case trig_integration:
          return 1;
       case integrate_exp:
          if(op.choice == 7)  /* intexponential */
             return 0;
          return 1;
       case integrate_by_parts:
          if(op.choice == 8) /* simpleint */
             return 1;
          return 0;
       case definite_integration:
          return 0;
       case trig_substitutions:
          if(op.choice == 9) /* simpleint */
             return 1;
          return 0;
       case trigonometric_integrals:
          return 0;
       case integrate_rational:
          if(op.choice <= 7)
             return 0;
          return 1;
       case simplify_calculus:
          if(op.choice == 12)  /* simpleint */
             return 1;
          return 0;
       case integrate_hyperbolic:
          return 1;
       case series_manipulations:
          if(op.choice == 9)  /* intseries */
             return 1;
          return 0;
       case advanced_sigma_notation:
          if(op.choice == 9)  /* intsigma  */
              return 1;
          return 0;
     }
   return 1;
}
/*______________________________________________________________*/
static void improve(term *t)
/* remove any 'false' arguments from the OR term *next */
{ term temp,next;
  int err;
  next = topflatten(*t);
  err = delete_false(next,&temp);
  if(!err)
     *t = temp;
}
/*______________________________________________________________*/
MEXPORT_AUTOMODE void embed_comment(PDOCDATA pDocData, char *comment)
/* Embed the given comment in the document so it will be displayed
and printed.  Comment can contain explicit carriage returns (character 13)
pDocData points to the document data structure.
*/
{ int currentline = get_currentline();
  store_comment(pDocData,currentline,(unsigned char *)comment);
  /* now we must update the history list of permanent memory;
     this is used by undo to 'reset' permament memory. If we
     don't update it here, if a comment is added to line 8, say,
     then the comment which was attached to line 8 (by the above code,
     after line 8 was recorded), will be living in memory that belongs
     to line 9, and hence will be in limbo when line 9 is undone. */
  pDocData->DocProverData.permhistory[currentline] =
    pDocData->DocProverData.nextworkspace;
}

/*____________________________________________________________________*/
#define EQOPS(a,b) ((a).men ==(b).men && (a).choice == (b).choice)

MEXPORT_AUTOMODE int think_ahead(PDOCDATA pDocData,operation op, message *msg)
 /* use autosimp to take four steps of the computer's solution.
 It is assumed that 'op' has just failed, causing think_ahead to
 be called.  pDocData points to the active document data.
 Use the result to generate a message to the user explaining
 what should be done.  Only returns the message, doesn't actually print
 it.  Return value 1 if no message can be constructed, 0 if one can.
*/

{ int i,nsteps,flag;
  operation plan[4]; /* next four operators to be used by automode */
  static char buf1[DIMREASONBUFFER + 20];
  if(pDocData->DocControlData.finishedflag)
     return 1;   /* automode is done  */
  /* Run cogitate to generate the plan */
  nsteps = cogitate(pDocData,4,plan,&flag);
   /* flag tells whether automode completes
      or not; we don't care here. */
  if(nsteps == 0)
     { pDocData->DocControlData.finishedflag = 1;
       return 1;   /* automode is done */
     }
  for(i=0;i<nsteps;i++)
     { if(EQOPS(plan[i],op))
          break;  /* user's operator discovered */
       if(cousins(plan[i],op))
          break;  /* good enough, e.g. collect once and collect all */
     }
   if(i==nsteps)
      return 1; /* user's operator not among Mathpert's choices */
   if(i == 0)
      { /* User is using Term Selection, and the operator would
           have applied if a different focus had been chosen.
           This can't happen in menu mode or the user's operator
           would have applied.
        */
        msg->line[0] = english(744);
        msg->line[1] = english(745);
        /* That  operation is a good choice, but you should
        apply it to a different term than the one you selected. */
        msg->nlines = 2;
        return 0;
      }
/*    Now i-1 is how many steps the user has skipped.  But there may
      be duplicates in this list.  Let's get them out: */
   if(i==3)
     { if(EQOPS(plan[0],plan[1]))
           { plan[2] = plan[1]; --i;}
       else if(EQOPS(plan[1],plan[2]))
          --i;
       else if(EQOPS(plan[0],plan[2]))
          --i;
     }
  if(i==2)
     { if(EQOPS(plan[0],plan[1]))
          --i;
     }
  /* Now plan[0]...plan[i-1] are the non-duplicated ops the user missed */
  if(i==1)
     { msg->line[0] = english(790); /* That operation almost applies here, but */
       msg->line[2] = cmdmenu(plan[0].men)[plan[0].choice-1];
       if(msg->line[2][0] == '$')
          { msg->line[1] = english(1990); /* you must first prepare for it by using  */
            msg->nlines = 3;
          }
       else
          { msg->line[1] = english(791); /* you must first prepare for it by using $*/
            msg->line[3] = "$";
            msg->nlines = 4;
          }
       return 0;
     }
  if(i==2)
     { msg->line[0] = english(792); /* You are getting ahead of yourself here. */
       msg->line[1] = english(793); /* Before you are ready to use that operation, */
       msg->line[2] = english(795); /* you have to prepare the way by using */
       strcpy(buf1,cmdmenu(plan[0].men)[plan[0].choice-1]);
       strcat(buf1, " and ");
       msg->line[3] = buf1;
       msg->line[4] = cmdmenu(plan[1].men)[plan[1].choice-1];
       msg->nlines = 5;
       return 0;
     }
  else /* i==3 */
     { msg->line[0] = english(797); /* That operator won't work here, */
       msg->line[1] = english(798); /* but you seem to be on the right track */
       msg->line[3] = cmdmenu(plan[0].men)[plan[0].choice-1];
       msg->line[2] = english(1992);  /* You might try */
       msg->nlines = 4;
       return 0;
     }
}
/*____________________________________________________________________*/
MEXPORT_AUTOMODE int cogitate(PDOCDATA pDocData, int n, operation *plan, int *flag)
/*  take n more steps in the projected solution
and store the operators used in 'plan', which must have
dimension at least n.  Return the number of steps
actually taken.  Set *flag = 1 if automode completes,
0 if not.   Don't put any automode_only operators into plan,
use get_brother to replace them with menu-mode equivalents.
   If pDocData->DocControlData.showstepflag is nonzero at entrance
(which means it was called from showstep), then do NOT call reset_heap,
because that will destroy the paths in the list of selected terms in
the papyrus structure that have been allocated during cogitate.
Otherwise do call reset_heap, and in any case,
reset all of Mathpert's global machinery at exit, including the
document 'permspace' memory, to the condition it was in
when this function was called.
*/

{ int i,err,nsteps;
  operation op;
  actualop code;
  int savecurrentline = get_currentline();
  int activeline = get_activeline();

  int savedisplay = inq_display_on();
  controldata d,savecontroldata;
  vardata saveVarData;
  char savec[DIM_COMMENT_BUFFER][80];
  char savee[DIM_COMMENT_BUFFER][80];
  char *comment_buffer = get_comment_buffer(0);
  char *error_buffer = get_error_buffer(0);
  void  *savenode = heapmax();  /* might not be zero if there are
                                       selected terms */
  polyflags savepolyflags = pDocData->DocPolyData;
  int cofi_index = get_cofi_index();
  if(activeline != savecurrentline)
      set_currentline(activeline);
  savecontroldata = d = pDocData->DocControlData;
  saveVarData = pDocData->DocVarData;
  memcpy(savec,comment_buffer, 80 * DIM_COMMENT_BUFFER);
  memcpy(savee,error_buffer, 80 * DIM_COMMENT_BUFFER);
  *flag = 0;
  display_off();
  for(i=0;i<n;i++)
     { err = silent_step(pDocData,1);
       if(!err)
          code = GetShowStepOperation();
       else
          code = NULL;
       if(!d.showstepflag)
          reset_heap(savenode);
       if(err)
           { *flag = 1;
             break;
           }
       if(code)
          code_to_op(code,&plan[i]);
       else
          { op = d.opseq[get_currentline()];
            if(op.men == automode_only ||
               op.men == automode_only2 ||
               op.men == automode_only3
              )
               code_to_op(get_brother(op.men, op.choice-1),&plan[i]);
            else
               plan[i] = op;
          }
     }
  nsteps = i;  /* number of steps taken in auto mode */
  /* Now restore everything to the condition it was
     originally in;  */
  for(i=0;i<nsteps;i++)
     { undo_assumptions();
       undo_letdefns();
       undo_inhibitions();
       decrement_currentline();
     }
  set_cofi_index(cofi_index);
  pDocData->DocProverData.nextworkspace = pDocData->DocProverData.permhistory[savecurrentline];
  if(activeline != savecurrentline)
     set_currentline(savecurrentline);
  else if(savecurrentline != get_currentline())
     assert(0);
  pDocData->DocControlData = savecontroldata;  /* so unpack_flags isn't needed */
  pDocData->DocVarData = saveVarData;   /* including eigenvariable, nvariables, etc. */
  pDocData->DocPolyData = savepolyflags;
  if(savedisplay)
     display_on();
  memcpy(comment_buffer, savec, 80 * DIM_COMMENT_BUFFER);
  memcpy(error_buffer, savee,80 * DIM_COMMENT_BUFFER);
  return nsteps;
}
/*__________________________________________________________________*/
MEXPORT_AUTOMODE int silent_step(PDOCDATA pDocData, int flag)
/* Take one step in automode, updating the document structure but
not doing any visible display of the result.  (Intermediate
displays such as display_progress are turned on or off by
display_off(), display_on(), which control pDocData->display_on.
So 'silent_step' is a bit of a misnomer. )
   The second parameter, flag, is nonzero if this is
called from cogitate, in which case we should NOT embed comments
in the document data structure.
Return 0 for success,
1 if automode can't find anything to do.
   hwndDoc is the document window handle.
*/

{ int err;
  int currentline;
  unsigned char reason[DIMREASONBUFFER];
  operation op;
  controldata p = pDocData->DocControlData;
  unsigned char *comment;
  term next;
  term x,integrand;
  unsigned f,i;
  int savenextassumption = get_nextassumption();
  int savenextdefn = get_nextdefn();
  int nextdefn, nextassumption;
  int mathmode;
  char *errmsg;
  mathmode = p.mathmode;
  automode();
  for(i=0;i<p.nfailedops;i++)
     p.model[access_mod(p.failedops[i])].inhibited = 1;
     /* inhibit without pushing a node on the inhibitions list */
     /* This saves the time of trying these ops again in automode */
  err = one_step(pDocData,&next,reason,&op);
  for(i=0;i<p.nfailedops;i++)
     { p.model[access_mod(p.failedops[i])].inhibited=0;
       /* 'release' the inhibition created above */
     }
  get_controldata(&p);  /* one_step can change the control flags, maybe, so
                           retrieve them again now. */
  if(!err && in_loop(next,op)==2)
     /* next is the same as the previous line but a let_defn
        has been made in between. */

     { if(p.nfailedops < 4)
           { p.failedops[p.nfailedops]=access_optable(op.men)[op.choice-1];
             ++p.nfailedops;
           }
        else  /* replace the last failedop */
           p.failedops[p.nfailedops-1]=access_optable(op.men)[op.choice-1];
     }
  errmsg = get_error_buffer(0);
  if(!flag && errmsg[0] == '!')
     { /* this signals we should print the message in the solution */
       /* Note that such 'permanent' error messages should
          be only one line long. */
       embed_comment(pDocData,errmsg+1);  /* skip the '!'  */
       /* This embeds the comment in the document data structure, but
          does not get coordinates into the papyrus structure telling
          where the comment should be painted.  A comment that is left
          by an operator that succeeds is assigned coordinates in solution.c
          when processing the WM_NEWSTEP message. But one that is left
          by an error message is handled here.
        */
        adjust_papyrus_for_comment(pDocData);
     }
  if(err)
     { p.mathmode = mathmode;  /* restore original mode */
       pDocData->DocControlData = p;
       nextdefn = get_nextdefn();
       nextassumption = get_nextassumption();
       if(nextdefn != savenextdefn)
          { increment_currentline();
            undo_letdefns();
            decrement_currentline();
          }
       if(nextassumption != savenextassumption)
          { increment_currentline();
            undo_assumptions();
            decrement_currentline();
          }
       return err;  /* if !flag, this means automode is finished */
     }
  /* Now the operator worked */
  p.nfailedops = 0;
  f = FUNCTOR(next);
  if(f == AND)
     { for(i=0;i<ARITY(next);i++)
          { if(equals(ARG(i,next),false))
               { next = false;
                 f = FUNCTOR(next);
                 break;
               }
          }
       if(f == AND && contains_at_toplevel(next,TRUEFUNCTOR))
          { /* this can happen in RELATED_RATES */
            /* drop the 'true' expressions      */
            int k=0;
            term p = make_term(AND,ARITY(next));
            for(i=0;i<ARITY(next);i++)
               { if(equals(ARG(i,next),true))
                    continue;
                 ARGREP(p,k,ARG(i,next));
                 ++k;
               }
            if(k==0)
               { RELEASE(p);
                 next = true;
               }
            else if(k==1)
               { next = ARG(0,p);
                 RELEASE(p);
               }
            else
               { SETFUNCTOR(p,AND,k);
                 next = p;
               }
            f = FUNCTOR(next);
          }
     }
  else if(f == OR  && p.selected_equation >=0)
     improve(&next);
  else if(p.selected_equation < 0 )
     { int k = abs(p.selected_equation)-1;
       f = FUNCTOR(ARG(k,next));
       if(f==OR || f == AND)
          improve(ARGPTR(next) + (- p.selected_equation - 1));
          /* in case a selected equation has changed to an unflattened OR */
     }
  ++p.autosteps;
  p.localfailures = 0;  /* reset counter for number of wrong choices in a row */
  if(integrationop(op))
     /* supply constant(s) of integration if necessary */
     next = constants_of_integration(history(get_currentline()),next);
  else if(FUNCTOR(next) == '=' &&
          contains_integral(ARG(0,next),&integrand,&x) &&
          !contains(ARG(1,next),CONSTANTOFINTEGRATION) &&
          !contains(ARG(1,next),INTEGRAL)
         )
     { /* If we've just solved an equation for an integral, the right side needs
       to get a constant of integration.
       */
       next = equation(ARG(0,next),sum(ARG(1,next), constant_of_integration(integrand,x)));
     }
  else if(FUNCTOR(next) == '=' &&
          contains_integral(ARG(1,next),&integrand,&x) &&
          !contains(ARG(0,next),CONSTANTOFINTEGRATION) &&
          !contains(ARG(0,next),INTEGRAL)
         )
     { /* Theoretically the user could manually solve such an equation and wind up
          with the integral on the right and no constant of integration on the left.
          Let's get it right in that case too.
       */
       next = equation(sum(ARG(0,next), constant_of_integration(integrand,x)),ARG(1,next));
     }
  increment_currentline();  /* increment the REAL currentline */
  currentline = get_currentline();
  if(currentline >= (int) pDocData->DocProverData.maxhistory)
     assert(0);
  set_history(currentline,next);
  if(p.factorflag & 0x0100)
     mark_irreducibles(historyp(currentline));
  adjust_eigenvariable(next);
  pack_flags(currentline);   /* store control flags for use by undo */
  store_reason(pDocData,currentline,reason);
  pDocData->DocControlData.plan[0].choice = 0;
  pDocData->DocControlData.plan[0].men = 0;
  comment = get_permanent_comment();
  if(comment)
     { store_comment(pDocData, currentline, comment);
       free2(comment);
     }
  else
     store_comment(pDocData, currentline, NULL);
     /* it will be NULL to start with, but if undo has been done,
        it can't be assumed NULL, so in order to avoid the reappearance
        of a comment associated with an undone step, this is necessary.
     */
  pDocData->DocProverData.permhistory[currentline] = pDocData->DocProverData.nextworkspace;
  p.opseq[currentline] = op;
  p.mathmode = mathmode;
  pDocData->DocControlData = p;
  return 0;
}
/*__________________________________________________________________*/
MEXPORT_AUTOMODE int finish_exec(PDOCDATA pDocData,int err, operation op, term next, char *reason)
/* the user's operator has been tried.  If it failed, err is nonzero
and next and reason are garbage.  If it succeeded, err is zero and
next is the intended next line, with reason the justification string.
Update the document data structures appropriately, and return err.
hwnd is the document window.
*/
{ controldata p;
  int i;
  int currentline = get_currentline();
  int problemtype = get_problemtype();
  unsigned char *comment;
  unsigned short f;
  term integrand,x;
  get_controldata(&p);
  if(err==0 )
     { if(p.plan[0].choice == op.choice && p.plan[0].men == op.men)
          { /* ShowStep may have left an operation in p.plan[0]; if
               it's the same as the user's operation, we count
               that as if it were an automode step for loop-detection
               purposes.  Otherwise, ShowStep can go into a loop
               instead of duplicating the automode solution. */
            ++p.autosteps;
          }
       else
          p.autosteps = 0;
       p.localfailures = 0;  /* reset counter for number of wrong choices in a row */
       if(integrationop(op))
          /* supply constant(s) of integration if necessary */
          next = constants_of_integration(history(currentline),next);
       else if(FUNCTOR(next) == '=' &&
               contains_integral(ARG(0,next),&integrand,&x) &&
               !contains(ARG(1,next),CONSTANTOFINTEGRATION) &&
               !contains(ARG(1,next),INTEGRAL)
              )
          { /* If we've just solved an equation for an integral, the right side needs
            to get a constant of integration.
            */
            next = equation(ARG(0,next),sum(ARG(1,next), constant_of_integration(integrand,x)));
          }
       else if(FUNCTOR(next) == '=' &&
               contains_integral(ARG(1,next),&integrand,&x) &&
               !contains(ARG(0,next),CONSTANTOFINTEGRATION) &&
               !contains(ARG(0,next),INTEGRAL)
              )
          { /* Theoretically the user could manually solve such an equation and wind up
               with the integral on the right and no constant of integration on the left.
               Let's get it right in that case too.
            */
            next = equation(sum(ARG(0,next), constant_of_integration(integrand,x)),ARG(1,next));
          }
       f = FUNCTOR(next);
       if(f == AND)
          { for(i=0;i<ARITY(next);i++)
               { if(equals(ARG(i,next),false))
                    { next = false;
                      f = FUNCTOR(next);
                      break;
                    }
               }
            if(f == AND && contains_at_toplevel(next,TRUEFUNCTOR))
               { /* this can happen in RELATED_RATES */
                 /* drop the 'true' expressions      */
                 int k=0;
                 term p = make_term(AND,ARITY(next));
                 for(i=0;i<ARITY(next);i++)
                    { if(equals(ARG(i,next),true))
                         continue;
                      ARGREP(p,k,ARG(i,next));
                      ++k;
                    }
                 if(k==0)
                    { RELEASE(p);
                      next = true;
                    }
                 else if(k==1)
                    { next = ARG(0,p);
                      RELEASE(p);
                    }
                 else
                    { SETFUNCTOR(p,AND,k);
                      next = p;
                    }
                 f = FUNCTOR(next);
               }
          }
       else if(f == OR && p.selected_equation >=0)
          improve(&next);
       if((f == AND || f == OR) && p.selected_equation < 0 )
          { int k = abs(p.selected_equation)-1;
            unsigned short g = FUNCTOR(ARG(k,next));
            SET_SELECTED(ARG(k,next));
            if(f == OR && (g == AND || g == OR))
               improve(ARGPTR(next) + (- p.selected_equation - 1));
               /* in case a selected equation has changed to an unflattened OR */
          }
       if(problemtype == LINEAR_EQUATIONS &&
          f == AND &&
          !LINEUP(next) &&
          LINEUP(history(currentline)) &&
          (void *) access_optable(op.men)[op.choice-1] == (void *) changesigns
         )
          SET_LINEUP(next);
       if(problemtype == RELATED_RATES &&
          (void *) access_optable(op.men)[op.choice-1] == (void *) difeqn &&
          FUNCTOR(next) == OR
         )
          { SETFUNCTOR(next,AND,ARITY(next));
            next = topflatten(next);
          }
       increment_currentline();  /* increment the REAL currentline */
       ++currentline;            /* AND the local copy             */


       set_history(currentline,next);
       if(p.factorflag & 0x0100)
          mark_irreducibles(historyp(currentline));
       /* In some cases the eigenvariable can have been eliminated,
          e.g. if the last line was an OR of equations, only one of
          which contained the eigenvariable, and checkroot rejected
          that root.  We have to make sure the eigenvariable is
          changed if this happens. */
       adjust_eigenvariable(next);
       pack_flags(currentline);   /* store control flags for use by undo */
       store_reason(pDocData,currentline,(unsigned char *)reason);
       p.plan[0].choice = 0;
       p.plan[0].men = 0;
       /* ensure that whatever is there next time is not left over from now. */
       /* Note that we set p.plan rather than pDocData->DocControlData.plan
          because set_controldata(&p) is going to be called soon. */
       if(pDocData->DocControlData.selected_equation >= 0)
          p.selected_equation = 0;  /* set_controldata is going to be called soon */
       p.opseq[currentline] = op;
       /* There might be a comment marked 'permanent' with an initial
          exclamation point, which should be embedded in the solution now. */
       comment = get_permanent_comment();
       if(comment)
          { store_comment(pDocData, currentline, comment);
            free2(comment);
          }
       else
          store_comment(pDocData, currentline, NULL);
          /* it will be NULL to start with, but if undo has been done,
             it can't be assumed NULL, so in order to avoid the reappearance
             of a comment associated with an undone step, this is necessary.
          */
       pDocData->DocProverData.permhistory[currentline] = pDocData->DocProverData.nextworkspace;
       pDocData->justsaved = 0;
     }
  if(err==1)  /* cannot execute the operator chosen */
     { ++p.successivefailures;  /* documented in globals.c */
       ++p.localfailures;
       ++totalfailures;
       if(p.nfailedops < 4)
          { p.failedops[p.nfailedops]=access_optable(op.men)[op.choice-1];
            ++p.nfailedops;
          }
     }
  /* This allows for error values > 1 to exist but not be counted as
     failures, e.g. when a user interrupts a search for factors. */
  set_controldata(&p);
  return err;
}

/*_________________________________________________________________*/
static void adjust_eigenvariable(term t)
/* in case t does not contain the eigenvariable, reset the
eigenvariable by tracing back oldeigen through the definitions
until you come to a variable that is contained in t, or
until you can't go further back.
*/
{ defn *defns;
  int nextdefn;
  int i,count;
  int nvariables = get_nvariables();
  term *varlist;
  term x = get_eigenvariable();
  count = 0;
  if(get_pending())
     return;  /* this prevents changing the eigenvariable while we're
                 just simplifying a cubic discriminant or a substitution
                 in integration-by-substitution */
  while(!contains(t,FUNCTOR(x)) && count <= nvariables)
     { nextdefn = get_nextdefn();
       defns = get_defns();
       varlist = get_varlist();
       for(i=nextdefn-1;i>=0;i--)
          { if(FUNCTOR(defns[i].left) == FUNCTOR(x) ||
               (FUNCTOR(defns[i].left) == AND && contains(defns[i].left,FUNCTOR(x)))
              )
               break;
          }
       if(i < 0)
          { count = nvariables+1;  /* I do not think this can happen */
            break;
          }
       x = varlist[defns[i].oldeigen];
       ++count;
     }
  if(count == 0)
     return;  /* no need to reset eigenvariable */
  if(count <= nvariables) /* so t does contain x */
     { set_eigenvariable(defns[i].oldeigen);
       return;
     }
  /* Just set the eigenvariable to the first variable in t */
  for(i=0;i<nvariables;i++)
     { if(contains(t,FUNCTOR(varlist[i])))
          { set_eigenvariable(i);
            return;
          }
     }
  /* Now t is constant */
  set_eigenvariable(0);
}

/*________________________________________________________________________*/
static void adjust_papyrus_for_comment(PDOCDATA pDocData)
/* there may be a comment at the currentline that has not
got its coordinates assigned in the papyrus yet.  Fix that.
*/
{ Papyrus *pPapyrus;
  int currentline = get_currentline();
  coord shift;
  unsigned char *comment = get_comment(pDocData,currentline);
  if(!comment)
     return;
  pPapyrus = pDocData->papyrus;
  if(pPapyrus ->lines[currentline].comment.y)
     return;
  pPapyrus->lines[currentline+1].formula.x = (unsigned short)(pPapyrus->lines[currentline+1].formula.x + 32);
  pPapyrus->height = (unsigned short)(pPapyrus->height + 32);
  /* all such comments are only one line; we leave 16 coord units
     for one line, and 16 more for a space following the comment.
  */
  if(pPapyrus->lines[currentline+1].formula.x > pPapyrus->bottom)
     { shift =(unsigned short)(pPapyrus->lines[currentline+1].formula.x - pPapyrus->bottom);
       pPapyrus->top += shift;
       pPapyrus->bottom += shift;
     }
}


/*____________________________________________________________________*/
MEXPORT_AUTOMODE int autoans(term t, int n, term *ans, int *flag)
/* Run automode to termination on the currently active document.
or for n steps, or until a new variable is introduced in the
computation (but in the assumptions is OK) or a new definition is made,
whichever comes first.  Back up, not taking the step that made a
definition or introduced a new variable (e.g. binomialtheorem introducing
an indexed sum).
  Put the last line in *ans, put a nonzero value in *flag
if automode does terminate, and returning the number of steps taken.
*/

{ int i,err,nsteps,j,k;
  int savecurrentline = get_currentline();
  int savedisplay = inq_display_on();
  controldata savecontroldata;
  vardata saveVarData;
  PDOCDATA pDocData = (PDOCDATA) GetActiveDoc();
  int savenextdefn = get_nextdefn();
  term x = get_eigenvariable();
  term *atomlist1,*atomlist2;
  unsigned savenextworkspace = pDocData->DocProverData.nextworkspace;
  void  *savenode;
  int nvars1,nvars2;
  int savenextassumption = get_nextassumption();
  int nextassumption;
  char savec[DIM_COMMENT_BUFFER][80];
  char savee[DIM_COMMENT_BUFFER][80];
  char *comment_buffer = get_comment_buffer(0);
  char *error_buffer = get_error_buffer(0);
  polyflags savepolyflags;
  nvars1 = variablesin(t,&atomlist1); /* so atomlist1 lives below savenode */
  savenode = heapmax();  /* might not be zero if there are
                                       selected terms */
  increment_currentline();
  if(savecurrentline < 0)
      pDocData->DocProverData.permhistory[0] = savenextworkspace;
  permcopy(t,&pDocData->DocProverData.history[get_currentline()]);
  savepolyflags = pDocData->DocPolyData;
  savecontroldata = pDocData->DocControlData;
  saveVarData = pDocData->DocVarData;
  memcpy(savec,comment_buffer, 80 * DIM_COMMENT_BUFFER);
  memcpy(savee,error_buffer, 80 * DIM_COMMENT_BUFFER);
  *flag = 0;
  display_off();
  for(i=0;i<n;i++)
     { err = silent_step(pDocData,1);
       reset_heap(savenode);
       if(err)
           { *flag = 1;
             break;
           }
       if(get_nextdefn() > savenextdefn ||
          !equals(x,get_eigenvariable())
         )
           { undo_assumptions();
             undo_letdefns();
             undo_inhibitions();
             decrement_currentline();
             break;
           }
       nvars2 = variablesin(pDocData->DocProverData.history[get_currentline()],&atomlist2);
       /* is there any variable in atomlist2 which is not in atomlist1 ?  If so back
          up one step and return. */
       /* This can happen e.g. if binomialtheorem has been used. */
       for(j=0;j<nvars2;j++)
          { for(k=0;k<nvars1;k++)
               { if(equals(atomlist1[k],atomlist2[j]))
                    break;
               }
            if(k==nvars1)
               { /* variable not found */
                 undo_assumptions();
                 undo_letdefns();
                 undo_inhibitions();
                 decrement_currentline();
                 break;
               }
          }
       free2(atomlist2);
       if(j < nvars2)
          break;
     }
  nsteps = i;  /* number of steps taken in auto mode */

  save_and_reset(pDocData->DocProverData.history[get_currentline()],savenode,ans);
  /* Now restore everything to the condition it was
     originally in;  */
  for(i=0;i<nsteps;i++)
     { undo_assumptions();
       undo_letdefns();
       undo_inhibitions();
       decrement_currentline();
     }
  nextassumption = get_nextassumption();
  if(nextassumption != savenextassumption)
     assert(0);
  pDocData->DocProverData.nextworkspace = savenextworkspace;
  assert(savecurrentline +1 == get_currentline());
  pDocData->DocControlData = savecontroldata;  /* so unpack_flags isn't needed */
  pDocData->DocVarData = saveVarData;   /* including eigenvariable, nvariables, etc. */
  pDocData->DocPolyData = savepolyflags;
  if(savedisplay)
     display_on();
  memcpy(comment_buffer, savec, 80 * DIM_COMMENT_BUFFER);
  memcpy(error_buffer, savee,80 * DIM_COMMENT_BUFFER);
  decrement_currentline();  /* to match the original increment */
  free2(atomlist1);  /* it lives below savenode so save_and_reset didn't recover it */
  return nsteps;
}

/*___________________________________________________________________________*/
static int contains_integral(term t, term *integrand, term *x)
/* if t contains an indefinite integral either at toplevel, or
as a summand, or as a factor of a summand, return 1, with
the integrand and integration variable in *integrand and *x
If not return 0.
*/

{ unsigned short n,i;
  term u;
  int rval;
  if(FUNCTOR(t) == INTEGRAL && ARITY(t) == 2)
     { *x = ARG(1,t);
       *integrand = ARG(0,t);
       return 1;
     }
  if(FUNCTOR(t) == '*')
     { n = ARITY(t);
       for(i=0;i<n;i++)
          { u = ARG(i,t);
            if(FUNCTOR(u) == INTEGRAL && ARITY(u) == 2)
                { *x = ARG(1,u);
                  *integrand = ARG(0,u);
                  return 1;
                }
          }
       return 0;
     }
  if(FUNCTOR(t) == '-')
     return contains_integral(ARG(0,t),integrand,x);
  if(FUNCTOR(t) == '+')
     { n = ARITY(t);
       for(i=0;i<n;i++)
          { rval = contains_integral(ARG(i,t),integrand,x);
            if(rval)
               return rval;
          }
       return 0;
     }
  return 0;
}
/*_____________________________________________________________________________*/
MEXPORT_AUTOMODE int GetTransform(term a, term b, actualop *op, term *ans, char *reason)
/* find an operation that will transform a into b, or take it closer to b at least,
and return the operation in op,  the result of the operation (applied to a) in ans,
and the reason it gives in reason. Return 0 for success;
*/
{ actualop ops[50];
  int i=0,k,nops,err;
  term arg;
  SETFUNCTOR(arg,0,0);
  if(FUNCTOR(b) == '^' && equals(ARG(1,b),two) && 
     FUNCTOR(a) == '+'
    )
     { switch(FUNCTOR(ARG(0,b)))
          { case COS:
               ops[i] = sinsquare2; ++i;
               break;
            case SIN:
               ops[i] = sinsquare3; ++i;
               break;
            case SEC:
               ops[i] = tansquare1; ++i;
               break;
            case TAN:
               ops[i] = tansquare2; ++i;
               break;
          }
     }
  pre_ops(a,ops+i,&k);
  k += i;
  post_ops(a,&arg,ops+k,&nops);
  nops += k;
  for(i=0;i<nops;i++)
     { err = (ops[i])(a,arg,ans,reason);
       if(!err)
          { *op = ops[i];
            return 0;
          }
     }
  return 1;
}

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