Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/var/
Upload File :
Current File : /usr/home/beeson/MathXpert/var/vaux.c

/* M. Beeson; code to add variables to varlist, for Mathpert and Gentzen */
/*
12.2.93 extracted from earlier code
3.18.99 modified
1.9.00  added PSEUDOATOM to set_valuepointers
3.2.00  added bindvar and unbindvar
7.22.00 modified initialize_parameter to use GreekString
7.28.00 deleted declaration of now-unused 'buffer' in initialize_parameter
5.29.01 removed a superfluous line in make_atom_from_string
9.6.04 changed ltoa to sprintf in gensym
9.9.04  changed make_atom_from_string to use eulere and pi.
       changed string_to_atom to handle "infty".
6.24.05 modified vaux for binders EVAL and LAM, EXISTS, FORALL
7.21.05 modified getnewvar to return ILLEGAL when vaux fails
10.25.05 added LVARGPTR in several places
8.19.07 modified string_to_atom and make_atom_from_string for eulergamma
        modified vaux and set_valuepointers for EULERGAMMA
9.8.11  cast result of strlen to int  for 64-bit compilation without warnings
5.4.13  changed wrong dimension of buf in getnewvar, which was a bug on "theta".
        Eliminated stricmp in getnewvar.
5.21.13 Put GreekString in this file and made it static.
5.22.13 modified initialize_parameter to deal with y_0^'.
3.18.23  deleted unused function vvaux
11.24.23  ans  = zero to silence warnings
11.25.23  rewrote GreekString to not use OEM characters, but just TeX
1.30.24  in vaux(), line   varinfo[varDLL->nvariables-1].linenumber = varDLL->currentline+1;
should NOT have that +1 at the end, according to the spec,  and I removed it.
4.29.24.  But that change was wrong, as then undo doesn't properly remove defined variables.
So, I put the line back (dated in comment) and changed the mistaken spec.
5.5.24  added dated lines in swapvars
5.11.24 made gensym_counter part of vardata
9.7.24  in initialize_parameter, if varlist[i] is already a parameter, set its value to initialvalue
2.16.25 removed unused variable in getboundvar
3.8.25  made initialize_parameter reset rangemin and rangemax on existing parameters
3.10.25 Added activeparameter as a field of DocVarData, and
made initialize_parameter set varDLL->activeparameter
*/

/* var.dll works  like this:  it maintains internal static variables
varlist, varinfo, parameters, defns,
maxvariables, maxparameters, maxdefns,
nvariables, nparameters, nextdefn, eigenvariable, currentline.
These are also the names of fields in the DocVarData, which is
a part of the DocData of each document.  When we switch TO a
document, init_varDLL is called and initializes the DLL's variables
from the DocVarData.  Now the arrays point to the same data.  The
integers nvariables (etc) in the DocVarData are never accessed in
MathXpert except by init_varDLL and exit_varDLL.  The rest of the code
uses, for example, get_nvariables() to access nvariables.
*/

#include <string.h>  /* memcpy */
#include <stdio.h>   /* sprintf */
#include <assert.h>

#include "terms.h"
#include "speed.h"
#include "defns.h"
#include "vaux.h"
#include "constant.h"   /* externs for eulere, pi_term, complexi */
#include "dcomplex.h"
#include "deval.h"
#include "ceval.h"
#include "dispfunc.h"  /* newatom, GreekString, atom_string */
#include "probtype.h"  /* used by invent_new_independent_variable */

static vardata *varDLL;
/* will always point to the current document's vardata */
/* Set by init_varDLL.  */

static char *compute_subscripted_varname(term expr);
static double local_drandom(void);
static char  *GreekString(term atom);
/*__________________________________________________________*/
int vaux(term t)
/* continue adding new variables to varlist, which has dimension
   maxvariables;  add all variables in t.
   if it isn't big enough, make more space.  Indeed it must leave
   one UNUSED space, so that info about bound variables can be
   added BEFORE vaux is called.
*/
/* doesn't count PI, e, complexi, LEFT, RIGHT, or INFINITYFUNCTOR */
/* Always leaves at least two unused spaces for enhance_problem to use */
/* accepts 'i' only as the index variable of a sum */
/* sets varinfo.scope to BOUND correctly */
/* sets varinfo.type to whatever the TYPE macro returns on the
   variable at its first new occurrence in t.  Variables created
   by getnewintvar for instance have this set to INTEGER.
*/
/* sets varinfo.linenumber to varDLL->currentline +1,
because the new variable won't appear until the next line.  */
/* for bound variables, sets varinfo[j].locus to an ILLEGAL term;
   this gets set and reset as we traverse the term, but should be ILLEGAL
   while we are not traversing */
/* Returns zero for success.  Nonzero returns as follows:
      65:    complexi used as variable of integration with complex numbers on
      66:    index variable of sum can't occur outside the sum
      67:    index variable of product can't occur outside the product
      68:    variable of definite integration can't occur outside the integral
      69:    limit variable can't occur outside the limit
      15:    MathXpert can't handle any more variables!
*/

