Sindbad~EG File Manager
/* 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 select_max_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
*/
#include <string.h>
#include <assert.h>
#include <math.h>
#define TRIGCALC_DLL
#include "globals.h"
#include "graphstr.h"
#include "document.h"
#include "ops.h"
#include "calc.h"
#include "checkarg.h"
#include "operator.h"
#include "prover.h"
#include "getprob.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 */
/* 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. */
MEXPORT_TRIGCALC int get_maxflag(void) { return maxflag;}
MEXPORT_TRIGCALC int get_minflag(void) { return minflag;}
/*_______________________________________________________________*/
/* 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;
}
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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);
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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,false)) /* 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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
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,false) /* 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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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�-�/2 < x < n�+�/2, but
because n is existential, the complement is x=k�+�/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,true))
return false;
if(equals(t,false))
return true;
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,&cancelled,&m)
)
{ if(ISATOM(m) && FRACTION(ARG(1,b)) &&
equals(ARG(1,ARG(1,b)),two) &&
equals(ARG(0,ARG(1,b)),pi)
)
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)
)
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);
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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, �x + �(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,false))
{ 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,false))
{ 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,false))
{ *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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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 = false;
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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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,false))
return 1;
if(used2(TABULATE))
return 1; /* can't use this more than once */
return tab_aux(t,0,next,reason);
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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,false))
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, oem_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,true))
{ 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, oem_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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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);
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;
}
/*_______________________________________________________________*/
MEXPORT_TRIGCALC 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 */
const char *nominmaxmsg = english(2230); /* !No minimum or maximum on this interval */
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 reflect 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;
}
if(navoid==2 && equals(endpoints[0],minusinfinity) && equals(endpoints[1],infinity))
{ nomaxmsg = english(1019); /* !No maximum */
nominmsg = english(1020); /* !No minimum */
}
}
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;
}
/*___________________________________________________________*/
MEXPORT_TRIGCALC 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
MEXPORT_TRIGCALC 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),INFINITY) &&
contains(ARG(1,interval),INFINITY)
)
)
{ 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,oem_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); */
}
return eliminate(t,x,a,b,ans);
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists