Sindbad~EG File Manager

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

/* Operators for minmax and related_rates which don't appear on other menus */
/*
M. Beeson, for MathXpert
Original date 9.27.92
last modified 2.18.99
1.25.06 modified selectmax_aux to not depend on the contents of errbuf to determine success or failure
1.25.06 made rejectpoint work on a MATRIX term
1.25.06 modified tab and then put it back more or less the same as it was
5.20.13 removed include getprob.h
5.29.13 added return 0 in selectmax_aux, which seems to have been deleted somehow.
        deleted label :nominmax and code that used in selectmax_aux; this code was wrong.
6.11.13  corrected select_max, which did not return 1 if selectmax_aux fails.
3.18.23  changed comma to seimicolon, line 514.
3.19.23  added else return 1 in ep_aux  and add_endpoints
12.11.23  replaced oem_atom_string by atom_string
         and if-0'd nominmaxmsg
*/


#include <string.h>
#include <assert.h>
#define sincos _foo
#include <math.h>
#undef sincos
#include "globals.h"
#include "graphstr.h"
#include "mpdoc.h"
#include "ops.h"
#include "calc.h"
#include "checkarg.h"
#include "operator.h"
#include "prover.h"
#include "algaux.h"
#include "deriv.h"
#include "nextstep.h"  /* opseq */
#include "sing.h"      /* get_sing */
#include "domain.h"
#include "deval.h"
#include "symbols.h"
#include "cflags.h"
#include "errbuf.h"
#include "mpminmax.h"
#include "dispfunc.h"
#include "pvalaux.h"
#include "mpmem.h"
#include "cancel.h"
#include "elim.h"

static int tab_aux(term t, int flag, term *next, char *reason);
static int tab(int,term,term,term *);
static int selectmax_aux(term, term *, int);
static int ep_aux(term t, term *ans);

#define CLOSED(x)    (ARITY(x) == 2 && FUNCTOR(ARG(0,x)) == LE && FUNCTOR(ARG(1,x)) == LE)
#define RIGHT_OPEN(x)  ((ARITY(x) == 1 && equals(ARG(1,x),varlist[eigenvariable])) || (ARITY(x)==2 && FUNCTOR(ARG(1,x)) == '<'))
#define LEFT_OPEN(x)  ((ARITY(x) == 1 && equals(ARG(0,x),varlist[eigenvariable])) || (ARITY(x)==2 && FUNCTOR(ARG(0,x)) == '<'))

/*________________________________________________________________________________________*/
static int maxflag;  /* set when "No max" message is put in errbuf */
static int minflag;  /* set when "No min" message is put in errbuf */
static int minmaxflag;  /* set when "No min or max" message is put in errbuf  */
/* The purpose of these flags is to prevent "No max" or "No min" messages from
being printed twice.  They are set to zero when we tabulate values,
and then set when those error messages are printed.  They are also used by 
fremark to see if we're done.  */  

int get_maxflag(void) { return maxflag;}
int get_minflag(void) { return minflag;}
int get_minmaxflag(void) { return minmaxflag;}

/*_______________________________________________________________*/
/*  In solving minmax problems, the user enters the formula to be maximized
or minimized (or whose extreme points are to be found), and (optionally)
an interval.  She then
constructs a list of equations to be solved, by throwing in the
critical points, the endpoints, and the points where f'(x) is undefined.
(The phrase 'critical point' is not used, because its definition varies
from book to book. Some books include the points where f'(x) is undefined
as critical points and some do not.)  These equations can then be solved
like any other list of equations.  In addition, you can reject roots
lying outside a specified interval, once the equations are solved.
(You can't reject an unsolved equation by proving that its roots, whatever
they are, lie outside the interval.  Problems like that aren't given to
freshmen anyway.)   You can't turn on complex variables for this problem type.

Specifying the interval to consider:
The interval to be considered has to be specified by the user.   It does not
appear in the list of equations to be solved, as is logically correct:
these equations are connected by OR, and this OR is connected to the
interval (which is an AND of inequalities) by an AND.  It can be specified by
an operator on the menus, "Specify interval to consider".
Once specified it will be displayed in the assumption window.
It must be specified at the same time as the problem, directly in
the problem window.

The (address of) the interval will be kept as the value of the global
variable 'minmax_intervalp'.  This will be the address of one of the
assumptions, often the first one, but not necessarily if other assumptions
were made during analysis of the domain.

Enhance_problem makes sure that the expression to be analyzed has the
form of an equation y = u(x)  if the user did not enter an equation;
the user could also enter f(x) = ...expression in x.   After
enhance_problem, then, the expression history(0) will be an equation.
The interval, if entered at the problem window, is removed to the
list of assumptions.

If we are dealing with a function continuous on a closed interval,
we can just tabulate the values.  We can do that anyway, but in the
case of an interval without endpoints, selectmax and selectmin won't be
valid operators.  We have to add the limits at the endpoints first.

*/
/* mpminmax.h gives the choice numbers of the various operators
on the maxima_and_minima menu, so these can be changed by changing
these defines only */




#define THEPROBLEM   ARG(1,history(0))

/*_______________________________________________________________*/
static term closure(term t)
/* t must be an interval; return its closure, i.e. the closed
interval with the same endpoints.  t could be a two-sided or
a one-sided interval.   */

{ switch(FUNCTOR(t))
     { case AND: return and(closure(ARG(0,t)),closure(ARG(1,t)));
       case '<': return le(ARG(0,t),ARG(1,t));
       default : return t;
     }
}
/*_______________________________________________________________*/
int functionisconstant(term t,term arg, term *next, char *reason)
/* finish the problem quickly when the function is constant */
{ term problem,v;
  copy(THEPROBLEM,&problem);
  if(!seminumerical(problem) && contains(problem,FUNCTOR(get_eigenvariable())))
     return 1;
  if(!equals(t,history(get_currentline())))
     return 1;  /* only works at toplevel */
  if(!equals(ARG(1,t),problem))
     return 1;
  v = make_term(VECTOR,2);
  ARGREP(v,0,get_minmax_interval());
  ARGREP(v,1,problem);
  *next = make_term(MATRIX,1);
  ARGREP(*next,0,v);
  HIGHLIGHT(*next);
  strcpy(reason, english(2241));  /* function is constant */
  commentbuf(0,english(2242));  /* Max and min are the same for a constant function. */
  return 0;
}

