Sindbad~EG File Manager

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

/* M. Beeson, user model in MATHPERT */
/* original date 5.5.91
   last modified 1.23.98
*/

#define AUTOMODE_DLL
#include <assert.h>
#include <stdlib.h>
#include "globals.h"   /* 'actualop' */
#include "mtext.h"     /* MAXMENUS   */
#include "operator.h"
#include "checkarg.h"
#include "ops.h"
#include "graphstr.h"   /* document.h needs it */
#include "display.h"
#include "document.h"   /* automode.h needs it */
#include "automode.h"
#include "trig.h"      /* ssolveop  */
#include "calc.h"      /* polyvalop etc. */
#include "optable.h"
#include "mpmem.h"    /* permalloc,permfree used in inhibit */
#include "symbols.h"   /* optable   */
#include "menusize.h"
#include "userfunc.h"  /* MAXUSERFUNCTIONS */

static int model_initialized = 0;   /* set to 1 by init_default_model or,
                                in the DOS version, by model_from_disk */
static int lookupop(actualop code, operation *op);
static void init_default_model(void);
static short hash(actualop op);
static int count_inhibitions(int index, inhibition *inhibitions);
static int stopflag;
/*_______________________________________________________________________*/
static hashbucket *model;

MEXPORT_AUTOMODE void set_model(hashbucket *mod)
/* called by activate  */
{ model = mod;
}
/*_______________________________________________________________________*/

/* DESIGN: We have to look up information from the 'actualop', the executable
code of an operator.  This is done by hashing with linear open addressing,
as explained on pp. 518-519 of Knuth volume 3. */
/* See model.h for more remarks */


/*___________________________________________________________________________*/
static short hash(actualop op)
  /* hash(k) =  k mod p, where p is a suitable prime number */
  /* Hence hash(x+y) = hash(x) + hash(y) as required in savemod.c */
  /* According to Knuth volume 3, page 509, we should choose p to be
          a prime number not close to any power of 2.  This prime, which
     is #-defined as HASHPRIME in model.h, will also be the dimension
     of the hash table, and so should be chosen between 2^10 and 2^11--
     smaller to decrease the size of the hash table, larger to decrease
     the load factor and improve efficiency.  */

{ return (short) (((unsigned long) op) % HASHPRIME);
}
/*___________________________________________________________________________*/
MEXPORT_AUTOMODE short access_mod(actualop op)
/*  return the integer index such that model[index] is the entry
    corresponding to actualop */
/*  Assumes that the dimension of 'model' array is more than the number
    of operators whose statuses have to be stored.  If access is requested
    for an operator not in the model when the model array is already full,
    an unterminating loop will occur; but long before that point the model
    array will be "overloaded" and function too slowly.  The load factor
    should be kept under 2/3.  The number of menus times 16 should be
    at most 2/3 of HASHPRIME.
      There are (5.7.91) 57 menus of non-graphing operators, so
    there's plenty of room. Note, in June 1996 there are 120 menus, but
    HASHPRIME has been increased to make more room in the meantime.
*/

