Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/trigcalc/
Upload File :
Current File : /usr/home/beeson/MathXpert/trigcalc/induct.c

/* Operators for proofs by induction */
/* M. Beeson, for MathXpert
8.19.92 original date
2.27.98 last modified
*/
/*___________________________________________________________________*/
/* As a design decision, automode should NOT try to do double inductions,
though you can do so in menu mode.  The reason is that if you let it do so,
it will always try a double induction whenever it can't prove either the
basis case or the induction step, and this is ugly to see. */


#include <assert.h>
#include <string.h>

#include "globals.h"
#include "graphstr.h"
#include "mpdoc.h"
#include "prover.h"
#include "probtype.h"
#include "ops.h"
#include "trig.h"
#include "checkarg.h"
#include "induct.h"
#include "autosub.h"  /* psubst */
#include "intsub.h"   /* pushpending etc. */
#include "eqn.h"      /* ssolve */
#include "cflags.h"
#include "psubst.h"
#include "errbuf.h"
#include "dispfunc.h"
#include "mpmem.h"

#define MAXDEPTH 10  /* maximum depth of nested inductions */

static int indhyp[MAXDEPTH];   /* stores the indices of assumed induction
                                  hypotheses */
static int currentdepth = 0;   /* depth of current induction */
static int inductionvariable = -1;  /* negative means not chosen yet */
static void inhibit_sumop(term t);
static int auto_select(term);
static int get_start(term, term *);
static int select_aux(term t);

/* In proofs by induction, the induction variable is the
eigenvariable; this must be ensured when the problem is set up,
but it can be changed by selectinductionvariable */

/*  When we use 'basiscase' or 'inductionstep',
the current line changes to the new goal, and the original
problem is stored in the pending list.  It is actually put in as a
term AND(t,depth,k); the integer k is zero at first.  The integer
'depth' is the depth of nesting of inductive proofs, it's 1 for the
case of a simple induction, 2 for a double induction, and so on.
The bits of k have meaning as follows:
  bit 0:  basis case begun
  bit 1:  basis case completed
  bit 2:  induction step begun
  bit 3:  induction step finished

This allows the basis case and induction step to be done in either order,
and allows automode not to begin the basis case a second time after it's
just finished.  The variable currentdepth changes
value when something is pushed on the pending stack by basiscase
or inductionstep for the first time in a given induction, and is
decremented again by thereforeasdesired.

In terms of Gentzen sequent proofs, depth is the depth of the proof tree
(more or less; we've condensed several Gentzen steps in 'inductionstep');
and k tells you which branch you went up and whether the other one is done
yet.
*/
/* These macros handle the coding of k discussed above */
#define BASIS_BEGUN(k)  ((k)&1)
#define BASIS_DONE(k)   ((k)&2)
#define STEP_BEGUN(k)   ((k)&4)
#define STEP_DONE(k)    ((k)&8)
#define SET_BASIS_BEGUN(k)  k|= 1
#define SET_BASIS_DONE(k)  k|= 2
#define SET_STEP_BEGUN(k)  k|=4
#define SET_STEP_DONE(k)  k|=8

/*________________________________________________________________________*/
/* In order to make 'undo' work right, we must know the history of the
static global variables in this file, inductionvariable and currentdepth.
These are both integers, so we can simply keep a linked list
and add a new node each time one of them changes.   Because this
list must have document-duration,  we have to use permspace to
hold the nodes. */

typedef struct p { int iv;  /* induction variable */
                   int cd;  /* current depth */
                   int line;  /* line number */
                   struct p *link;
                 } historynode;

static historynode *varhist = NULL;

static void pushnode(void)
{ historynode *temp = varhist;
  varhist = permalloc(sizeof(historynode));
  if(varhist==NULL)
      { nospace();
        return;
      }
  varhist->link = temp;
  varhist->iv = inductionvariable;
  varhist->cd = currentdepth;
  varhist->line = get_currentline();
}

static void popnode(void)
{ historynode *temp = varhist;
  if(varhist == NULL)
     return;
  varhist = varhist->link;
  inductionvariable = temp->iv;
  if(inductionvariable >=0)
      set_eigenvariable(inductionvariable);
  else
      set_eigenvariable(0);  /* never set it negative */
  currentdepth = temp->cd;
/* no need to free temp as permspace isn't ever freed */
}



void undo_inductionvars(void)
/* needed by undo, therefore */
/* reset the values of the static globals in this file */
{ if(varhist == NULL)
      return;
  if(varhist->line == get_currentline()-1)
      popnode();
}