{  int i,err;
   unsigned a = FUNCTOR(t);
   term v;
   term *varlist = varDLL->varlist;
   varinf *varinfo = varDLL->varinfo;
   if(ISATOM(t))
      {
         if(a==PI_ATOM || (a== 'i' && TYPE(t) != INTEGER) || a == 'e' ||
            a == INFINITYFUNCTOR || a==LEFT || a==RIGHT || a == EULERGAMMA || 
            a == TRUEFUNCTOR || a == FALSEFUNCTOR
           )
              return 0; /* don't count these */
              /* But do count i if it has type integer; normally
              it should then be bound, and already counted by
              virtue of the code below, but when the Graph button
              is pushed in sigma notation, if the variable of
              summation is 'i', then we get a free 'i' here.
              */
          /* Now determine if t is already in varlist */
         for(i=0;i<varDLL->nvariables;i++)
            { if(FUNCTOR(varlist[i]) == a)
                 break;  /* already there */
            }
         if(i==varDLL->nvariables)  /* wasn't already there, must add it */
            { if(varDLL->nvariables >= varDLL->maxvariables-1)
                 {  /* No use allocating more space, because the dependencies are stored in
                    a 32-bit word (and maxvariables is 32), so even if we allocate more,
                    the dependency management will be broken.  */
                    return 15;
                 }
              /* Now go ahead and add t to the varlist */
              varlist[varDLL->nvariables] = t;
              LVARGPTR(varlist[varDLL->nvariables]) = (void *) &varinfo[varDLL->nvariables].realpart;
              ++varDLL->nvariables;
                /* Now add info to varinfo[nvariables-1]  */
              if(varinfo[varDLL->nvariables-1].scope != BOUND
                 && varinfo[varDLL->nvariables-1].scope != HALFBOUND
                )  /* it's already been set for bound variables,
                      don't change it.   Bound variables are caught
                      BELOW here in the code, and the scope set,
                      but then they get sent through again to
                      get added to varlist */

                 varinfo[varDLL->nvariables-1].scope = UNIVERSAL;
                  /* universal, type unspecified */
                  /* if it should be EXISTENTIAL, it will be changed later */
              varinfo[varDLL->nvariables-1].linenumber = varDLL->currentline+1; // 4.29.24
              varinfo[varDLL->nvariables-1].type = TYPE(varlist[varDLL->nvariables-1]);
              varinfo[varDLL->nvariables-1].realpart = local_drandom();
               /* the value of the variable under deval() is set
                  randomly.  Ssolve uses numerical evaluation to
                  try to refute equations even if they have parameters. */
              if(SUBSCRIPT(a))
                 { char *printname = compute_subscripted_varname(t);
                   if(strlen(printname) > 4)
                     assert(0);
                   strcpy(varinfo[varDLL->nvariables-1].name, printname);
                 }
            }
         return 0;
      }
   if(OBJECT(t))
      return 0;

   /* so now it's a compound term */
   /* catch bound variables and label them in varinfo */
   if(a==SUM || a== PRODUCT || (a==INTEGRAL && ARITY(t)==4) || a==LIMIT|| a == EVAL ||
       a == LAM || a == EXISTS || a == FORALL
     )
     { if(a==LIMIT)
          { v = ARG(0,ARG(0,t));
            SETORDERED(v);  /* see order.c, search for "limit"  */
          }
       else if( a == LAM || a == EXISTS || a == FORALL)
          v = ARG(0,t);
       else
          v = ARG(1,t);
       for(i=0;i<varDLL->nvariables;i++)
         { if(ISATOM(v) && FUNCTOR(varlist[i]) == FUNCTOR(v))
              { if(varinfo[i].scope != BOUND)
                      /* variable occurs both free and bound */
                  { switch(a)
                       { case SUM:      return 66;
                         case PRODUCT:  return 67;
                         case INTEGRAL: return 68;
                         case LIMIT:    return 69;
                       }
                  }
                else
                   break;
              }
         }
       if(i == varDLL->nvariables)
          { /* So now v is a variable not encountered yet: add it */
            varinfo[varDLL->nvariables].scope = BOUND;
            SETFUNCTOR(varinfo[varDLL->nvariables].locus,ILLEGAL,0);
            ZEROINFO(varinfo[varDLL->nvariables].locus);
            varinfo[varDLL->nvariables].linenumber = varDLL->currentline + 1;
            if(FUNCTOR(v) == 'i')
             /* then the code above won't get it */
               { if(a == SUM || a == PRODUCT)
                    { SETFUNCTOR(varlist[varDLL->nvariables],'i',0);
                      ZEROINFO(varlist[varDLL->nvariables]);
                      SETTYPE(varlist[varDLL->nvariables], INTEGER);
                      varinfo[varDLL->nvariables].type = INTEGER;
                      LVARGPTR(varlist[varDLL->nvariables]) = (void *) &varinfo[varDLL->nvariables].realpart;
                      ++varDLL->nvariables;
                    }
                 else
                    return 65;   /*  i can't be used as a variable of integration
                                      when complex numbers are on */
               }
            else
              { err = vaux(v);
                if(err)
                   return err;
              }
          }
       if(a==LIMIT)
          { err = vaux(ARG(1,ARG(0,t)));
            if(err)
               return err;
            err = vaux(ARG(1,t));
            if(err)
               return err;
            if (ARITY(t)==3)
               { err = vaux(ARG(2,t));
                 if(err)
                    return err;
               }
            return 0;
          }
       else
          { for(i=0;i<ARITY(t);++i)
               { if((a == SUM || a == PRODUCT || a == INTEGRAL || a == EVAL) && i==1)
                    continue;  /* already did ARG(1,t) */
                 else if((a == LAM || a == EXISTS || a == FORALL) && i==0)
                    continue;
                 err = vaux(ARG(i,t));
                 if(err)
                    return err;
               }
          }
       return 0;
      }
   else if((FUNCTOR(t)==INTEGRAL && ARITY(t)==2)  /* indefinite integral */
           || (FUNCTOR(t) == DIFF )  /* derivative */
          )
      { for(i=0;i<varDLL->nvariables;i++)
           { if(ISATOM(ARG(1,t)) && FUNCTOR(varlist[i])==FUNCTOR(ARG(1,t)))
                { varinfo[i].scope = HALFBOUND;
                  break;
                }
           }
        if (i==varDLL->nvariables)  /* variable is not already in varlist */
           { varinfo[varDLL->nvariables].scope = HALFBOUND;
             vaux(ARG(1,t));  /* the variable of integration */
             err = vaux(ARG(0,t));
             if(err)
                return err;
             if(ARITY(t)==3)  /* higher derivative */
             err = vaux(ARG(2,t));
             if(err)
                return err;
             return 0;
           }
      }
   else
      varinfo[varDLL->nvariables].scope = UNIVERSAL;
      /* So it won't accidentally be BOUND or HALF-BOUND in
         the calls below;  we can't assume that the unused parts
         of varinfo contain zero. They do if it's a fresh document,
         but not if reset_document has been called (when the Next
         or Previous buttons are used).
      */
   for(i=0;i<ARITY(t);i++)
     { err = vaux(ARG(i,t));
       if(err)
          return err;
     }
   return 0;
}
/*_____________________________________________________*/
#define IA 16807
#define IMAX 2147483647L
#define IQ 127773L
#define IR 2836

static double local_drandom(void)
/* return a random number between 0 and 1 */
/* Adapted from Numerical Recipes p. 279, 2nd edition */

{ long k;
  static double idum = 17;  /* can be any seed but CANNOT BE ZERO */
  k = (long) idum/IQ;
  idum = IA *(idum-k*IQ)-IR*k;
  if(idum < 0)
     idum += IMAX;
  return (1.0/IMAX) * idum;
}


/*_______________________________________________________________________*/
term getnewvar(term t,char *data)
/*  get a new variable which does not occur in t, preferring
to use the characters of data as the functor of the new variable.
  For example getnewvar(t,"ijk")
will return 'i' if t doesn't contain i, but will return k if t contains
i and j.  If t contains all the letters in data, it will use the first
letter as a root for gensym.  Enter the new variable in varlist and
varinfo just as if it had been entered with the problem, EXCEPT that
its scope is made the same as that of varlist[eigenvariable].
    The string 'data' may also be exactly "theta" or "epsilon".  In
the former case the choice of variables will be from theta, phi, t, alpha,beta.
In the latter it will be epsilon or a subscripted epsilon.
   WARNING: operators should not call this function unless they are
going to succeed and return an answer containing the new variable, to
avoid getting random new variables created that aren't used.
   The new variable must also not duplicate one already in varlist.
   When there's no more space for subscripted variables, it will
return an ILLEGAL term. */