{ short index = hash(op);
  int err;
  hashbucket p = model[index];
  operation menuop;
  while( p.used && (void  *) access_optable(p.men)[p.choice] != (void  *) op )
     { --index;
       if(index < 0)
          index = HASHPRIME-1;  /* cycle */
       p = model[index];
     }
  if(!p.used)   /* this operator isn't in the model yet */
     { p.used = 1;
       p.status = KNOWN;    /* by default */
       p.inhibited = 0;     /* operators get inhibited only by other operators */
       err = lookupop(op,&menuop);
       if(err && model_initialized) /* then something is wrong!  DEBUG */
          assert(0);  /* This can be caused by a missing brace in
                         postops or preops, or by an operation invoked
                         in term selection mode or by automode that
                         is not listed anywhere in optable.c  */
       p.men = menuop.men;
       p.choice = menuop.choice-1;
       model[index] = p;   /* p is only local; write it into the global array */
     }
  return index;
}
/*__________________________________________________________________*/
MEXPORT_AUTOMODE void code_to_op(actualop code, operation *ans)
/* look up 'code' in the model by hashing and return the
corresponding operator (menu and choice number)
*/
{ hashbucket b = model[access_mod(code)];
  ans->men = b.men;
  ans->choice = b.choice+1;
}
/*___________________________________________________________________________*/
MEXPORT_AUTOMODE int status(actualop op)
/* get the status of actualop from the student model.
 Return either UNKNOWN, LEARNING, KNOWN, or WELLKNOWN */
{
  return model[access_mod(op)].status;
}
/*______________________________________________________*/
MEXPORT_AUTOMODE void set_status(actualop op, int newvalue)
/* set the status of op to newvalue */
/* if op isn't in the model, access will cause a crash */
{ int index = access_mod(op);
  model[index].status = newvalue;
  if((void  *) op == (void  *) arithmetic)
     set_model_arithflag(newvalue);
}
/*___________________________________________________________________________*/
/* initialize user model to default values */
static void init_default_model(void)
/* model is passed as an array of dimension HASHPRIME */
{ int i,j,k;
  actualop *optable;
  aflag arithflag;
  arithflag.fract = 1;
  arithflag.varadd = 1;   /* do arithmetic even on non-adjacent terms */
  arithflag.intexp = 1;
  arithflag.negexp = 1;
  arithflag.ratexp = 1;
  arithflag.roots = 0;
  arithflag.pure = 0;
  arithflag.gcd = 1;
  arithflag.abs = 0;    /* don't evaluate abs */
  arithflag.relop = 0;  /* don't let arithmetic work on '=', <, etc.
                        lpt() sets a local flag when calling arith() so
                        arith can work on '=' and < when called from lpt() */
  arithflag.factorial = 1;
  arithflag.matrix =  0;
  arithflag.inverse = 0;
  arithflag.functions=0;
  arithflag.sums = 0;  /* because it says "arithmetic (not on �)" on the menus */
  arithflag.flt = 0;
  arithflag.complex = 0;
  arithflag.complexpowers = 0;
  arithflag.comdenom = 1;
  arithflag.mod = 0;
  set_arithflag(arithflag);
    /* Now initialize the hash table */
  model_initialized = 0;
  for(i=0;i<MAXMENUS;i++)
     { menusize(i,&k);  /* k is number of items including "finished" */
       optable = access_optable(i);
       for(j=0;j<k-1;j++)
          access_mod(optable[j]);
     }
  /* The operators on the functions_menu  menu, namely applyfunction0,
     applyfunction1, etc., haven't been put in yet because menusize
     returns the number of function definitions for that menu,
     rather than MAXUSERFUNCTIONS.
  */
  optable = access_optable(functions_menu);
  for(j=0;j<MAXUSERFUNCTIONS;j++)
     access_mod(optable[j]);

  /* 'access' initializes everything to KNOWN.  Let's set a  few things
  to WELLKNOWN.  */
  set_status(arithmetic,WELLKNOWN);
  set_status(pushminusin,WELLKNOWN);
  set_status(doubleminus,WELLKNOWN);
  set_status(multiplyfractions,WELLKNOWN);

  /* Finally by default set the powerful operators to LEARNING */
  set_status(polyvalop, LEARNING);
  set_status(ssolveop,LEARNING);
  set_status(multiplyoutandsimp,LEARNING);
  set_status(limvalop,LEARNING);
  set_status(simpleint,LEARNING);
  set_status(derivop,LEARNING);
  model_initialized=1;
}
/*____________________________________________________________________*/
/* Inhibited operators will not be applied by one-step and hence not
used in automode or think_ahead.  An operator can be inhibited more
than once, and each 'release' undoes just one inhibition.  The
.inhibited field of model[index] tells if that operator is inhibited
at the moment.  In order that 'undo' can undo inhibitions, we must
know at which line the inhibition took effect.  We therefore keep
a linked list of inhibitions, in chronological order.  The nodes
of this list live in 'permspace' so they have the lifetime of the
document. */