/*________________________________________________________________________*/
static void unassume(term t)
/*  t  has been assumed as an induction hypothesis and must now
be `removed' from the assumptions list; if t does not occur in
the assumptions list, do nothing. */

/* Any assumptions made DURING the induction step will remain; no
such assumptions may involve the induction variable--at present
nothing prevents such assumptions being generated. */

{ int k;
  for(k=get_nextassumption()-1; k >= 0; k--)
     { if(equals(t,get_assumption(k)))
          { push_assumption(trueterm,k);  /* this is how we 'remove' t */
            return;
          }
     }
}

/*________________________________________________________________________*/
int selectinductionvariable(term t, term arg, term *next, char *reason)
/* fails on purpose after the selection, no visible result.
   Therefore it must return 2
*/
{ int i,err;
  int nvariables = get_nvariables();
  char buffer[128];
  term *varlist = get_varlist();
  varinf *varinfo = get_varinfo();
  if(FUNCTOR(arg) != ILLEGAL)  /* variable selected by user */
     { for(i=0;i<nvariables;i++)
          { if(equals(varlist[i],arg))
                { if(varinfo[i].scope == BOUND)
                     { errbuf(0, english(554));
                        /* Can't use a bound variable for induction. */
                        return 1;
                     }
                  if(varinfo[i].type != INTEGER)
                     { varinfo[i].type = INTEGER;
                       assume(type(varlist[i],INTEGER));
                     }
                  inductionvariable = i;
                  set_eigenvariable(inductionvariable);
                  pushnode();
                  goto out;
                }
          }
       assert(0);  /* get_arg shouldn't take a non-variable */
     }
  /* Now user hasn't selected the induction variable */
  /* For now we just take the first free var of type integer that occurs in t */
  if(inductionvariable >=  0)
     return 1;   /* fail quickly, it's already chosen */
  err = simple_select(t);  /* is there an obvious compulsory choice? */
  if(err >= 0) goto out; /* success */
  err = auto_select(t);  /* do a more intelligent job */
  if(!err) goto out;
  return 1;
  /* two or more variables */
  out:
     strcpy(buffer, english(555));
     /* !Prove it by induction on  */
     strcat(buffer,atom_string(varlist[inductionvariable]));
     errbuf(0,buffer);
     return 2;  /* fail on purpose as problem is unchanged */
}
/* __________________________________________________________________*/
int simple_select(term t)
/* set inductionvariable for proving t by induction. Succeed only if
there is an obvious and compulsory choice.
return -1 in case there's no way to select an induction variable;
return -2 if there's more than one way (so this function doesn't do it);
return the value to which inductionvariable is set, if the function
succeeds. */

{ int i;
  term *varlist = get_varlist();
  varinf *varinfo = get_varinfo();
  int nvariables = get_nvariables();
  int countfree = 0;  /* count free variables */
  int countints = 0;  /* count free integer variables */
  int freeflag = -1;  /* index of first free variable when positive */
  for(i=0;i<nvariables;i++)
     { if(contains(t,FUNCTOR(varlist[i])))
         { if(varinfo[i].scope  != BOUND)
              {  ++countfree;
                 if(countfree == 1)
                     freeflag = 1;
                 if(varinfo[i].type == INTEGER)
                    { ++countints;
                      if(inductionvariable < 0)
                          { inductionvariable = i;
                            set_eigenvariable(i);
                            pushnode();
                          }
                    }

              }
        }
     }
  if(countfree == 0)
     return -1; /* t is constant */
  if(countints > 1)   /* more than one free integer variable */
    { set_eigenvariable(0);
      inductionvariable = -1;
      pushnode();
      return 1;
    }
  if(countints == 1)
    {  /* check for the cases of simple sum formulas which are on
          the menus separately */
      inhibit_sumop(t);
      return 0;
    }
  /* so now countints == 0 */
  if(countfree ==1)
    { inductionvariable = freeflag;
      set_eigenvariable(inductionvariable);
      assume(type(varlist[freeflag],INTEGER));
      varinfo[freeflag].type = INTEGER;
      pushnode();
      return 0;
    }
  assert(countints == 0 && countfree > 1);
  inductionvariable = -1;
  set_eigenvariable(0);
  return 1;
}

/*_________________________________________________________________*/
static int auto_select(term t)
/* select induction variable for proving t by induction,
when there is more than one free variable and
either no or more than one free integer variables */