/*_______________________________________________________________*/
int minmaxexperiment(term t,term arg, term *next, char *reason)
/* Allow the user to see numerical values of the function */
{ term problem;
  copy(THEPROBLEM,&problem);
  return evalatpoint(problem,arg,next,reason);
}

/*_______________________________________________________________*/
int addcriticalpoints(term t,term arg, term *next, char *reason)
{ term x = get_eigenvariable();
  term new;
  int i;
  term u;
  unsigned short f = FUNCTOR(t);
  int currentline = get_currentline();
  if(!equals(t,history(currentline)))
      return 1;   /* this works at toplevel only */
  copy(THEPROBLEM,&u);
  if(f != '=' && f != OR && f != FALSEFUNCTOR)
     return 1;   /* after you've tabulated values it's too late */
  if(used2(ADDCRITICALPOINTS))    /* can't use this one more than once */
     return 1;
  if(seminumerical(u) || !contains(u,FUNCTOR(x)))
     { errbuf(0, english(2242));
       errbuf(1, english(2243));
       /* Max and min are the same for a constant function.
          It is not necessary, and will not help, to take the derivative. */
       return 1;
     }
  new = equation(diff(u,x),zero);
  SETCOLOR(new,YELLOW);
  if(FUNCTOR(t) == '=' || equals(t,falseterm))   /* a single equation */
      { *next = new;
      }
  else if(FUNCTOR(t) != OR)
     return 1;
  else  /* FUNCTOR(t) == OR */
      {  /* a system of equations, if 'add end points'
            has been chosen first, for example */
        *next = make_term(OR,(unsigned short)(ARITY(t)+1));
        ARGREP(*next,0,new);
        for(i=0;i<ARITY(t);i++)
           { ARGREP(*next,i+1,ARG(i,t));
           }
      }
  if(currentline==0)  /* this is the first operator chosen */
     strcpy(reason, english(988));   /* critical points */
  else
     strcpy(reason, english(989));   /* add critical points */
  return 0;
}
/*_______________________________________________________________*/
int addendpoints(term t,term arg, term *next, char *reason)
{ term a,b,temp;
  term x = get_eigenvariable();
  term interval;
  term new;
  unsigned short f,n;
  int i;
  unsigned short g = FUNCTOR(t);
  int currentline = get_currentline();
  if(!equals(t,history(currentline)))
      return 1;   /* this works at toplevel only */
  if(g != '=' && g != OR && g != FALSEFUNCTOR)
     return 1;   /* after you've tabulated values it's too late */
  if(used2(ADDENDPOINTS))  /* can't use this one more than once */
     return 1;
  interval = get_minmax_interval();
  f = FUNCTOR(interval);
  if(f==AND)
    { a = ARG(0,ARG(0,interval));
      b = ARG(1,ARG(1,interval));
    }
  else if(equals(ARG(0,interval),x))
    { a = minusinfinity;
      b = ARG(1,interval);
    }
  else if(equals(ARG(1,interval),x))
    { a = ARG(0,interval);
      b = infinity;
    }
  else
    return 1;
  new = or(NOTDEFINED(a) ? arrow(x,a) : equation(x,a),
           NOTDEFINED(b) ? arrow(x,b)  :equation(x,b)
          );
  HIGHLIGHT(new);
  if(FUNCTOR(t) == OR)
     { remove_dups(t,&temp);
       t = temp;
     }
  if(currentline==0  /* this is the first operator chosen */
     || equals(t,falseterm)   /* previous equation simplified to false */
    )
     { *next = new;
       strcpy(reason, english(990));  /* endpoints */
       return 0;
     }
  else if(FUNCTOR(t)==OR)
     { n = (unsigned short)(FUNCTOR(new)==OR ? 2 : 1);
       *next = make_term(OR,(unsigned short)(ARITY(t) + n));
       if(n==1)
          ARGREP(*next,0,new);
       else
          { ARGREP(*next,0,ARG(0,new));
            ARGREP(*next,1,ARG(1,new));
            SETCOLOR(ARG(0,*next),YELLOW);
            SETCOLOR(ARG(1,*next),YELLOW);
          }
       for(i=n;i<ARITY(*next);i++)
          { ARGREP(*next,i,ARG(i-n,t));
          }
       goto out;
     }
  else if(FUNCTOR(new) != OR)
     { *next = or(new,t);
       goto out;
     }
  else
     { assert(ARITY(new) == 2);
       *next = make_term(OR,3);
       ARGREP(*next,0,ARG(0,new));
       ARGREP(*next,1,ARG(1,new));
       HIGHLIGHT(ARG(0,*next));
       HIGHLIGHT(ARG(1,*next));
       ARGREP(*next,2,t);
       goto out;
     }
  out:
  if(FUNCTOR(*next) == OR)
     { temp = *next;
       remove_dups(temp,next);
       if(ARITY(*next) == ARITY(t) && FUNCTOR(t) == OR)
          { errbuf(0, english(1033));
              /* Both endpoints are already listed. */
            return 1;
          }
     }
  strcpy(reason, english(991));  /* add endpoints */
  return 0;
}
/*_______________________________________________________________*/
term complement(term t)
/* return the complement of an interval or
   Boolean combination of intervals, including the case of an
   interval defined by a single inequality.  If any other input
   is passed (not intended, but not guaranteed when called from
   nextline.c on newly-introduced assumptions in minmax problems)
   just returns the logical negation of t.

   When t contains an existential variable, complement(t) should,
   but does not yet, take this into account.  For example, the set
   where cosine is nonzero is given by n pi- pi/2 < x < n pi+ pi/2, but
   because n is existential, the complement is  x=k pi+ pi/2, where k
   is also existential.
*/