/* 'inhibition' is defined in model.h */

/*____________________________________________________________________*/
void SetStopFlag(int n)
/* call this with n = 1 to stop the inhibit/release mechanism from working
while an operator is tried.  Call it with n = 0 to reactivate the mechanism.
This is used to turn off the mechanism when using operators to construct
the Term Selection Menu or in the prover.
*/
{ stopflag = n;
}

int GetStopFlag(void)
/* when you want to turn the mechanism off, and then back to the original state,
you'll need to know the original state. */
{ return stopflag;
}
/*____________________________________________________________________*/
MEXPORT_AUTOMODE void inhibit(actualop code)
/* Can be called (by an operator) to prevent automode from
using the inhibited operator until another operator 'releases' it.
Example: findcommondenominator inhibits arithmetic and
multiplyfractions releases it.
  Push an 'inhibit' node onto the inhibitions list
  Does nothing if stopflag is nonzero.
*/
{ int index = access_mod(code);
  inhibition *p;
  inhibition *inhibitions;
  if(stopflag)
     return;   /* doing nothing */
  inhibitions = get_inhibitions();
     /* head of the linked list of inhibitions */
  model[index].inhibited = 1;
     /* Now push a node onto the inhibitions list */
  p = (inhibition *) permalloc(sizeof(inhibition));
  p->index = (short) index;
  p->kind = 1;  /* an inhibition */
  p->linenumber = (short)(get_currentline() + 1);
     /* currentline is the last one on the screen, which we are
        working on.  This inhibition is being made as history[currentline+1]
        is being created */
  p->link = inhibitions;
  set_inhibitions(p);
}
/*____________________________________________________________________*/
MEXPORT_AUTOMODE void temp_inhibit(actualop code)
/* used instead of inhibit when you know it will be released in the same
line, e.g. in reduce_ineq.  This avoids allocating nodes on the inhibitions list.
*/
{ int index = access_mod(code);
  model[index].inhibited = 1;
}
/*____________________________________________________________________*/
MEXPORT_AUTOMODE void temp_release(actualop code)
/* used together with temp_inhibit, when finished with the inhibition */
{ int index = access_mod(code);
  model[index].inhibited = 0;
}
/*____________________________________________________________________*/

MEXPORT_AUTOMODE void release(actualop code)
/* Push a 'release' node onto the inhibitions list,
   unless the net count of inhibitions of the operator
   in question is already zero. */
{ int index = access_mod(code);
  inhibition *p;
  int count;
  inhibition *inhibitions;
  if(stopflag)
     return;
  inhibitions = get_inhibitions();
     /* head of the linked list of inhibitions */
  /* was this operation inhibited multiple times? */
  count = count_inhibitions(index, inhibitions);
  if(count == 1)
     model[index].inhibited = 0;  /* release the operation */
     /* Now push a node onto the inhibitions list */
  if(count == 0)
     return;   /* don't push an unnecessary node */
  p = (inhibition *) permalloc(sizeof(inhibition));
  p->index = (short) index;
  p->kind = -1;  /* a release */
  p->linenumber = (short)(get_currentline() + 1);
     /* currentline is the last one on the screen, which we are
        working on.  This inhibition is being made as history[currentline+1]
        is being created */
  p->link = inhibitions;
  set_inhibitions(p);
}
/*_________________________________________________________________*/
static int count_inhibitions(int index, inhibition *inhibitions)
/* return the net number of inhibitions of the operation
at model[index], that is, inhibitions minus releases.
*/