{ int i;
  int countfree = 0;  /* count free variables */
  int countints = 0;  /* count free integer variables */
  int freeflag = -1;  /* index of first free variable when positive */
  term *varlist = get_varlist();
  varinf *varinfo = get_varinfo();
  int nvariables = get_nvariables();
  for(i=0;i<nvariables;i++)
     { if(contains(t,FUNCTOR(varlist[i])))
         { if(varinfo[i].scope  != BOUND)
              {  ++countfree;
                 if(countfree == 1)
                     freeflag = 1;
                 if(varinfo[i].type == INTEGER)
                    { ++countints;
                      if(inductionvariable < 0)
                          { inductionvariable = i;
                            set_eigenvariable(i);
                            pushnode();
                          }
                    }

              }
        }
     }
  assert(countints > 1 || (countints == 0 && countfree > 1));
     /* otherwise simple_select would have succeeded */
  if(countints == 0 && countfree ==1)
     /* just make the first free variable of type int and choose it */
    { inductionvariable = freeflag;
      set_eigenvariable(inductionvariable);
      pushnode();
      assume(type(varlist[freeflag],INTEGER));
      varinfo[freeflag].type = INTEGER;
      return 0;
    }
  /* Now countints > 1 ; just use the first integer variable */
  return 0;
}

/*________________________________________________________________________*/
int basiscase(term t, term arg, term *next, char *reason)
/* arg is the starting value of the induction, given by the user
   in menu mode and taken as zero by auto mode */

{ unsigned f = FUNCTOR(t);
  int k;
  term q;
  int err;
  term *varlist;
  term *theorems;
  int nexttheorem;
  int stackflag = 0;  /* set when pending stack is ready for further steps */
  if(get_problemtype() != INDUCTION)
     return 1;
  if(f != '=' && f != '<' && f != '>' && f != LE && f != GE && f != NE)
     return 1;
  nexttheorem = get_nexttheorem();
  theorems = get_theorems();
  if(nexttheorem && equals(theorems[nexttheorem-1],t))
     { errbuf(0,  english(556));
         /*  It's already proved. */
       return 1;
     }
  if(FUNCTOR(arg)==ILLEGAL)
    {  err = get_start(t,&arg);
       if(err) arg = zero;
    }
  err = readpending(&q);
  if(!err)  /* something on the pending stack */
    { if(
          (f == '=' && equals(ARG(0,t),ARG(1,t)))
          || (f != '=' && equals(t,trueterm))
        )
        { /* A verified identity is on-screen */
           assert(FUNCTOR(q) == AND && ARITY(q)==3 && TYPE(ARG(2,q))==INTEGER);
           k = (int) INTDATA(ARG(2,q));
           if(BASIS_BEGUN(k)
              && currentdepth == INTDATA(ARG(1,q))  /* need this if we are
                           going to be able to prove the basis case of one
                           induction by another induction */
             )

              { errbuf(0, english(557));
                  /*  Basis case is already done. */
                return 1;
              }
           else if (currentdepth == INTDATA(ARG(1,q)))
              { poppending(&q);
                k = (int) INTDATA(ARG(2,q));
                assert(STEP_BEGUN(k));
                SET_STEP_DONE(k);
                SET_BASIS_BEGUN(k);
                pushpending(and3(ARG(0,q),ARG(1,q),make_int(k)), get_currentline());
                t = ARG(0,q);
                unassume(t);   /*  was assumed by inductionstep */
                stackflag = 1;
              }
        }
      else if(get_mathmode() == MENUMODE)  /* don't do nested inductions in automode */
        {  commentbuf(0, english(558));
              /* Trying a proof by nested induction. */
           commentbuf(1, english(559));
              /* You can undo if you didn't try nested */
           commentbuf(2, english(560));
              /*  induction on purpose. */
        }
      else return 1;  /* this stops nested inductions in automode */
    }
  /* which variable is the induction variable? */
  if(inductionvariable < 0)  /* no variable yet selected */
      { err = select_aux(t);
        if(err<0)
           return 1;
      }
  SETCOLOR(arg,YELLOW);
  varlist = get_varlist();
  subst(arg,varlist[0],t,next);
  assume(le(arg,get_eigenvariable()));
  strcpy(reason, english(561));  /* basis case */
  if(!stackflag)
    {
      pushpending(and3(t,zero,one),get_currentline()); /* we don't use SET_BASIS_BEGUN to
                                        save a little space by using 'one' */
    }
  return 0;
}