{  unsigned short f = FUNCTOR(t);
   unsigned short n = ARITY(t);
   term ans, cancelled, m,a,b;
   int i;
   int flag = 0;  /* set if we take complement of an equality */
   if(equals(t,trueterm))
      return falseterm;
   if(equals(t,falseterm))
      return trueterm;
   if(interval_as_and(t) && contains_existentials(t))
      { /* see comments above */
        a = ARG(0,ARG(0,t));
        b = ARG(1,ARG(1,t));
        if(FUNCTOR(ARG(0,t)) == '<' && FUNCTOR(ARG(1,t)) == '<' &&
           FUNCTOR(a) == '+' && FUNCTOR(b) == '+' &&
           equals(ARG(0,a),ARG(0,b)) &&
           ARITY(a) == 2 && ARITY(b) == 2 &&
           NEGATIVE(ARG(1,a)) &&
           equals(ARG(0,ARG(1,a)),ARG(1,b)) &&
           FUNCTOR(ARG(0,a)) == '*'  &&
           !cancel(ARG(0,ARG(0,a)),pi_term,&cancelled,&m)
          )
           { if(ISATOM(m) && FRACTION(ARG(1,b)) &&
                equals(ARG(1,ARG(1,b)),two) &&
                equals(ARG(0,ARG(1,b)),pi_term)
               )
                 return equation(ARG(0,ARG(1,t)),b);
             if(FUNCTOR(m) == '*' && ISINTEGER(ARG(0,m)) &&
                ARITY(m) == 2 && ISATOM(ARG(1,m)) &&
                equals(ARG(1,b),pi_term)
               )
                 return equation(ARG(0,ARG(1,t)),b);
            }
      }
   if(f == AND || f == OR)
      { ans = make_term((unsigned short)(f==AND ? OR : AND),n);
        for(i=0;i<n;i++)
           { ARGREP(ans,i,complement(ARG(i,t)));
             if(FUNCTOR(ARG(i,t))== '=')
                flag = 1;
           }
        return flag ? topflatten(ans) : ans;
      }
   switch(f)
     { case LE:  return lessthan(ARG(1,t),ARG(0,t));
       case '<': return le(ARG(1,t),ARG(0,t));
       case GE:  return lessthan(ARG(0,t),ARG(1,t));
       case '>': return le(ARG(0,t),ARG(1,t));
       case '=': return or(lessthan(ARG(0,t),ARG(1,t)),lessthan(ARG(1,t),ARG(0,t)));
       case NE : return equation(ARG(0,t),ARG(1,t));
     }
   return not(t);
 }
/*_______________________________________________________________*/
int addundefinedpoints(term t,term arg, term *next, char *reason)
/* add the points where f'(x) is undefined */

{ term x = get_eigenvariable();
  int nvariables, savenvariables;
  term problem,d,newpts,y,p,temp;
  char buffer[128];
  short saveit;
  int i,err;
  int success = 0;
  char yprime[32];
  unsigned short f = FUNCTOR(t);
  int currentline = get_currentline();
  savenvariables = nvariables = get_nvariables();
  if(!equals(t,history(currentline)))
     return 1;   /* this works at toplevel only */
  if(used2(ADDUNDEFINEDPOINTS))
     return 1;
  if(f != '=' && f != OR && f != FALSEFUNCTOR)
     return 1;   /* after you've tabulated values it's too late */
  if(used2(TABULATE) || used2(TABULATEEXACT))
     return 1;   /* the current line can be 'false' even after tabulating, if all values are rejected, as on
                    the problem 1/t on 0 < t < 1. */
  copy(THEPROBLEM,&problem);
         /* example,  \sqrt x + \sqrt (1-x) */
  y = ARG(0,history(0));
  saveit = get_nextassumption();
  set_nextassumption(-1);         /* signal not to make or use any assumptions */
  d = derivative(problem,x);
  if(contains(d,DIFF))
     return 1;
  newpts = codomain(d);  /* where d is undefined */
  set_nextassumption(saveit);
  if(saveit > 0)
     UNSET_ALREADY(newpts); /* so lpt can work on it below, using the assumptions */
  nvariables = get_nvariables();
  if(nvariables > savenvariables)  /* codomain might introduce new integer variables */
     { if(FUNCTOR(newpts) != '=' ||
          !equals(ARG(0,newpts),x) ||
          contains(ARG(1,newpts),FUNCTOR(x)) ||
          nvariables - savenvariables > 1
         )
          { errbuf(0, english(1044));
              /* Sorry, can't calculate those points. */
            return 1;
          }
       if(savenvariables > 1)
         /* switch varlist[nvariables-1] and varlist[1], because
            ep_aux wants the integer variable in varlist[1]  */
          { swapvars(nvariables-1,1);

            /* integer variables don't depend on anything so
               you don't have to worry about screwing up the
               dependency information in varinfo. The other variable
               can't depend on anything either:  it can only be a
               parameter of the problem.   */
          }
       err = ep_aux(ARG(1,newpts),&temp);
       if(err == 2)
          { errbuf(0,english(1043));
               /* Too many such points in the interval */
            return 1;
          }
       if(err)
          { errbuf(0, english(1044));
               /* Sorry, can't calculate those points */
            return 1;
          }
       newpts = temp;
     }
  else if(equals(newpts,falseterm))
     { if(get_mathmode() != AUTOMODE)

       /* if we generate this message in automode, it comes out more than once,
          as this operator is tried on different lines.  Rather than
          use another global variable to keep track of it, and update that
          variable properly in 'undo' and reset, we just forego that
          message in automode. */

          { errbuf(0, english(994));
               /* !This function is everywhere differentiable, */
            errbuf(1,english(992));
               /* !so we don't need to consider points */
            errbuf(2,english(993));
               /* !where the derivative is undefined. */
         }
       return 1;
     }

    /* Since we've assumed x is in the CLOSED minmax interval,
       and we've made no MORE assumptions (because nextline.c
       puts them into the current line as further disjuncts),
       we HAVEN'T assumed f'(x) is defined.  If the minmax interval
       is open, and f'(x) is undefined at an endpoint, this operator
       is going to generate that point too, which is fine.  */

  /* Now 'newpts'  contains points outside the closed interval as well
     as points in it where f'(x) is not defined.  It may also contain
     inequalities.   It would be harmless to enter points outside the
     interval as they would get rejected soon anyway.  But it's
     disastrous to enter inequalities there. */

  newpts = lpt(newpts);  /* this will use the assumptions, which are in force
                      again after nextassumption = saveit */
  if(equals(newpts,falseterm))
     { if(get_mathmode() != AUTOMODE)
          { strcpy(buffer,english(995));
               /* !This function is differentiable, */
            strcat(buffer,english(996));
               /* on the whole interval, */
            strcat(buffer,english(992));
               /* so we don't need to consider points */
            strcat(buffer,english(993));
               /* where the derivative is undefined. */
          }
      return 1;
    }
  if(FUNCTOR(newpts) != '=' && FUNCTOR(newpts) != OR)
     { strcpy(buffer, english(997));
          /* !Can't simplify the expression for the */
       strcat(buffer, english(998));
          /* points where f' is undefined. */
       strcat(buffer, english(1032));
          /* Answer cannot be guaranteed correct */
       errbuf(0,buffer);
       return 1;
     }
  if(FUNCTOR(newpts) == '=' && equals(ARG(1,newpts),x))
     newpts = equation(ARG(1,newpts),ARG(0,newpts));
     /* report x = 0, not 0 = x */
  HIGHLIGHT(newpts);
  if(FUNCTOR(newpts) == OR)
     { term u;
       for(i=0;i<ARITY(newpts);i++)
          { u = ARG(i,newpts);
            if(FUNCTOR(u) == '=' && equals(ARG(1,u),x))
               ARGREP(newpts,i,equation(ARG(1,u),ARG(0,u)));
            HIGHLIGHT(ARG(i,newpts));
          }
     }
  if(ISATOM(y))
     { strcpy(yprime,atom_string(y));
       strcat(yprime,"\'");
     }
  else
     { functor_string(FUNCTOR(y),SCREEN,yprime);
       strcat(yprime,"\'");
       strcat(yprime,"(");
       strcat(yprime,atom_string(x));
       strcat(yprime,")");
     }
  strcpy(reason, english(999)); /* add points where     */
  strcat(reason,"$");
  strcat(reason,yprime);
  strcat(reason,"$");
  strcat(reason, english(1000));  /*   is undefined */
  if(currentline==0)
     { *next = newpts;
       return 0;
     }
  /* combine new and t */
  if(currentline == 0 || equals(t,falseterm))
     { *next = newpts;
        return 0;
     }
  if(FUNCTOR(t)== '=' && FUNCTOR(newpts) == '=')
     { *next = or(t,newpts);
       success = 1;
     }
  else if(FUNCTOR(t) == '=' && FUNCTOR(newpts) == OR)
     { *next = make_term(OR,(unsigned short)(ARITY(newpts)+1));
       for(i=0;i<ARITY(newpts);i++)
          { ARGREP(*next,i,ARG(i,newpts));
          }
       ARGREP(*next,ARITY(newpts),t);
       success = 1;
     }
  else if(FUNCTOR(t) == OR && FUNCTOR(newpts) == '=')
     { *next = make_term(OR,(unsigned short)(ARITY(t)+1));
       ARGREP(*next,0,newpts);
       for(i=0;i<ARITY(t);i++)
          { ARGREP(*next,i+1,ARG(i,t));
          }
       success = 1;
     }
  if(FUNCTOR(t) == OR && FUNCTOR(newpts) == OR)
     { *next = topflatten(or(newpts,t));
       success = 1;
     }
  if(!success)
     return 1;
  if(FUNCTOR(*next) != OR)
     return 0;
  p = *next;
  remove_dups(p,next);
  remove_dups(t,&p);
  if(FUNCTOR(*next) != OR || FUNCTOR(p) != OR || ARITY(p) != ARITY(*next))
     return 0;
  /* All the new points were already in the list */
  errbuf(0, english(1036));
          /* All points in the interval where f'(x)=0 */
  errbuf(1, english(1037));
          /* are already listed. */
  return 1;
}