{ int i,j;
  double z;
  term ans = zero; // silence a warning
  char buf[6];
  char str[2];
  int n = (int) strlen(data);
  int err;
  term *varlist = varDLL->varlist;
  varinf *varinfo = varDLL->varinfo;
  if(n == 7 && !strcmp(data,"epsilon"))
     { if(!contains(t,EPSILON))
          { SETFUNCTOR(ans,EPSILON,0);
            SETTYPE(ans,R);
          }
       else
          { ans = gensym(EPSILON);
            SETTYPE(ans,R);
          }
     }
  else
     { if (n==5 && !strcmp(data,"theta"))
          { buf[0] = THETA;
            buf[1] = PHI;
            buf[2] = 't';
            buf[3] = ALPHA;
            buf[4] = BETA;
            buf[5] = 0;
            n = 5;
            data = buf;
          }
       for(i=0;i < n; i++)
          { if( !contains(t,data[i]) )
              { for(j=0;j<varDLL->nvariables;j++)
                   { /* check if this letter is already in use */
                     if(data[i]==FUNCTOR(varlist[j]))
                        break;
                   }
                if(j<varDLL->nvariables)  /* it's already been used */
                   continue;     /* so go on to the next letter in data */
                /* if we get here, data[i] is ok--use it */
                str[0] = data[i];
                str[1] = '\0';
                ans = make_atom_from_string(str);
                break;
              }
          }
       if(i==n)
          { ans = gensym(data[0]);
            if(FUNCTOR(ans) == ILLEGAL)
               return ans;
          }
     }
  err = vaux(ans);  /* put it in varlist and varinfo */
  if(err)
     { // too many variables, probably
       SETFUNCTOR(ans, ILLEGAL,0);
       return ans;
     }
  varinfo[varDLL->nvariables-1].scope = varinfo[varDLL->eigenvariable].scope;

  /* Now set the value of this variable randomly; this is used in
     check_numerically in ssolve.c when checking solutions of equations
     that involve parameters.  The parameters are thus given random
     values which will, with high probability, enable rejection of
     truly spurious solutions without rejecting genuine solutions. */

  z =  local_drandom();
  ans.args = (void *) &varinfo[varDLL->nvariables-1].realpart;
  SETVALUE(ans,z);
  //  printf("from vaux.c, nvariables is now %d\n", get_nvariables());
  return ans;
}
/*_______________________________________________________________________*/
term index_variable(term x)
/*  get a new index variable which does not occur in x */
/*  Like getnewintvar, but sets the scope to BOUND, not EXISTENTIAL  */
{ term ans = getnewvar(x,"kjmnpqsJMNPQ");
  if(FUNCTOR(ans)==ILLEGAL)
     return ans;
  varDLL->varinfo[varDLL->nvariables-1].type = INTEGER;
    /* that's enough so 'immediate' can deduce the type; but
       the assumption that it's an integer won't show on this basis */
  varDLL->varinfo[varDLL->nvariables-1].scope = BOUND;
  SETTYPE(ans,INTEGER);
  SETTYPE(varDLL->varlist[varDLL->nvariables-1],INTEGER);
  SETVALUE(ans, 0.0);
  return ans;
}
/*_________________________________________________________________*/
static term getnewintvar_aux(term x, char *s,int scope)
/* call getnewvar and make it an int, choosing a name preferably from
among characters of s that don't occur in x.  Set the 'scope' field in
varinfo to scope, which will be BOUND or EXISTENTIAL.  */
{ term ans = getnewvar(x,s);
  term *varlist;
  varinf *varinfo;
  if(FUNCTOR(ans)==ILLEGAL)
     return ans;
  varlist = varDLL->varlist;
  varinfo = varDLL->varinfo;
  varinfo[varDLL->nvariables-1].type = INTEGER;
    /* that's enough so 'immediate' can deduce the type; but
       the assumption that it's an integer won't show on this basis */
  varinfo[varDLL->nvariables-1].scope = scope;
  SETVALUE(ans, 0.0);
  SETEXISTENTIALVAR(ans);  /* mark a certain bit in the info field */
  SETEXISTENTIALVAR(varlist[varDLL->nvariables-1]);
  /* you must mark the copy in the varlist too */
  SETTYPE(ans,INTEGER);  /* getnewvar gets it right only on a few default values,
                            but once you've used them up, getnewvar sets the
                            type to R. */
  SETTYPE(varlist[varDLL->nvariables-1],INTEGER);
  return ans;
}

/*________________________________________________________________*/
term getnewintvar(term x, char *s)
/* call getnewvar and make it an int, choosing a name preferably from
among characters of s that don't occur in x.  Set the 'scope' field in
varinfo to EXISTENTIAL.  */
{ return getnewintvar_aux(x,s,EXISTENTIAL);
}
/*________________________________________________________________*/
term getnewboundintvar(term x, char *s)
/* call getnewvar and make it an int, choosing a name preferably from
among characters of s that don't occur in x.  Set the 'scope' field in
varinfo to EXISTENTIAL.  */
{ return getnewintvar_aux(x,s,BOUND);
}
/*________________________________________________________________*/
term getnewboundvar(term x, char *s)
/* call getnewvar, choosing a name preferably from
among characters of s that don't occur in x.  Set the 'scope' field in
varinfo to BOUND */
{ term ans = getnewvar(x,s);
  term *varlist;
  if(FUNCTOR(ans)==ILLEGAL)
     return ans;
  varlist = varDLL->varlist;
    /* that's enough so 'immediate' can deduce the type; but
       the assumption that it's an integer won't show on this basis */
  SETVALUE(ans, 1.0);
  bindvar(ans);  // set scope field to BOUND
  bindvar(varlist[varDLL->nvariables-1]);
  /* you must mark the copy in the varlist too */
  SETTYPE(ans,R);  
  SETTYPE(varlist[varDLL->nvariables-1],R);
  return ans;
}
/*_______________________________________________________________________*/
static int cofi_index;

void set_cofi_index(int n)
/* set the index for the next constant of integration */
{ cofi_index = n;
}
/*_______________________________________________________________________*/
int get_cofi_index(void)
/* access the value of cofi_index */
{ return cofi_index;
}
/*______________________________________________________________________*/
int contains_free(term t, unsigned short f)
/* return 1 if t contains f free, 0 if not.  If it contains
t both free and bound, 1 is returned, although such terms are
not supposed to arise in Mathpert. */
{ unsigned short n,i,g;
  int j = -1;
  if(OBJECT(t))
     return 0;
  if(ISATOM(t))
     return f == FUNCTOR(t) ? 1 : 0;
  n = ARITY(t);
  g = FUNCTOR(t);
  if(g == INTEGRAL && n == 4)
     j = 1;
  if(g == SUM || g == PRODUCT || g == EVAL || g == BIGOH)
     j = 1;
  if(g == LAM || g == FORALL || g == EXISTS)
     j = 0;
  if(g == LIMIT)
     { if(f == FUNCTOR(ARG(0,ARG(0,t))))
          return 0;
       if(contains_free(ARG(1,ARG(0,t)),f))
           return 1;
        j = 0;
     }
  else
     { if(FUNCTOR(ARG(j,t)) == f)
          return 0;
     }
  for(i=0;i<n;i++)
     { if(i==j)
          continue;
       if(contains_free(ARG(i,t),f))
          return 1;
     }
  return 0;
}
/*_______________________________________________________________________*/
/* Constants of integration are created by the following function. */

term constant_of_integration(term s,term x)
/* Usually x is the variable of integration, s is the integrand.
But x can also be a term like and(x,y) or and(x,and(y,z))
listing several variables, if more than one integral has been
done at the same line.  The constant of integration to be
returned should depend on variables in s which are not in x.
  Constants of integration are not atoms.  They are
terms with functor CONSTANTOFINTEGRATION.  They will be
printed as c_k  or as c_k(a,b..) where a,b... are the
variables on which they depend.  The 0-th argument is the
subscript that will be printed.
*/