/*________________________________________________________________________*/
int inductionstep(term t, term arg, term *next, char *reason)
{ unsigned f = FUNCTOR(t);

  int k;
  term q;
  int nexttheorem;
  term *theorems;
  term  nplusone;
  term *varlist;
  int err;
  int stackflag = 0;  /* set when pending stack is ready to go */
  if(get_problemtype() != INDUCTION)
     return 1;
  if(f != '=' && f != '<' && f != '>' && f != LE && f != GE && f != NE)
     return 1;
  theorems = get_theorems();
  nexttheorem = get_nexttheorem();
  varlist = get_varlist();
  if(nexttheorem && equals(theorems[nexttheorem-1],t))
     { errbuf(0, english(556));
         /* It's already proved. */
       return 1;
     }
  err = readpending(&q);
  if(!err)  /* something on the pending stack */
    { if(
          (f == '=' && equals(ARG(0,t),ARG(1,t)))
          || (f != '=' && equals(t,trueterm))
        )
        { /* A verified identity is on-screen */
           assert(FUNCTOR(q) == AND && ARITY(q)==3 && TYPE(ARG(2,q))==INTEGER);
           k = (int) INTDATA(ARG(2,q));
           if(STEP_BEGUN(k)
              && currentdepth == INTDATA(ARG(1,q))
             )
              { errbuf(0, english(562));
                  /* Induction step is already done. */
                return 1;
              }
           else if (currentdepth == INTDATA(ARG(1,q)))
              { poppending(&q);
                k = (int) INTDATA(ARG(2,q));
                assert(BASIS_BEGUN(k));
                SET_BASIS_DONE(k);
                SET_STEP_BEGUN(k);
                pushpending(and3(ARG(0,q),ARG(1,q),make_int(k)),get_currentline());
                t = ARG(0,q);
                stackflag = 1;
              }
        }
      else if(get_mathmode() == MENUMODE)
        {  commentbuf(0, english(558));
               /* Trying a proof by nested induction. */
           commentbuf(1, english(559));
               /* You can undo if you didn't try nested */
           commentbuf(2, english(560));
               /* induction on purpose. */
        }
      else return 1;  /* no nested inductions in automode */
    }
  /* which variable is the induction variable? */
  if(inductionvariable < 0)  /* no variable yet selected */
      { err = select_aux(t);
        if(err<0) return 1;
      }
  nplusone = sum(varlist[inductionvariable],one);
  psubst(nplusone,varlist[inductionvariable],t,next);
  indhyp[currentdepth] = get_nextassumption();
  assume(t);
  strcpy(reason, english(563)); /* induction step */
  commentbuf(0, english(564)); /* This is to be proved using */
  commentbuf(1, english(565)); /* the induction hypothesis. */
  if(!stackflag)
     { pushpending(and3(t,zero,zero),get_currentline());
       ++currentdepth;
       pushnode();
     }
  return 0;
}