/*_______________________________________________________________*/
int addlimits(term t,term arg, term *next, char *reason)
/* This operation can only be used AFTER tabulating
values.  It replaces any undefined values by a limit term. */

{ term x = get_eigenvariable();
  term problem;
  term a,b,yvalue,yvalue2,xvalue,row,newrow;
  term interval = get_minmax_interval();
  int i;
  unsigned short n,k;
  int splitflag;
  int flag = 0;
  int currentline = get_currentline();
  if(!equals(t,history(currentline)))
     return 1;   /* this works at toplevel only */
  if(used2(ADDLIMITS))
     return 1;
  copy(THEPROBLEM,&problem);
  if(FUNCTOR(t) != MATRIX)
    { errbuf(0, english(1001));
         /* You must tabulate values first */
      return 1;
    }
  n = ARITY(t);  /* number of rows */
  assert(n < 0x8000);
  *next = make_term(MATRIX,(unsigned short)(2*n));  /* leave room for 'split rows' at interior
                                 points where y is undefined */
  for(i=k=0;i<n;i++)
    { row = ARG(i,t);
      splitflag = 0;
      newrow = make_term(VECTOR,ARITY(row));
      yvalue = ARG(1,ARG(1,row));
      if(NOTDEFINED(yvalue))
         { xvalue = ARG(1,ARG(0,row));
           if(equals(xvalue,infinity))
               yvalue = limit(arrow(x,infinity),problem);
           else if (equals(xvalue,minusinfinity))
               yvalue = limit(arrow(x,minusinfinity),problem);
           else if(FUNCTOR(interval)==AND)
              { a = ARG(0,ARG(0,interval));
                b = ARG(1,ARG(1,interval));
                if(equals(xvalue,a))
                     /* left endpoint */
                      yvalue = limit3(arrow(x,a),right,problem);
                else if(equals(xvalue,b))
                    /*  right endpoint */
                      yvalue = limit3(arrow(x,b),left,problem);
                else goto interior;  /* it's an interior point */
              }
           else if(equals(xvalue,ARG(0,interval))) /* left endpoint */
                yvalue = limit3(arrow(x,xvalue),right,problem);
           else if(equals(xvalue,ARG(1,interval))) /* right endpoint */
                yvalue = limit3(arrow(x,xvalue),left,problem);
           else /* an interior point, as when problem = x/(x-1) on whole line */
              { /* the point has to split into two entries */
                interior:   /* from 8 lines above */
                splitflag=1;
                yvalue = limit3(arrow(x,xvalue),left,problem);
                yvalue2 = limit3(arrow(x,xvalue),right,problem);
              }
           ARGREP(newrow,0,ARG(0,row));
           SETCOLOR(yvalue,YELLOW);
           ARGREP(newrow,1,arrow(ARG(0,ARG(1,row)),yvalue));
           ARGREP(*next,k,newrow);
           ++k;
           if(splitflag)  /* make and insert an extra row */
              { newrow = make_term(VECTOR,ARITY(row));
                ARGREP(newrow,0,ARG(0,row));
                SETCOLOR(yvalue2,YELLOW);
                ARGREP(newrow,1,arrow(ARG(0,ARG(1,row)),yvalue2));
                ARGREP(*next,k,newrow);
                ++k;
              }
           ++flag;
         }
      else
         { ARGREP(*next,k,row);
           ++k;
         }
    }
  if(flag == 0)
    { RELEASE(*next);
      return 1;
    }
  else
    SETFUNCTOR(*next,MATRIX,k);
  strcpy(reason, english(1002)); /* limits at open ends */
  return 0;
}
/*_______________________________________________________________*/
int rejectpoint(term t,term arg, term *next, char *reason)
/* reject point outside the closure of  *minmax_intervalp.  */