{ term ans;
  int i,k;
  unsigned short f;
  term *varlist = varDLL->varlist;
  unsigned short count = 0;
  /* On how many variables will this constant depend? */
  for(i=0;i<varDLL->nvariables;i++)
     { f = FUNCTOR(varlist[i]);
       if(!contains(x,f) && (contains_free(s,f) || depends(s,varlist[i])) &&
          (!depends(x,varlist[i]) || isparameter(varlist[i])>= 0 ) &&
          !depends(varlist[i],x)
         )
          ++count;
     }
  ans = make_term(CONSTANTOFINTEGRATION,(unsigned short)(count+1));
  ARGREP(ans,0,make_int(cofi_index));
  ++cofi_index;  /* ready for next time */
  k=1;
  for(i=0;i<varDLL->nvariables;i++)
     { f = FUNCTOR(varlist[i]);
       if(!contains(x,f) && (contains_free(s,f) || depends(s,varlist[i])) &&
          (!depends(x,varlist[i]) || isparameter(varlist[i])>=0) &&
          !depends(varlist[i],x)
         )
          { ARGREP(ans,k,varlist[i]);
            ++k;
          }
     }
  return ans;
}
/*__________________________________________________________*/
void initialize_parameter(int i, int linenumber, double initialvalue)
   /* initialize varlist[i] as a parameter, putting
      linenumber into the .linenumber field of the
      parameter data structure for the new parameter. 
         If i is negative, that signals that the parameter name
      should be formed by adding a prime to the end of the previous 
      parameter name (after making i positive)
         If varlist[i] is already a parameter, set its value to initialvalue
   */

{ int j;
  int ode2flag = 0;
  parameter *parameters = varDLL->parameters;
  term *varlist = varDLL->varlist;
  char *pname;
  if(i<0)
     { i = -i;
       ode2flag = 1;
     }

  /* first check if this variable has already been made a parameter */
  /* If so, just reset rangemin and rangemax  */
  for(j=0;j<varDLL->nparameters;j++)
     { if(parameters[j].functor == FUNCTOR(varlist[i]))
          { /* it's already there */
             *(parameters[j].addr) = initialvalue;
             parameters[j].rangemax = initialvalue + 10 *  parameters[j].increment;
             parameters[j].rangemin = initialvalue - 10 *  parameters[j].increment;
             return;
          }
     }
  if (varDLL->nparameters >= varDLL->maxparameters)
      return;  /* just don't do it; this should rarely happen;
                   in fact I'm not certain it can happen at all. */
  if(ode2flag)
     { char *prev = parameters[varDLL->nparameters-1].name;
       int m = (int) strlen(prev);
       pname = callocate(m+3, sizeof(char));
       strcpy(pname, prev);
       pname[m-1] = 0;  // erase the 0 with which it ends
       if(m-2>=0 && pname[m-2] == '_')
          pname[m-2] = 0;   // erase the underscore before the zero, if there was one
       strcat(pname,"'_0");
     }
  else if(PREDEFINED_ATOM(FUNCTOR(varlist[i])))
     pname = GreekString(varlist[i]);
  else
     pname = atom_string(varlist[i]);
     /* pointer to a static string or an entry in the symbols array */

  parameters[varDLL->nparameters].name = pname;
  if(pname[0] == 'b')
     parameters[varDLL->nparameters].increment = 0.1;
  else
     parameters[varDLL->nparameters].increment = 1.0;  /* default value of increment */
  parameters[varDLL->nparameters].addr = &varDLL->varinfo[i].realpart;
  parameters[varDLL->nparameters].index = i;
  if(varDLL->activeparameter == -1)
     varDLL->activeparameter = varDLL->nparameters;
  parameters[varDLL->nparameters].functor = FUNCTOR(varlist[i]);
  parameters[varDLL->nparameters].linenumber = linenumber;
  *(parameters[varDLL->nparameters].addr) = initialvalue;
  parameters[varDLL->nparameters].rangemax = initialvalue + 10 *  parameters[varDLL->nparameters].increment;
  parameters[varDLL->nparameters].rangemin = initialvalue - 10 *  parameters[varDLL->nparameters].increment;
  // thus the value of the parameter is one of the values in the middle of the range
  ++varDLL->nparameters;
 }
/*_________________________________________________________*/
void push_parameters(void)
/* push the current values of ALL parameters
   onto their respective history lists, provided the
   current values are different from what is already at
   the head of the list */


{ unsigned i;
  unsigned n = varDLL->nparameters;
  parameter *parameters = varDLL->parameters;
  dlist *marker;
  for(i=0;i<n;i++)  /* first check if there are new values to push */
     { if(parameters[i].history == NULL ||
          *parameters[i].addr != parameters[i].history->data
         )
          break;
     }
  if(i==n)
     /* nothing to do */
     return;
  for(i=0;i<n;i++)
     { marker = parameters[i].history;
       parameters[i].history = (dlist *) mallocate(sizeof(dlist));
       if(parameters[i].history == NULL)
          nospace();
       parameters[i].history->next = marker;
       parameters[i].history->data = * parameters[i].addr;
     }
}
/*_________________________________________________________*/
void clear_parameter_history(void)
/* free the linked lists parameter[i].history */
{ unsigned i;
  unsigned n = varDLL->nparameters;
  parameter *parameters = varDLL->parameters;
  dlist *marker, *prev;
  for(i=0;i<n;i++)
     { marker = parameters[i].history;
       while(marker)
          { prev = marker;
            marker = marker->next;
            free2(prev);
          }
       parameters[i].history = NULL;
     }
}

/*_________________________________________________________*/
int get_index(term t)
/* t must be an atom already in varlist.  Return the index i such
that t is varlist[i]. */
{ int i;
  int n = varDLL->nvariables;
  term *varlist = varDLL->varlist;
  assert(ISATOM(t));
  for(i=0;i<n;i++)
     { if(FUNCTOR(t) == FUNCTOR(varlist[i]))
           return i;
     }
  assert(0);
  return 0;
}
/*__________________________________________________________*/
int get_eigenindex(void)
{ return varDLL->eigenvariable;
}
/*__________________________________________________________*/
term get_eigenvariable(void)
{ return varDLL->varlist[varDLL->eigenvariable];
}
/*__________________________________________________________*/
int get_currentline(void)
{ return varDLL->currentline;
}
/*__________________________________________________________*/
void set_currentline(int n)
{ varDLL->currentline = n;
}
/*__________________________________________________________*/
void increment_currentline(void)
{ ++varDLL->currentline;
}
/*__________________________________________________________*/
void decrement_currentline(void)
{ --varDLL->currentline;
}
/*__________________________________________________________*/
int get_nvariables(void)
{ return varDLL->nvariables;
}
/*__________________________________________________________*/
void set_nvariables(int n)
/* used to eliminate recently-defined variables */
{ if (n > varDLL->nvariables)
     assert(0);
  assert(n >= 0);
  if(varDLL->nvariables == n)
     return;   /* A breakpoint on the next line will catch genuine changes.*/
  varDLL->nvariables = n;
}

/*__________________________________________________________*/
int get_nparameters(void)
{ return varDLL->nparameters;
}
/*__________________________________________________________*/
void set_nparameters(int n)
{ varDLL->nparameters = n;
}
/*__________________________________________________________*/
void decrement_nparameters(void)
/* used by undo() to undo 'Regard variable as constant' */
{ assert(varDLL->nparameters > 0);
  --varDLL->nparameters;
}

/*__________________________________________________________*/
void set_eigenvariable(int i)
{ if(varDLL->nvariables == 0)
     { assert(i==0);
       varDLL->eigenvariable = 0;
       return;
     }
  if(i >= varDLL->nvariables)
     assert(0);
  varDLL->eigenvariable = i;
}
/*__________________________________________________________*/
void init_varDLL(vardata *pData)
/* Called whenever we switch documents; it's passed a pointer
to the new document's DocVarData.  Must initialize var.dll to work with
the new document. */
{  varDLL = pData;
}
/*___________________________________________________________*/
void swapvars(int i, int j)
/* swap varlist[i] and varlist[j] and
also swap varinfo[i] and varinfo[j].
If one of these variables occurs in the parameters array, fix that
entry up also.  */