/*________________________________________________________________________*/
int thereforeasdesired(term t, term arg, term *next, char *reason)
{ int err;
  term q;
  int k,i;
  int nextassumption = get_nextassumption();
  term *varlist;
  unsigned f = FUNCTOR(t);
  if(f != '=' && f != '<' && f != '>' && f != LE && f != GE)
     return 1;
  if(f == '=' && ! equals(ARG(0,t),ARG(1,t)))
     return 1;
  else if(f != '=' && !equals(t,trueterm))
     return 1;
  err = readpending(&q);
  if(err)
      return 1;
  if(FUNCTOR(q) != AND)
     return 1;
  if(ARITY(q) != 3)
     return 1;
  if(TYPE(ARG(2,q)) != INTEGER)
     return 1;
  k  = (int) INTDATA(ARG(2,q));
  if(!BASIS_BEGUN(k) || !STEP_BEGUN(k))
     return 1;
  varlist = get_varlist();
  poppending(&q);
  *next = ARG(0,q);
  strcpy(reason, english(2167));  /* proved by induction */
  unassume(*next);  /* assumed by inductionstep */
  /* Now check that no assumptions depend on the induction variable */
  for(i=0;i<nextassumption;i++)
    { if(depends(get_assumption(i),varlist[inductionvariable]))
         { errbuf(0, english(567));
              /* Some assumption depends on the induction variable. */
           errbuf(1, english(568));
              /* Possibly what you are trying to prove isn't */
           errbuf(2, english(569));
              /* correct without more assumptions. */
         }
    }
  --currentdepth;
  record_theorem(*next);  /* prover.h */
  return 0;
}
/*______________________________________________________________________*/
int useinductionhyp(term t, term arg, term *next, char *reason)
/* apply the induction hypothesis */
{ int err,k;
  unsigned f,g;
  term q,leftside,rightside;
  if(get_problemtype() != INDUCTION)
      return 1;
  err = readpending(&q);
  if(err)
     return 1;
  if(FUNCTOR(q) != AND)
     return 1;
  if(ARITY(q) != 3)
     return 1;
  if(TYPE(ARG(2,q)) != INTEGER)
     return 1;
  k = (int) INTDATA(ARG(2,q));
  if(!(STEP_BEGUN(k) && !STEP_DONE(k)))
      { errbuf(0, english(570));
           /* You aren't working on an induction step now */
        errbuf(1, english(571));
           /* so you don't have an induction hypothesis. */
        return 1;
      }
  f = FUNCTOR(ARG(0,q));
  if(f == '=' || f == '<' || f == LE || f == GE)
    { leftside = ARG(0,ARG(0,q));
      rightside = ARG(1,ARG(0,q));
    }
  else
    { leftside = ARG(1,ARG(0,q));
      rightside = ARG(0,ARG(0,q));
    }
  if(f == '=')
    /* first try it left-to-right, then if that fails try it right-to-left */
    /* but only left-to-right in auto mode, or we get loops */
    { if(subst(rightside,leftside,t,next) ||
         (get_mathmode() == MENUMODE && subst(leftside,rightside,t,next))
        )
         { strcpy(reason, english(572)); /* induction hypothesis */
           release(orderterms);  /* inhibited by splitofflastterm */
           return 0;
         }
      return 1;  /* not applicable */
    }
  g = FUNCTOR(t);
  if( g == '<' || g == LE)
    { return 1;  /* FINISH THIS */
    }
  return 1;
}
/*________________________________________________________________*/
static void inhibit_sumop(term t)
/* inhibit operators already on the sigma-notation menu so they
can be proved by induction in automode */
{ term u,index,lo,hi,left;
  if(FUNCTOR(t) != '=') return;
  left = ARG(0,t);
  if(FUNCTOR(left) != SUM) return;
  u = ARG(0,left);
  index = ARG(1,left);
  lo = ARG(2,left);
  hi = ARG(3,left);
  if(equals(u,index) && ONE(lo) && ISATOM(hi))
     { inhibit(sumofi);
       return;
     }
  if(FUNCTOR(u) == '^' && equals(ARG(1,u),two) && equals(ARG(0,u),index)
     && ONE(lo) && ISATOM(hi)
     )
     { inhibit(sumofisquared);
       return;
     }
  if(FUNCTOR(u) == '^' && equals(ARG(1,u),index) && ZERO(lo))
     { inhibit(sumofallpowers);
       inhibit(differenceofnthpowers);
       return;
     }
}
/*___________________________________________________________________*/
static int get_start(term t, term *ans)
/* get the starting value of the induction variable.  At the top-level
call t is the thing to be proved by induction.  Return 0 for success.
Succeed if t involves a sum containing the induction variable in the upper
limit, in which case we look at the lower limit. */

{ int err;
  term hi,lo;
  unsigned short i,n;
  term *varlist;
  if(inductionvariable < 0)
     return 1;
  if(ATOMIC(t))
     return 1;  /* failure */
  if(FUNCTOR(t)==SUM)
     { lo = ARG(2,t);
       hi = ARG(3,t);
       varlist = get_varlist();
       if(!contains(hi,FUNCTOR(varlist[inductionvariable])))
           return 1;
       if(equals(hi,varlist[inductionvariable]))
           { *ans = lo;
             return 0;
           }
       err = ssolve(equation(hi,lo),varlist[inductionvariable],ans);
       return err;
     }
   n = ARITY(t);
   for(i=0;i<n;i++)
     { err = get_start(ARG(i,t),ans);
       if(!err)
          return 0;
     }
   return 1;
}
/*_______________________________________________________*/
static int select_aux(term t)
/* select a variable by simple_select, returning its index in varlist,
or if not possible, leave an error message and return a negative value. */
 { int err = simple_select(t);
   if(err == -1)
      { errbuf(0, english(573));
           /* No variable present, can't use induction. */
        return 1;
      }
   else if(err == -2)
      { errbuf(0, english(574));
           /* More than one possible induction variable. */
        errbuf(1, english(575));
           /* First use \"select induction variable\" */
        return 1;
      }
   return err;
}
/*___________________________________________________________*/
void reset_induction(void)
/* reset the values of all local static variables to
   their default values.  This is called by reset in topaux.c
   and in the windows version it must be called by allocate_doc_data */
{ int i;
  inductionvariable = -1;
  currentdepth = 0;
  for(i=0;i< MAXDEPTH; i++)
    indhyp[i] = 0;
  varhist = NULL;
   /* don't worry about freeing the list, permspace is reset
      automatically at the beginning of a new document. */
}

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