{ int i,err,k;
  term temp,u;
  unsigned short n,count=0;
  int *flags;
  int currentline = get_currentline();
  term interval = get_minmax_interval();
  term x = get_eigenvariable();
  if(!equals(t,history(currentline)))
      return 1;   /* this works at toplevel only */
   /* don't allow rejection of open endpoints, even if they are the only 
      points listed now, as there might still be other points, e.g. points where f' is not defined,
      that have to be considered. */
  interval = closure(interval);  
  if(FUNCTOR(t) == '='  && equals(ARG(0,t),x) && !depends(ARG(1,t),x))
    { subst(ARG(1,t),x,interval,&temp);
      err = refute(temp);
      if(err)
         return 1;
      nosolution:
      *next = falseterm;
      commentbuf(0,  english(1003));
         /* No solution in the specified interval. */
      goto out;
    }
  if(FUNCTOR(t) != OR && FUNCTOR(t) != MATRIX)
     return 1;
  n = ARITY(t);
  flags = callocate(n,sizeof(int));
  if(flags == NULL)
     { nospace();
       return 1;
     }
  for(i=0;i<n;i++)
    { u = ARG(i,t);
      if(FUNCTOR(u) == VECTOR)  /* t was a MATRIX */
          u = ARG(0,u);
      if(!equals(ARG(0,u),x) || depends(ARG(1,u),x))
          continue;
      subst(ARG(1,u),x,interval,&temp);
      err = refute(temp);
      if(err)
         continue;
      flags[i] = 1;
      ++count;
    }
  if(count == n)
    goto nosolution;
  if(count == n-1)
    { /* only one remaining solution */
      for(i=0;i<n;i++)
        { if(flags[i]==0)
             break;
        }
      assert(i<n);
      if(FUNCTOR(t) == OR)
         *next = ARG(i,t);
      else
         { *next = make_term(MATRIX,1);
           ARGREP(*next,0,ARG(i,t));
         }
      goto out;
    }
  if(count == 0)
     { /* no values were rejected */
       /* Maybe the user is trying to reject an endpoint before tabulating. */
       errbuf(0, english(2244));
       errbuf(1, english(1039));
       errbuf(2, english(1040));
              /*  Perhaps you are trying to reject an endpoint.
              If the min or max occurs at an endpoint, it will
              be a mistake to reject endpoints too soon. */
       return 1;
     }
  *next = make_term(FUNCTOR(t),(unsigned short)(n-count));
  k=0;
  for(i=0;i<n;i++)
    { if(flags[i] == 1)
         continue;
      ARGREP(*next,k,ARG(i,t));
      ++k;
    }
  assert(k==n-count);
  out:
  strcpy(reason, english(1004));
     /* drop values             outside interval */
  return 0;
}
/*_______________________________________________________________*/
int tabulate(term t,term arg, term *next, char *reason)
/* On the menus as "make table of decimal y-values".
The table has to be represented as a matrix term.
You can't use this operator prematurely.  If you do use it
without specifying an interval, it will set minmax_intervalp to
point to (a copy of) 'true'.
*/

{ int currentline = get_currentline();
  if(!equals(t,history(currentline)))
     return 1;  /* this can be applied only to the whole line */
  if(equals(t,falseterm))
     return 1;
  if(used2(TABULATE))
     return 1;  /* can't use this more than once */
  return tab_aux(t,0,next,reason);
}