{ varinf saveit;
  term temp;
  int k;
  parameter *parameters = varDLL->parameters;
  term *varlist = varDLL->varlist;
  varinf *varinfo = varDLL->varinfo;
  temp = varlist[i];
  saveit = varinfo[i];
  varlist[i] = varlist[j];
  varinfo[i] = varinfo[j];
  varlist[j] = temp;
  varinfo[j] = saveit;
  LVARGPTR(varlist[i]) = (void *) &varinfo[i].realpart;
  LVARGPTR(varlist[j]) = (void *) &varinfo[j].realpart;
  /* Now work on the parameters array */
  for(k=0;k<varDLL->nparameters;k++)
     { if(parameters[k].index == i)
          { parameters[k].addr = &varinfo[j].realpart;
            parameters[k].index =j;
          }
       else if(parameters[k].index == j)
          { parameters[k].index = i;
            parameters[k].addr = &varinfo[i].realpart;
          }
     }
  if(varDLL->eigenvariable == i)   // added 5.5.24
     varDLL->eigenvariable = j;
  if(varDLL->eigenvariable ==j)
     varDLL->eigenvariable = i;
     
  return;
}
/*_________________________________________________________*/
term * get_varlist(void)
{ return varDLL->varlist;
}
/*_________________________________________________________*/
varinf * get_varinfo(void)
{ return varDLL->varinfo;
}
/*_____________________________________________________________*/
term getvar(term t, char *data)
/* like getnewvar, this gets a variable not contained in t,
chosen if possible from the letters in string data; but getnewvar
always gets a NEW variable, while getvar doesn't care if the
variable is already in use. */
{ int i,j;
  term ans = zero;  // silence a warning
  double z;
  char str[2];
  int n = (int) strlen(data);
  term *varlist = varDLL->varlist;
  varinf *varinfo = varDLL->varinfo;
  for(i=0;i < n; i++)
    { if( !contains(t,data[i]) )
         { for(j=0;j<varDLL->nvariables;j++)
              { /* check if this letter is already in use */
                if(data[i]==FUNCTOR(varlist[j]))
                   break;
              }
           if(j<varDLL->nvariables)  /* it's already in use */
              return varlist[j];   /* done */
           /* if we get here, data[i] is ok--use it */
           str[0] = data[i];
           str[1] = '\0';
           ans = make_atom_from_string(str);
           break;
         }
     }
   if(i==n)
      ans = gensym(data[0]);
   vaux(ans);  /* put it in varlist and varinfo */
   varinfo[varDLL->nvariables-1].scope = varinfo[varDLL->eigenvariable].scope;
     /* Now set the value of this variable randomly */
   z =  local_drandom();
   ans.args = (void *) &varinfo[varDLL->nvariables-1].realpart;
   SETVALUE(ans,z);
   return ans;
}
/*______________________________________________________________________*/
int let_permanent(term l, term r)
{ defn *defns = varDLL->defns;
  int err = let(l,r);
  if(err)
     return 1;
  defns[varDLL->nextdefn-1].permanent = 1;  /* don't unwind at the end */
  varDLL->eigenvariable = defns[varDLL->nextdefn-1].oldeigen;  /* don't change eigenvariable */
  return 0;
}
/*______________________________________________________________________*/
void invent_new_independent_variable(term t, int problemtype)
/* add a new independent variable, leave it in varlist[0] , and 
put the variable formerly in varlist[0] into varlist[1],
and the variable formerly in varlist[1] ends up in last place. */
{ term x;
  int nvariables = get_nvariables();
  if(problemtype == POLAR_GRAPH)
     x = getnewvar(t,"theta");
     /* this will get theta or phi or alpha or beta */
  else if (problemtype == PARAMETRIC_GRAPH || 
           // problemtype == SOLVE_ODE ||
           problemtype == SOLVE_TWO_ODES || 
           problemtype == HIGH_ORDER_ODE
          )
     x = getnewvar(t,"tsuvw");
  else
     x = getnewvar(t,"xtusw");
  vaux(x);  /* add it to varlist */
  if(nvariables >= 2)  /* there were at least 2 variables to begin with, so now there are at least 3 */
     swapvars(0,1);  /* e.g. on y' = ay,  we had (y,a,t), and after this line, (a,y,t) */
  swapvars(0,nvariables);  /* put x in varlist[0];  so now we have (t,y,a), so dependent variable y is in varlist[1] */
}
int reverse_let(term l, term r)
/* used for substitutions l = r where l is an old variable and
   r contains a new variable.  All the new variables have already
   been added to varlist.
*/
{ defn *defns = varDLL->defns;
  int err = let(l,r);
  if(err)
     return 1;
  defns[varDLL->nextdefn-1].reverse = 1;
  return 0;
}

/*______________________________________________________________________*/
int let(term l, term r)
/* enter the defn 'l=r' (in which l is being defined as r) */
/* does not cause any visible display */
/* currentline is recorded for use by undo  */
/* In case of simultaneous definitions,  *l and r will be AND terms of the
   same arity, e.g.  (x,y) = (r cos theta, r sin theta). None of the variables on
   the left side is allowed to occur in any of the right sides */
/* The new variable(s) must be entered in varlist, and its (their) dependencies
   on other variables properly recorded.  That a variable is dependent on
   some other variables is recorded in a certain bit of its .info field by
   the macro SET_DEPENDENT; that bit is read by DEPENDENT.  If it IS dependent,
   which variables varlist[i] depends on are recorded in varinfo[i].dp; the
   k-th bit of this 21-bit field tells whether varlist[i] depends on varlist[k].
*/
/* Return 0 for success, 1 for failure (too many variables or out of space) */
/* This function must also cause multcompare to place the (each) newly-defined
   variable in the same position in multiplicative order as the
   variable in the right side of its definition (the first variable if there
   is more than one), so that after a change of variable we don't see a
   lot of order-changing.  This is accomplished by setting varinfo[i].multorder
   to 1;  multcompare examines that field. */
/* The lifetime of the terms l and r placed in the defns array will be
   determined by the calling program.  No 'permanent' copies are made.
   Mathpert should call 'permcopy' before passing the args to let, so that
   the lifetime will be that of the document. */
/* Since there can be at most 32 variables, there can be at most 32 definitions.
   So there's no use in reallocing when we're out of space.  We just fail.  */