{ int count=0;
  inhibition *marker;
  for(marker = inhibitions; marker != NULL; marker = marker->link)
      { if(marker->index == index)
           { if(marker->kind > 0)
                ++count;  /* an inhibition */
             else if(marker->kind < 0)
                --count;  /* a release */
           }
      }
  if(count < 0)
     assert(0);  /* release never pushes superfluous nodes */
  return count;
}

/*__________________________________________________________________*/
MEXPORT_AUTOMODE void undo_inhibitions(void)
/* delete all nodes from the head of the inhibitions list that were
made at the current line.  Check whether that operator is still inhibited
by another node further down the list.  If not, adjust its
.inhibited field in the model.
*/
{  inhibition *temp;
   int ndx;
   int count;
   int currentline = get_currentline();
   inhibition *inhibitions = get_inhibitions();
   if(inhibitions==NULL)
      return;
   while(inhibitions && inhibitions->linenumber == currentline)
      { ndx = inhibitions->index;  /* model[ndx] is the inhibited operator */
        temp = inhibitions;
        set_inhibitions(inhibitions->link);
        inhibitions = inhibitions->link;
        count = count_inhibitions(ndx, inhibitions);
        if(count == 0)
           model[ndx].inhibited = temp->kind > 0 ? 0 : 1;
           /* operation doesn't occur in rest of list or
              is released as many times as inhibited, so
              release this inhibition. */

        if(count > 0 && temp->kind == -1)
           /* it was inhibited before and a release
              node is being deleted */
           model[ndx].inhibited = 1;  /* inhibit it again */


        permfree(temp);  /* a do-nothing function in Windows Mathpert,
                            where permspace is never freed.   */
      }
}
/*___________________________________________________________________*/
MEXPORT_AUTOMODE void kill_inhibitions(void)
/* does not free memory used in list of inhibitions (cleanup will
take care of that).  Just adjusts the model array and sets 'inhibitions'
to NULL */

{  inhibition *p = get_inhibitions();
   while(p != NULL) /* traverse the list and adjust the model */
      { model[p->index].inhibited = 0;
        p = p->link;
      }
   set_inhibitions(NULL);
}
/*____________________________________________________________________*/
MEXPORT_AUTOMODE int inhibited(actualop code)
{ return model[access_mod(code)].inhibited;
}
/*___________________________________________________________________*/
MEXPORT_AUTOMODE void set_model_arithflag(int s)
/* make sure arithflag properly reflects the status of 'arithmetic' */
/* assumes that arithflag is already initialized for SOME status, i.e.
it sets only those fields that change with the status. */
{ aflag arithflag = get_arithflag();
  switch(s)
    { case UNKNOWN:
      case LEARNING:
         arithflag.comdenom = 0;
         arithflag.roots = 0;
         break;
      default:
         arithflag.comdenom = 1;
         arithflag.roots = 1;
         break;
    }
  set_arithflag(arithflag);
}
/*__________________________________________________________________*/
MEXPORT_AUTOMODE void init_model(int currenttopic, hashbucket *mod)
/* initialize the default model, then adjust it to the specified
current topic */
{ hashbucket *saveit = model;
  model = mod;
  init_default_model();
  adjust_model(currenttopic);
  model = saveit;
}

/*_____________________________________________________________________*/
static int lookupop(actualop code, operation *op)
/* given the (name of) an operator, find the (or at least a) corresponding
menu and choice number.  In other words, invert the 'optable'
in exec.c.  This is used to build the hash table in model, so you
can't presume that table exists. */
/* Return zero for success */

{ int n,i,j;
  actualop *optable;
  for(i=0;i<MAXMENUS;i++)
    { menusize(i,&n);
      optable = access_optable(i);
      for(j=0;j<n-1;j++) /* n-1 because last item is "finished" */
         { if((void  *) optable[j] == (void  *) code)
              { op->men = i;
                op->choice = j+1;
                return 0;
              }
         }
    }
  return 1;
}

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