/*_______________________________________________________________*/
int tabulateexact(term t,term arg, term *next, char *reason)
/* On the menus as "make a table of exact y-values".
The table has to be represented as a matrix term.
You can't use this operator prematurely.
*/
{ int currentline = get_currentline();
  if(!equals(t,history(currentline)))
     return 1;  /* this can be applied only to the whole line */
  if(equals(t,falseterm))
     return 1;
  if(used2(TABULATEEXACT))
     return 1;  /* can't use this more than once */
  return tab_aux(t,1,next,reason);
}
/*____________________________________________________________________*/
static int tab_aux(term t, int flag, term *next, char *reason)
/* if flag is 0, use decimal values; else use exact values;
prepare a matrix term representing a table of y-values;
make sure it's not done prematurely.  Return 0 for success,
1 for failure. */
{ int i,err;
  term x = get_eigenvariable();
  term row;
  unsigned short n;
  term problem;
  char buffer[128];
  if(!used2(ADDCRITICALPOINTS))
     return 1;  /* you won't even see this operation on the Term
                   Selection Menu until addcriticalpoints has been used. */
  if(!used2(ADDENDPOINTS))
      { errbuf(0, english(1005));
           /* You should add the endpoints */
        errbuf(1, english(1006));
           /* before you tabulate the values. */
        return 1;
      }
  if(!used2(ADDUNDEFINEDPOINTS))
      { err = addundefinedpoints(t,zero,next,reason);
        clear_error_buffer();
        if(!err)
           { errbuf(0, english(1007));
                /* Watch out for points where the */
             errbuf(1,english(1008));
                /* derivative is undefined!  You */
             errbuf(2, english(1009));
                /* shouldn't tabulate values yet. */
             return 1;
           }
      }
  copy(THEPROBLEM,&problem);
  if(FUNCTOR(t)=='=' || FUNCTOR(t)==ARROW)  /* a single equation */
      { if(equals(ARG(0,t),x) && !depends(ARG(1,t),x))  /* already solved */
             { err = tab(flag,t,problem,&row);  /* produces a VECTOR term */
               if(err)
                  return 1;
               *next = make_term(MATRIX,1);
               ARGREP(*next,0,row);
               goto out;
             }
        solvefirst:
        strcpy(buffer, english(1010));
           /* You must first solve the equation(s) for */
        strcat(reason,"$");
        strcat(buffer, atom_string(x));
        strcat(reason,"$");
        strcat(buffer, ".");
        errbuf(0,buffer);
        return 1;
      }
  /* Now there's more than one equation */
  assert(FUNCTOR(t)==OR);
  n = ARITY(t);
  for(i=0;i<n;i++)  /* check if all equations are solved already */
     { row = ARG(i,t);
       if(FUNCTOR(row) != '=' && FUNCTOR(row) != ARROW)
          return 1;   /* not assert(0);  this can happen if
                         the function is constant, so the
                         derivative = 0 equation simplifies to true.
                      */
       if(!equals(ARG(0,row),x) || depends(ARG(1,row),x))
          {  /* not already solved */
             goto solvefirst;
          }
     }
  /* OK, finally ready to tabulate */
  *next = make_term(MATRIX,n);
  for(i=0;i<n;i++)
     { err = tab(flag,ARG(i,t),problem,&row);
       if(err)
          return 1;
       ARGREP(*next,i,row);
     }
  out:
  strcpy(reason, english(1011));  /* tabulate values */
  maxflag = minflag = 0;   /* they may have been 1 from a previous problem */
  return 0;
}
/*_______________________________________________________________*/
static int tab(int flag,term t, term z, term *ans)
/*  t is a solved equation, e.g. x = 2.  Return a in *ans a VECTOR of dimension two,
whose first arg is t and whose second arg is an equation giving
the corresponding value of z (when the substitution t is made).
Use decimal values if flag is zero, else use exact values.
Return 0 for success, 1 for
failure due to unsolved equations or non-numerical values.  In case
of failure, write suitable messages to error_buffer. */
/* In case of constant functions, or functions defined by cases which
are constant on part of the interval, t can be an inequality.  In that
case, return in *ans the value of z when the midpoint of the interval
is substituted for the eigenvariable. */

{ term y =  ARG(0,history(0));
  /* the left side of the equation created by enhance_problem or entered by user */
  term x,right;
  double xval,yval,saveval;
  term temp,yofx;
  unsigned short f = FUNCTOR(t);
  if(equals(t,trueterm))
     { t = get_minmax_interval();
       f = FUNCTOR(t);
     }
  if(interval_as_and(t))
     { x = ARG(1,ARG(0,t));
       right = make_fraction(sum(ARG(0,ARG(0,t)),ARG(1,ARG(1,t))),two);
     }
  else if(f == '<' || f == '>' || f == LE || f == GE)
     { x = get_eigenvariable();
       if(equals(x,ARG(0,t)))
          right = ARG(1,t);
       else if(equals(x,ARG(1,t)))
          right = ARG(0,t);
       else
          return 1;
     }
  else /* f is '='  or ARROW*/
     { x = ARG(0,t);
       right = ARG(1,t);
     }
  if(!ISATOM(x))
     return 1;
  if(f == ARROW && NOTDEFINED(right)) /* right is infinity or minusinfinity */
     { yofx = undefined;
       goto out;
     }
  else if(!seminumerical(right))
     { char buffer[128];
       strcpy(buffer, english(1012));
          /* Can't tabulate at non-numerical values of  */
       strcat(buffer,"$");
       strcat(buffer, atom_string(x));
       strcat(buffer,"$");
       errbuf(0,buffer);
       return 1;
     }
  deval(right,&xval);
  saveval = VALUE(x);
  SETVALUE(x,xval);
  deval(z,&yval);
  SETVALUE(x,saveval);
  if(flag == 0)
     yofx = yval == BADVAL ? undefined : make_double(yval);
  else
    { if(yval == BADVAL)
         yofx = undefined;
      else
         { subst(right,x,z,&temp);
           polyval(temp,&yofx);
         }
    }
  out:
  *ans = make_term(VECTOR,2);
  ARGREP(*ans,0,t);
  ARGREP(*ans,1,equation(y,yofx));
  SETCOLOR(ARG(1,*ans),YELLOW);
  PROTECT(ARG(1,*ans));   /* in case it's y = undefined, we don't want
                             ssolve replacing it by 'false'; and we might
                             as well protect it in general as nothing can
                             be done to it anyway. */

  return 0;
}
/*_______________________________________________________________*/
int selectmax(term t,term arg, term *next, char *reason)
/* select the maxima from the list */
{ int i,err;
  int currentline = get_currentline();
  if(!equals(t,history(currentline)))
     return 1;  /* use this only at top level */
  if(FUNCTOR(t) != MATRIX)
     { errbuf(0, english(1001));
          /* You must tabulate values first. */
       return 1;
     }
  if(used2(SELECTMAX))
    return 1;
  i = used2(SELECTMIN);
  if(i>0)
     t = history(i-1);
  err = selectmax_aux(t,next,0);   /* leaves a message in error_buffer */
  if(err)
     return 1;
  if(ARITY(*next) == 1)
     strcpy(reason, english(1013));   /* select maximum */
  else
     strcpy(reason,  english(1014));  /* select maxima */
  SETCOLOR(*next,YELLOW);
  return 0;
}
/*_______________________________________________________________*/
int selectmin(term t,term arg, term *next, char *reason)
/* select the minima from the list */
{ int i,err;
  int currentline = get_currentline();
  if(!equals(t,history(currentline)))
     return 1;  /* use this only at top level */
  if(FUNCTOR(t) != MATRIX)
     { errbuf(0, english(1001));
          /* You must tabulate values first */
       return 1;
     }
  if(used2(SELECTMIN))
     return 1;
  i= used2(SELECTMAX);
  if(i>0)
     t = history(i-1);
  err = selectmax_aux(t,next,1);  /* leaves a message in error_buffer */
  if(err)
     return 1;
  if(ARITY(*next) == 1)
     strcpy(reason, english(1015));  /* select minimum */
  else
     strcpy(reason, english(1016));  /* select minima */
  SETCOLOR(*next,YELLOW);
  return 0;
}
/*_______________________________________________________________*/
static int selectmax_aux(term t, term *next, int flag)