{ int err, eigen;
  unsigned i,n;
  /* First make sure we have enough space to enter the new definition */
  if(varDLL->nextdefn == varDLL->maxdefns)
     return 1; /* failure, no more space for definitions.  See above. */
  /*  Now enter the definition */
  if(varDLL->defns == NULL)
     { nospace();
       return 1;
     }
  UNPROTECT(r);   /* r is local, but this is what will be entered in defns[nextdefn] */
                  /* if it's protected now, it still should be simplified after
                     unwind_definitions */
  varDLL->defns[varDLL->nextdefn].left = l;
  varDLL->defns[varDLL->nextdefn].right = r;
  varDLL->defns[varDLL->nextdefn].line = varDLL->currentline + 1;
  varDLL->defns[varDLL->nextdefn].visible = 1;  /* by default */
  varDLL->defns[varDLL->nextdefn].reverse = 0;  /* by default */
  varDLL->defns[varDLL->nextdefn].permanent = 0;  /* by default */
  ++varDLL->nextdefn;
  /*  Now put the variables in varlist and enter their dependencies */
  if(FUNCTOR(l) == AND)
     { n = ARITY(l);
       assert(n == ARITY(r) && FUNCTOR(r)==AND);
       eigen = varDLL->eigenvariable;
       for(i=0;i<n;i++)
          { err = let_aux(ARG(i,l),ARG(i,r),1);
            if(err)
               return 1;
          }
       /* on multiple definitions, let_aux screws up the .oldeigen
          field, since it sets the eigenvariable on the first call to
          let_aux and then on the second call sets the old eigenvariable
          to the new one.  Fix this here:  */
       varDLL->defns[varDLL->nextdefn-1].oldeigen = eigen;
       return 0;
     }
  else
     return let_aux(l,r,1);
}
/*____________________________________________*/
int let_aux(term l, term r, int flag)
/* l should be an atom;  add it to varlist, making more space if
necessary; also set l to depend on all atoms in r.  If flag is
nonzero, set the 'multorder' field of varinfo when entering l in
varlist, so multiplicative order will treat it like the first
variable in r, and also change the eigenvariable to l, but don't
change the eigenvariable if flag is zero.  Also if flag is
nonzero, assume the new variable l has been added by let,
so defns[varDLL->nextdefn-1] is the definition of l, and we
need to adjust dependency information there.  However, let_aux
also is called by grbutton when the Graph button is pressed, to
add a new variable for the y-axis, in which case there is no
definition to modify.
   Return 0 for success, 1 if the number of variables exceeds
MAXDEPENDS; in this case the new variable is entered in varlist
but the definition is not entered.

  Change 'eigenvariable' if varlist[eigenvariable] is being
substituted for. */

{ int i,j,natoms;
  unsigned f;
  int nonconstantflag = 0;
  unsigned long dependsinfo = 0;
  term *atomlist;
  term *varlist = varDLL->varlist;
  varinf *varinfo = varDLL->varinfo;
  int whichvar;
  assert(ISATOM(l));
  vaux(l); /* add l to varlist, making space if necessary; see getprob.c */
  if(varDLL->nvariables > MAXDEPENDS)
     return 1;  /* too many definitions */
  if(seminumerical(r))
     { varDLL->defns[varDLL->nextdefn-1].oldeigen = varDLL->eigenvariable;
       deval(r,&varinfo[varDLL->nvariables-1].realpart);
       varinfo[varDLL->nvariables-1].imagpart = 0;
       return 0;
     }
  if(complexnumerical(r))
     { dcomplex z;
       ceval(r, &z);
       varinfo[varDLL->nvariables-1].realpart = z.r;
       varinfo[varDLL->nvariables-1].imagpart = z.i;
     }
  natoms = atomsin(r,&atomlist);
  whichvar = varDLL->nvariables - 1;

    /* usually l = varlist[varDLL->nvariables-1].  But if l was already in varlist,
       then this isn't the case.  The next few lines will adjust
       whichvar so that in EVERY case we have l = varlist[whichvar]  */

  for(i=0;i<varDLL->nvariables;i++)
     { if(ISATOM(l) && FUNCTOR(l) == FUNCTOR(varlist[i]))
           { whichvar = i;
             break;
           }
     }
  if(flag)
     varinfo[whichvar].multorder = 1;
  if(varDLL->nvariables == 1)
     { /* there were no variables originally, now varlist contains
           only l, the variable being defined */
       varinfo[varDLL->nvariables-1].dp = 0L;
       free2(atomlist);
       return 0;
     }
  for(i=0;i<natoms;i++)
     { /* record that l depends on atomlist[i] */
       for(j=0;j<varDLL->nvariables-1;j++)
          { if(FUNCTOR(atomlist[i]) == FUNCTOR(varlist[j]))
               break;
          }
       f = FUNCTOR(atomlist[i]);
       if (j>=varDLL->nvariables)
           assert(f == 'e' || f == 'i' || f == PI_ATOM);
       else
          /*  l depends on varlist[j]; set bit j of dependsinfo */
          { dependsinfo |= (1L << j);
            nonconstantflag = 1;
          }
     }
  if(nonconstantflag)
     { SETDEPENDENT(varlist[whichvar]);
       if(flag)
          SETDEPENDENT(varDLL->defns[varDLL->nextdefn-1].left);
       /* set the 'depends' bit in the definition itself, so
          that if a later operator (such as trysubstitution)
          fishes it out of there it will have this bit
          correctly set. */
     }
     /* set bit in .info field that says it depends on SOMETHING */
  varinfo[whichvar].dp = dependsinfo;
  if(flag && contains(r,FUNCTOR(varlist[varDLL->eigenvariable])))
     {  /* then l is the new eigenvariable */
       varDLL->defns[varDLL->nextdefn-1].oldeigen = varDLL->eigenvariable;
       varDLL->eigenvariable = whichvar;
     }
  else if(flag)  /*  the variable is introduced by let, but the right side
                     doesn't contain the eigenvariable */
     varDLL->defns[varDLL->nextdefn-1].oldeigen = varDLL->eigenvariable;
  free2(atomlist);  /* allocated by atomsin */
  return 0;
}
/*_______________________________________________________________*/
int get_nextdefn(void)
/* return the number of definitions currently stored */
{ return varDLL->nextdefn;
}
/*_______________________________________________________________*/
void set_nextdefn(int n)
/* used to delete assumptions made since a previous time */
/* return the number of definitions currently stored */
{  assert(n <= varDLL->nextdefn);
   varDLL->nextdefn = n;
}
/*________________________________________________________________*/
defn get_defn(int i)
/* return the i-th definition */
{ assert(i < varDLL->nextdefn);
  return varDLL->defns[i];
}
/*______________________________________________________*/
parameter * get_parameters(void)
{ return varDLL->parameters;
}
/*______________________________________________________________________*/
int undo_letdefns(void)
/* return 1 if any definition is undone, zero if not */
/* Also removes any variables introduced at currentline by the theorem prover */

{ int i,saveit;
  defn *defns = varDLL->defns;
  varinf *varinfo = varDLL->varinfo;
  if(varDLL->nextdefn==0)
     { /* be careful that we don't try to access varinfo[-1] in case
          the problem had no variables. */
       while(varDLL->nvariables > 0 &&
             varinfo[varDLL->nvariables-1].linenumber==varDLL->currentline
            )
         { --varDLL->nvariables;
         }
       return 0;
     }
  saveit = varDLL->nextdefn;
  i = varDLL->nextdefn-1;
  while(i>=0 && defns[i].line == varDLL->currentline)
     { /*  destroy_term(defns[i].right);  no longer necessary as these
           are kept in 'permanent' memory */
       varDLL->eigenvariable = defns[i].oldeigen;
       --varDLL->nextdefn;
       --i;
     }
  while(varDLL->nvariables > 0 &&
        varinfo[varDLL->nvariables-1].linenumber==varDLL->currentline
       )
     --varDLL->nvariables;
  return (varDLL->nextdefn == saveit ? 0 : 1);
}
/*____________________________________________________________*/
defn * get_defns(void)
/* defns.c needs access  */
{ return varDLL->defns;
}
/*_____________________________________________________________________*/
term gensym(char root)
/* produce a new atom with a print name of the form c1, c2, ...c45... */
/* passing 0 or 1 for the root resets the counter to 0 or 1 and returns an ILLEGAL term. */
{  char s[32];
   if(root == 0 || root == 1)
      { term dummy;
        SETFUNCTOR(dummy, ILLEGAL,0); /* avoid warning message */
        varDLL->gensym_counter = root;
        return dummy;  /* return value is irrelevant */
      }
   sprintf(s+1,"%d",varDLL->gensym_counter++);
   s[0] = root;
   /* Now s contains the desired string */
   return make_atom_from_string(s);
  }
/*______________________________________________________________________*/
/* make_atom_from_string is the way in which atoms are originally to be
created, even if the string has length 1.
*/

term make_atom_from_string( char *a)
/* If you change this, change make_atom_from_string2 in parser.c too.
   Use predefined atoms for pi_term, eulere; that way their value pointers
   will point to the correct decimal value.
     However,  "i" is not returned as complexi.
*/
{ unsigned short atom;
  term ans;
  atom = string_to_atom(a);
  if(atom == PI_ATOM)
     return pi_term;
  if(atom == EULERGAMMA)
     return eulergamma;
  if(atom == 'e')
     return eulere;
  SETFUNCTOR(ans,atom,0);
  ZEROINFO(ans);  /* make sure the .info field is zero */
       /* Now set the type of the variable */
  if(atom == 'i')
     SETCOMPLEX(ans);
  else if( *a == 'n' || *a == 'm' || *a == 'k' || *a == 'j')
     SETTYPE(ans,INTEGER);
  else
     SETTYPE(ans,R);
  return ans;
}
/* _________________________________________________________________*/
unsigned short string_to_atom(char *x)
/* If you change this, change the static copy in parser.c too */
{ if(x[1] == '\0')  /* a one-symbol atom */
  /* We do not recognize extended character codes for Greek letters.
     The name of the Greek letter must be typed out.
  */
      return x[0];
  switch( x[0])
     { case 'a':
          if( !strcmp(x,"alpha"))
             return ALPHA;
          break;
       case 'b':
          if( !strcmp(x,"beta"))
             return BETA;
          break;
       case 'd':
          if( !strcmp(x,"delta"))
             return DELTA;
          break;
       case 'E':
       case 'e':
          if( !strcmp(x,"epsilon"))
             return EPSILON;
          if( !strcmp(x,"eulergamma")) 
            return EULERGAMMA;
           if( !strcmp(x,"Eulergamma") || !strcmp(x,"EulerGamma")) 
              return EULERGAMMA;
          break;
       case 'f':
          if( !strcmp(x,"false"))
             return FALSEFUNCTOR;
          break;
       case 'g':
          if( !strcmp(x,"gamma"))
             return LITTLEGAMMA;
          break;
       case 'i':
          if( !strcmp(x,"infinity"))
             return INFINITYFUNCTOR;
          if( !strcmp(x, "infty"))
             return INFINITYFUNCTOR;
          break;
       case 'l':
          if( !strcmp(x,"lambda"))
             return LAMBDA;
          break;
       case 'm':
          if( !strcmp(x,"mu"))
             return MU;
          break;
       case 'p':
          if( !strcmp(x,"phi"))
             return PHI;
          if( !strcmp(x,"pi"))
            return PI_ATOM;
          break;
       case 's':
          if( !strcmp(x,"sigma"))
             return SIGMA;
          break;
       case 't':
          if( !strcmp(x,"theta"))
             return THETA;
          if( !strcmp(x,"true"))
             return TRUEFUNCTOR;
          break;
       case 'z':
          if( !strcmp(x,"zeta"))
             return RIEMANNZETA;
          break;
     }
  return newatom(x);
}

/*__________________________________________________________*/
int isparameter(term x)
/* If x is a parameter, return its index in the parameters array;
if not, return -1  */
{ int i;
  parameter *parameters = varDLL->parameters;
  if(!ISATOM(x))
     return -1;
  for(i=0;i<varDLL->nparameters;i++)
     { if(FUNCTOR(x)==parameters[i].functor)
           return i;
     }
  return -1;
}
/*____________________________________________________________*/
int is_letdefined(term u, term *ans)
/* return 1 if u is let-defined, putting its definition in *ans;
or return 0 if u is not let-defined.   It presumes u is an atom. */
{ int i;
  defn *defns = varDLL->defns;
  if(!ISATOM(u))
      return 0;
  for(i=0;i<varDLL->nextdefn;i++)
     { if(FUNCTOR(defns[i].left) == FUNCTOR(u))
          { *ans = defns[i].right;
            return 1;
          }
     }
  return 0;
}
/*_____________________________________________________________*/
void undo_parameters(int currentline)
/* remove all parameters introduced at linenumbers >= currentline */
/* assumes that the parameters have all been created by initialize_parameter,
so they are in chronological order in the parameters array.  */

{ int i;
  parameter *parameters = varDLL->parameters;
  for(i=0;i<varDLL->nparameters;i++)
      { if(parameters[i].linenumber >= currentline)
           break;
      }
  varDLL->nparameters = i;
}
/*____________________________________________________________*/
static char *compute_subscripted_varname(term expr)
/* compute the print string for a subscripted variable.
Return a string in space created by callocate */
{ char buffer[32];
  char *printstring;
  char *q;
  term temp = expr;
  int subscript;
  unsigned short f = FUNCTOR(expr);
  SETFUNCTOR(temp,VARNAME(f),0);
  q = atom_string(temp);
  strcpy(buffer,q);
  subscript = SUBSCRIPT(f)-1;
  sprintf(buffer + strlen(buffer),"%d", subscript);
    /* Now the right string is in buffer */
  printstring = callocate((int) strlen(buffer)+1,sizeof(char));
  strcpy(printstring,buffer);
  return printstring;
}
/*_____________________________________________________________*/
static unsigned long lowbits(unsigned long x, int i)
/* return a long with bits 0, ..., i-1 the same as x and
higher bits zero. */
{ unsigned long mask = (unsigned long) -1;  /* all bits 1 */
  int nbits = 8 * sizeof(unsigned long);
  mask = (mask >> (nbits-i));  /* shift nbits-1 zeroes in from the left */
  return x & mask;
}
/*_____________________________________________________________*/

static unsigned long highbits(unsigned long x, int i)
/* return a long with bits i+1,...  the same as x
and bits 0,...,i set to zero
*/
{ unsigned long mask = (unsigned long) -1;  /* all bits 1*/
  mask = mask << (i+1);   /* shift in i+1 zero bits from the right */
  return x & mask;
}

/*_____________________________________________________________*/
int deletevar(term x)
/* remove atom x from varlist, moving all subsequent
variables down one and resetting nvariables.  Adjust
dependency information of any variables that depend on
the moved variables.  Return 1 if the variable is
found and deleted, 0 if it is not in varlist or if x
is not an atom.
*/