/* flag = 0 means maxima, flag = 1 means minima.
   flag = 2 means eliminate the open endpoints.
   t is a MATRIX, because we've already tabulated values.
   Each row is a pair of equations, such as  x=1  y = 2;
   Eliminate the unwanted equations and return the rest in *next,
   or make *next =false, if none are left.
   It's possible to return *next the same as t.
   Return 0 for success, 1 for failure.
*/

{ int i,j,err;
  unsigned short k,n = ARITY(t);
  term row;
  term interval = get_minmax_interval();
  const char *nomaxmsg = english(1017); /* !No maximum on this interval */
  const char *nominmsg = english(1018); /* !No minimum on this interval */
#if 0
  // This message is no longer used.
  const char *nominmaxmsg = english(2230); /* !No minimum or maximum on this interval */
#endif
  int endflag = 0;
  double yval,min,max;
  int initflag = 0;  /* set when min and max have been initialized */
  term trialx, putativemax;
  term x = get_eigenvariable();
  term endpoints[2];  /* open endpoints */
  int navoid=0;       /* how many open endpoints to watch out for */
  if(FUNCTOR(t) != MATRIX)
     return 1;
  /* t is a matrix; the left column is filled with entries like x = sqrt(2);
     the right column is filled with entries like  y = 0.433  */
  if(n==1)
     return 1;     /* this only happens on constant functions */
  /* Now t is a matrix of several rows, each containing two equations
     of the form   x = 2     y = 0.433   */
  /* Or, if the function is piecewise constant, the first entry could be an inequality in x */
  /* First build endpoints[] to contain the open ends */
  if(FUNCTOR(interval)==AND)
       /* there are two endpoints */
     { if(FUNCTOR(ARG(0,interval))=='<')  /* open at the left */
         { endpoints[0] = ARG(0,ARG(0,interval));
           navoid = 1;
         }
       if(FUNCTOR(ARG(1,interval)) =='<') /* open at the right */
         { endpoints[navoid] = ARG(1,ARG(1,interval));
           ++navoid;
         }
     }
  else  if (FUNCTOR(interval) == '<')
      /* two endpoints, one of which is infinity or minusinfinity */
     { if(equals(ARG(0,interval),x))
         { endpoints[0] = minusinfinity;
           endpoints[1] = ARG(1,interval);
           navoid=2;
         }
       else if(equals(ARG(1,interval),x))
         { endpoints[0] = ARG(0,interval);
           endpoints[1] = infinity;
           navoid = 2;
         }
      }
  else if (FUNCTOR(interval) == LE)
       /* one endpoint which is infinity or minusinfinity */
      { if(equals(ARG(0,interval),x))
          { endpoints[0] = minusinfinity;
            navoid = 1;
          }
        else
          { endpoints[0] = infinity;
            navoid = 1;
          }
      }
  /* Now that we've prepared a list of open endpoints, proceed:  */

  for(i=0;i<n;i++)
     { row = ARG(i,t);
       assert(FUNCTOR(row) == VECTOR);
       putativemax = ARG(1,ARG(1,row));
       if(OBJECT(putativemax) && TYPE(putativemax)==DOUBLE)
           yval = DOUBLEDATA(putativemax);
       else if(equals(putativemax,undefined))
           { errbuf(0, english(1021));
                /* First consider the limits at the */
             errbuf(1, english(1022));
                /* endpoint(s) where you now have \'undefined\' */
             return 1;
           }
       else if(FUNCTOR(putativemax) == LIMIT)
           { errbuf(0, english(1023));
                /* You must first evaluate the limit */
             return 1;
           }
       else if(equals(putativemax,infinity))
           { if(flag == 0)
                goto nomax;
             else
                continue;
           }
       else if(equals(putativemax,minusinfinity))
           { if(flag == 1)
                 goto nomin;
             else
                 continue;
           }
       else
           { err = deval(putativemax,&yval);
             if(err)
                return 1;
           }
       if(initflag==0)
          { min = max = yval;
            initflag = 1;
          }
       else
          { if(yval >  max)
                max = yval;
            else if(yval < min)
                min = yval;
          }
     }
  *next = make_term(MATRIX,n);
  k=0;
  for(i=0;i<n;i++)
     { row = ARG(i,t);
       putativemax = ARG(1,ARG(1,row));
       trialx = ARG(1,ARG(0,row));
       if(OBJECT(putativemax) && TYPE(putativemax)==DOUBLE)
           yval = DOUBLEDATA(putativemax);
       else if(equals(putativemax,infinity))
          { if(flag==0)
               goto nomax;
            else 
               continue;
          }
       else if(equals(putativemax,minusinfinity))
          { if(flag==1)
                goto nomin;
            else
                continue;
          }
       else
          { err = deval(putativemax,&yval);
            if(err)
               continue;  /* infinities were caught above and sent to nomax or nomin */
          }
       if(yval == (flag ? min : max))
          { /* watch out for open endpoints */
            for(j=0;j<navoid;j++)
               { if(equals(endpoints[j],trialx))
                    { endflag = 1;
                       /* max is taken on at an open endpoint;
                          don't quit because the max might be taken
                          on somewhere else, too!  */
                      break;  /* out of the j-loop */
                    }
               }
            if(j<navoid)
               continue;  /* don't enter this row */
            copy(row,ARGPTR(*next)+k);
            /*  ARGREP(*next,k,row) causes a crash;
                I don't know exactly why.  */
            ++k;
          }
     }
  if(k==0)  /* max taken at open endpoint */
     { assert(endflag);  /* that's the only way there's no max now */
       if(flag)
          goto nomin;
       else
         goto nomax;
     }
  SETFUNCTOR(*next,MATRIX,k);
  return 0;

  nomin:
     if(!minflag)
	    { errbuf(0,nominmsg);
          minflag = 1;
        }
     return 1;
  nomax:
     if(!maxflag)
	    { errbuf(0,nomaxmsg);
          maxflag = 1;
        }
     return 1;
}
/*___________________________________________________________*/
int used2(int k)
/* return the line at which choice k on the maxima_and_minima menu has
already been used, 0 if not */
{ int i,currentline;
  controldata cd;
  operation *opseq;
  get_controldata(&cd);
  currentline = get_currentline();
  opseq = cd.opseq;
  for(i=1;i<=currentline; i++)
     { if (  opseq[i].men == minima_and_maxima &&
             (int) opseq[i].choice == k
          )
           { if(get_mathmode()== MENUMODE)
                { errbuf(0, english(1024));
                     /* You've already chosen that once. */
                  errbuf(1, english(1025));
                     /* You can't choose it again. */
                }
             /* don't write in error_buffer when in auto mode, because
                it overwrites "!No maximum on this interval" which is put
                there when selectmax fails.  */

             return i;
           }
     }
  return 0;
}
/*___________________________________________________________*/
#define MAXSPLIT 20