{ int i,j;
  term *varlist = varDLL->varlist;
  unsigned long r;
  int nvariables = varDLL->nvariables;
  varinf *varinfo = varDLL->varinfo;
  if(!ISATOM(x))
     return 0;
  for(i=0; i< nvariables; i++)
     { if(FUNCTOR(x) == FUNCTOR(varlist[i]))
          break;
     }
  if(i == nvariables)
     return 0;  /* variable not found */
  /* Check whether any variable depends on this variable */
  for(j=0;j<nvariables;j++)
     { r = (1L << i);
       if(varinfo[j].dp & r)
          return 1;  /* refuse to delete */
     }
  /* OK, now move subsequent ones down */
  for(j=i+1; j<nvariables; j++)
     { varlist[j-1] = varlist[j];
       varinfo[j-1] = varinfo[j];
     }
  --varDLL->nvariables;
  --nvariables;
  for(j=0;j<nvariables;j++)
     { /* adjust dependency information */
       r = varinfo[j].dp;
       if(r >> i)
          { /* varlist[j] depended on the i-th or later variable */
            varinfo[j].dp = lowbits(r,i) | (highbits(r,i) >> 1);
          }
     }
  return 0;
}
/*_______________________________________________________*/
term firstdepends(term x)
/* x must be a variable.  Return the first variable that x
depends on, other than x itself; if x depends on no other
variable then return x.
*/
{ int i,j;
  term *varlist = varDLL->varlist;
  unsigned long d;
  int nvariables = varDLL->nvariables;
  varinf *varinfo = varDLL->varinfo;
  for(i=0;i<nvariables;i++)
     { if(FUNCTOR(varlist[i]) == FUNCTOR(x))
          break;
     }
  assert(i<nvariables);  /* you must find x in the varlist */
  if(varinfo[i].dp == 0)
     return x;
  for( j=0,d = varinfo[i].dp; d && ((!(d&1)) || j==i) ; ++j )
     d = d >> 1;
  if(d==0)
     return x;
  return varlist[j];
}

/*______________________________________________________________________*/
unsigned short bound_in(term x, term t)
/* if variable x occurs bound in t, return the binding functor.
If not, return 0. */
{ unsigned short f,n,ans;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  n = ARITY(t);
  if(f == LIMIT && equals(x,ARG(0,ARG(0,t))))
     return LIMIT;
  if((f == LAM || f == FORALL || f == EXISTS || f == BIGOH) && equals(x,ARG(0,t)))
     return f;
  if((f == SUM || f == PRODUCT || f == DIFF || f == INTEGRAL) && equals(x,ARG(1,t)))
     return f;
  for(i=0;i<n;i++)
     { ans = bound_in(x,ARG(i,t));
       if(ans)
          return ans;
     }
  return 0;
}
/*_________________________________________________________________*/
void set_valuepointers(term *t)
/*  set the .args field of each atom in *t correctly, using the
corresponding entry in varlist
*/
{ int i;
  unsigned short f,n;
  int nvariables = get_nvariables();
  term *varlist = get_varlist();
  if(ISATOM(*t))
     { f = FUNCTOR(*t);
       for(i=0;i<nvariables;i++)
         { if(f == FUNCTOR(varlist[i]))
              { t->args = varlist[i].args;
                SETTYPE(*t,TYPE(varlist[i]));
                /* in Mathpert, these should be the same anyway; in
                   Weierstrass, varlist[i] may have had its type set
                   by the parser when it parsed e.g.  x:N, and later
                   occurrences of 'x' don't have their types set until
                   this function is called. */
                return;
              }
         }
       switch(f)
         { case PI_ATOM: t->args = pi_term.args;
                         return;
           case 'e'    : t->args = eulere.args;
                         return;
           case EULERGAMMA:  
                         t->args = eulergamma.args;
                         return;
           case 'i'    : t->args = complexi.args;
                         return;
           case RIGHT:
           case LEFT:   /* direction indicators for one-sided limits are
                     not put into varlist, so they have no value pointers. */
           case BOUNDED_OSCILLATIONS:
           case UNBOUNDED_OSCILLATIONS:
           case UNDEFINED:            /* no value pointer for undefined */
           case INFINITYFUNCTOR: return;     /* no value pointer for infinity  */
           case ILLEGAL:  return;     /* abstract on a  LINEARSYSTEM term gets here */
           case TRUEFUNCTOR: return;
           case FALSEFUNCTOR: return;
           case VAR: return;
           case PSEUDOATOM: return;
           default     : assert(0);
                   /* you should have found the atom in varlist */
         }
     }
  if(OBJECT(*t))
     return;
  n = ARITY(*t);
  for(i=0;i<n;i++)
    { set_valuepointers(ARGPTR(*t) +i);
    }
}
/*_______________________________________________________________*/
void set_dependency_info(term u, term t)
/*  u must be a variable. Set the dependency info in
varinfo to show u depending on all variables in t.
t must contain at least one variable.
*/
{ term *varlist = get_varlist();
  int nvariables = get_nvariables();
  varinf *varinfo = get_varinfo();
  int i,j;
  unsigned long dpinfo = 0L;
  for(i=0;i<nvariables;i++)
     { if(equals(u,varlist[i]))
          break;
     }
  if(i==nvariables)
     assert(0);
  /* Now set bits in dpinfo corresponding to the atoms in t */
  for(j=0;j<nvariables;j++)
     { if(j==i)
          continue;
       if(contains(t,FUNCTOR(varlist[j])))
          dpinfo |= (1<<j);
     }
  varinfo[i].dp = dpinfo;
}
/*_______________________________________________________________________________*/
void bindvar(term x)
/* set the variable x's scope in varinfo to BOUND */
{ term *varlist;
  int nvariables,i;
  varinf *varinfo;
  if(!ATOMIC(x))
     return;
  varlist = get_varlist();
  nvariables = get_nvariables();
  varinfo = get_varinfo();
  for(i=0;i<nvariables;i++)
     { if(FUNCTOR(varlist[i]) == FUNCTOR(x))
          { varinfo[i].scope = BOUND;
            return;
          }
     }
}
/*_______________________________________________________________________________*/
void unbindvar(term x)
/* set the variable x's scope in varinfo to UNBOUND */
{ term *varlist;
  int nvariables,i;
  varinf *varinfo;
  if(!ATOMIC(x))
     return;
  varlist = get_varlist();
  nvariables = get_nvariables();
  varinfo = get_varinfo();
  for(i=0;i<nvariables;i++)
     { if(FUNCTOR(varlist[i]) == FUNCTOR(x))
          { varinfo[i].scope = UNIVERSAL;
            return;
          }
     }
}

/*__________________________________________________________________*/

static char  *GreekString(term atom)
/* Return a static  string for a Greek atom,  enclosed in dollar signs like $\\alpha.
Eventually it will be printed by mstring on the graph parameter button.
Originally this use the OEM characters for Greek letters,  and mstring would interpret
OEM characters inside dollar signs.  That became obsolete 11.25.23.
*/
{  unsigned short f = FUNCTOR(atom);
   if (!PREDEFINED_ATOM(f))
      return NULL;
   switch(f)
     { case ALPHA:  return "\\alpha";
       case BETA:   return "\\beta";
       case EULERGAMMA: return "\\gamma";
       case SIGMA:  return "\\sigma";
       case MU:     return "\\mu";
       case THETA:  return "\\theta";
       case DELTA:  return "\\delta";
       case PHI:    return "\\phi";
       case EPSILON:  return "\\epsilon";
       case PI_ATOM:  return "\\pi";
       case LAMBDA:   return "\\lambda";
       //  following are not used in MathXpert
      //  case NU:       return "\\nu";
      // case BIGPSI:   return "\\Psi";
      // case PSI:      return "\\psi";
       // case OMEGA:    return "\\omega";
       // case BIGOMEGA:  return "\\Omega";
     
       default:        assert(0);
   }
}
    

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