int eliminateparameter(term t,term arg, term *next, char *reason)
/* When t contains an equation x = u(n) where n is an integer
parameter introduced by solving an equation, this operator will
if possible find the values of n for which u(n) lies in
*minmax_intervalp, and replace the given equation by those finitely
many equations, if there are not more than MAXSPLIT of them; otherwise
if there lots of them, it will just enter an inequality on n as
an assumption.
   It is assumed there is only one integer parameter in history(currentline).
There might be others in the assumption list, though.  It assumes the parameter
is the first integer variable in varlist that is in t but is not the
eigenvariable.
*/

{  int i,j,err;
   unsigned short n,k;
   term x = get_eigenvariable();
   int nvariables = get_nvariables();
   term *varlist;
   varinf *varinfo;
   term right,temp,m;
   term interval = get_minmax_interval();
   if(nvariables < 2)
      return 1;   /* no parameter to eliminate */
   if(FUNCTOR(t) != '=' && FUNCTOR(t) != OR)
      return 1;
   varlist = get_varlist();
   varinfo = get_varinfo();
   for(i=1;i<nvariables;i++)
      { m = varlist[i];
        if(!contains(t,FUNCTOR(m)))
           continue;   /* m can be in the assumptions only */
        if(varinfo[i].type == INTEGER)
           break;
      }
   if(i==nvariables)
      return 1;   /* no parameter to eliminate */
   if( FUNCTOR(interval) == 0 ||
       ( FUNCTOR(interval)==AND &&  /* interval is whole real axis */
         contains(ARG(0,interval),INFINITYFUNCTOR) &&
         contains(ARG(1,interval),INFINITYFUNCTOR)
       )
     )
      { errbuf(0, english(1026));
           /* You must specify an interval first. */
        return 1;
      }
   if(FUNCTOR(t) != OR && !seminumerical(ARG(1,t)))
      { if(!equals(ARG(0,t),x) || depends(ARG(1,t),x)
          )
           { errbuf(0, english(1027));
                /* First solve the equation. */
             return 1;
           }
        err = ep_aux(ARG(1,t),next);
        if(err==2)
           goto toomany;
        if(err)
           return 1;
        goto out;
      }
   if(FUNCTOR(t) != OR)
      return 1;
   n = ARITY(t);
   *next = make_term(OR,(unsigned short)(n+MAXSPLIT));
   k=0;
   for(i=0;i<n;i++)
      { assert(FUNCTOR(ARG(i,t))== '=');
        right = ARG(1,ARG(i,t));
        if(seminumerical(right))
           { if(k > n+MAXSPLIT)
               goto toomany;
             ARGREP(*next,k,ARG(i,t));
             k++;
             continue;
           }
        err = ep_aux(right,&temp);
        if(err)
           { RELEASE(*next);
             if(err==2)
                goto toomany;
             return 1;
           }
        if(FUNCTOR(temp) != OR)
           { if(k > n+MAXSPLIT)
                goto toomany;
             HIGHLIGHT(temp);
             ARGREP(*next,k,temp);
             k++;
             continue;
           }
        for(j=0;j<ARITY(temp);j++,k++)
           { if(k > n+MAXSPLIT)
                goto toomany;
             HIGHLIGHT(ARG(j,temp));
             ARGREP(*next,k,ARG(j,temp));
           }
      }
   SETFUNCTOR(*next,OR,k);
   strcpy(reason, english(1028)); /* Solve for  */
   strcat(reason,"$");
   strcat(reason,atom_string(varlist[1]));
   strcat(reason,"$");
   return 0;
   toomany:
   errbuf(0, english(1029));
      /* Too many solutions in the interval. */
   errbuf(1, english(1030));
      /* Next line would be very long. */
   return 1;
   out:
   strcpy(reason, english(1031)); /* Eliminate parameters */
   return 0;
}
/*_______________________________________________________________*/
static int ep_aux(term t, term *ans)
/* t contains n=varlist[1]; return in *ans an equation or
OR of equations not containing n, by substituting for n all
possible values that make t lie in *minmax_intervalp */
/* If this can be done return 0, or 2 if there are more than
MAXSPLIT answers; else return 1; */

{
  term a,b;
  term *varlist = get_varlist();
  term x = varlist[0];
  unsigned short f;
  term interval = get_minmax_interval();
  f = FUNCTOR(interval);
  if(f== AND)
     { a = ARG(0,ARG(0,interval));
       b = ARG(1,ARG(1,interval));
     }
  else if(f == '<' || f == LE)
     { if(equals(ARG(0,interval),x))
          { b = ARG(1,interval);
            a = minusinfinity;
          }
       else if(equals(ARG(1,interval),x))
          { a = ARG(0,interval);
            b = infinity;
          }
       else
          return 1;   /*  assert(0); */
     }
  else
     return 1;
  return eliminate(t,x,a,b,ans);
}

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