Sindbad~EG File Manager
/* compute singularities and extrema of a graph as doubles when given
symbolic expressions for the singularities and extrema */
/*
M. Beeson for Mathpert
Original date 8.22.90
last modified 8.28.98
9.3.07 added assert(0) in get_linear_limits
4.25.13 added return 1 at line 864 in get_limits.
5.5.13 changed search.h to stdlib.h
12.8.14 moved call to get_linear_limits above the swap in get_limits
9.3.17 initialized some variables to avoid warnings
12.11.23 added includes for interval_as_and
*/
#include <math.h>
#include <assert.h>
#include <stdlib.h> /* qsort */
#include "globals.h"
#include "graphstr.h"
#include "grapher.h"
#include "sing.h"
#include "deval.h"
#include "deriv.h"
#include "ssolve.h"
#include "eqn.h" /* solved */
#include "gsub.h"
#include "pvalaux.h" /* is_linear_in */
#include "prover.h" /* interval_as_and */
#include "polynoms.h"
#include "dcomplex.h" /* proot.h needs it */
#include "proot.h" /* polyrange_aux */
#include "order.h" /* ncs */
#include "display1.h"
#include "bigrect.h"
#include "lterm.h" /* interval_as_and */
static int cmp(const void *, const void *);
static int allsolutions(term, term, double, double, term *, int, double *, int, int *);
static int getexistentials(term, term, term *, int, int *);
static int get_ex_aux(term, term, term *, int, int, int *);
static int check_ineq(term p);
static int RemoveDoubleDups(double *x, int n);
static int get_limits(term s, term m, double xmin, double xmax, int *small, int *big);
static int get_linear_limits(term s, term m, double xmin, double xmax, int *small, int *big);
/*______________________________________________________________________*/
static int cmp(const void *x,const void *y) /* for use in qsort */
{ if (*(double * )x < *(double *)y)
return -1;
if (*(double *) x == *(double *)y)
return 0;
else
return 1;
}
/*________________________________________________________________________*/
int compute_jumps(graph *g)
/* Similar to compute_singularities, but uses g->jumplist and
puts the numerical results in g->jumps */
/* It returns a nonzero value to indicate an error.
1 means formula too complicated to compute numerical jumps.
2 means more than one parameter in singularities formula (see below).
Otherwise it returns zero, whether or not there were any singularities.
That can be discovered by examining g->nsingularities.
*/
{ int err;
term x;
x = g->independent_variable;
err = get_sing(g->xmin, g->xmax,x,g->jumplist,g->dimjumplist,g->jumprestrictions,
g->dimjumprestrictions, &(g->jumps),&(g->njumps));
if(err)
return err;
return 0;
}
/*________________________________________________________________________*/
int get_sing
(
double xmin, double xmax, /* range of x-values to be graphed */
term x, /* the independent variable */
term *slist, /* array of symbolic expressions such as (2n+1)pi/2 or x = 1 */
int dimslist, /* dimension of array slist */
term *restrictions, /* array of inequalities */
int dimrestrictions, /* dimension of that array */
double **ans, /* pointer to output array of x-values of singularities
This function allocates space for the output array,
if it succeeds (except if *m==0 on return, see below)
which is why it must be passed a pointer
and not just the array. (That space is freed
in 'draw') */
int *m /* dimension of *ans */
)
/* members of slist are evaluated using deval; any atoms which are
not either e, pi_term, or valid parameters will be treated as integer parameters
which can take any value. The members of the array *ans are to be all
values obtained by evaluating any expression in slist, for any integer value
of the atoms it contains (other than valid parameters), that lie in
the range between xmin and xmax inclusive. The array *ans is to be
sorted in increasing order. */
/* The array "restrictions" may contain inequalities, all of which
have to be satisfied for each value of the integer parameters that is
used in generating *ans. */
/* Return value 0 is success (even if there were no singularities in
the specified range);
1 is too complicated a formula, can't compute numerical singularities;
2 is too many parameters (more than 1--we can only deal with 1);
These are the 'nonparameters' actually; the ones determining
the singularities. There can be many parameters in the formula
being graphed; here we're talking about the n in ax = (2n+1) pi/2
when the function is tan (ax).
3 is too many singularities (more than 400)
*/
/* This is not a very easy problem if the dependence on the integer
parameters is arbitrary! */
/* Evaluate each member of slist, one at a time, using fetch_values,
to get the singularities it generates. Place the computed values
in successive portions of 'workspace'. When done, sort workspace,
allocate heap space for *ans, copy workspace to *ans. */
/* If *m is returned as zero then *ans is not allocated, i.e. is garbage
and should not be freed by the calling function. */
#define MAXSING 400 /* maximum number of singularities willing to graph */
{ double workspace[MAXSING];
int index = 0; /* next unused member of workspace */
int i,err,k,mm=0,q;
term u,temp;
void *savenode;
if(dimslist == 0)
return 0; /* nothing to do */
if(dimslist == 1 && equals(slist[0],undefined))
return 1; /* singularities could not be calculated */
for(i=0;i<dimslist;i++)
{ u = slist[i];
if(FUNCTOR(u) == '=')
{ if(ISATOM(ARG(0,u)) &&
FUNCTOR(ARG(0,u)) == FUNCTOR(x) &&
!contains(ARG(1,u),FUNCTOR(x))
)
{ u = ARG(1,u);
err = fetchvalues(u,x,xmin, xmax,restrictions,dimrestrictions,
workspace+index,MAXSING-index,&mm
);
}
else if(contains_existentials(u))
{ savenode = heapmax();
if(get_nparameters())
{ gsub(u,&temp); /* use the numerical values of parameters
in calculating the singularities; this
way we will introduce no new assumptions
e.g. that a parameter is nonzero etc.
For example in tan(ax) we don't want
to assume a != 0. */
u = temp;
}
if(FUNCTOR(u) != '=')
{ reset_heap(savenode);
return 1; /* too hard */
}
if(!contains(u,FUNCTOR(x)))
/* e.g. on tan(ax) when a has value 0,
u is 0 = (1/2)(2n+1)pi */
{ double z1,z2;
long kk;
deval(ARG(0,u),&z1);
deval(ARG(1,u),&z2);
if(z1 == BADVAL || z2 == BADVAL ||
(nearint(z1-z2,&kk) && kk==0)
)
{ reset_heap(savenode);
return 1;
}
/* No singularities for this value of a */
*m = 0;
reset_heap(savenode);
return 0;
}
if(!solved(u,x) && is_linear_in(u,x))
{ err = ssolve(u,x,&temp);
if(err)
temp = u;
/* Not assert(0), since it might pass is_linear_in
and still be unsolvable. That shouldn't happen
but I can't prove that it can't.
*/
}
else
{ err = 0;
temp = u;
}
if(!err)
{ u = ARG(1,temp);
if(FUNCTOR(u) == OR)
{ /* ssolve produced a system */
q = ARITY(u);
for(k=0;k<q;k++)
{ err = fetchvalues(ARG(k,u),x,xmin,xmax,restrictions,dimrestrictions,
workspace + index, MAXSING-index, &mm
);
if(err)
{ reset_heap(savenode);
return 1;
}
index += mm;
}
}
else
err = fetchvalues(u,x,xmin, xmax,restrictions,dimrestrictions,
workspace+index,MAXSING-index,&mm
);
if(err)
{ reset_heap(savenode);
return 1;
}
}
reset_heap(savenode); /* Now that the desired values are
safely on the stack in workspace */
}
else
err = allsolutions(u,x,xmin,xmax,restrictions,dimrestrictions,
workspace+index,MAXSING-index,&mm
);
}
else
err = fetchvalues(u,x,xmin, xmax,restrictions,dimrestrictions,
workspace+index,MAXSING-index,&mm
);
if(err)
return err;
index += mm;
}
if(index == 0) /* no singularities in-range */
{ *m = 0;
return 0;
}
qsort(workspace,index,sizeof(double),cmp);
index = RemoveDoubleDups(workspace,index);
*ans = (double *) callocate(index,sizeof(double));
if(*ans == 0L)
nospace();
for(i=0;i<index;i++)
(*ans)[i] = workspace[i];
*m = index;
return 0;
}
/*_____________________________________________________________________*/
/* first argument, term s, is either an equation f(x) = linear(n) or
a term linear(n) to be interpreted as x = linear(n), or a monotone
function of a linear function of n, e.g. ln(2n+1)pi/2). The values to
be 'fetched' are the solutions x of this equation, lying between
xmin and xmax and not closer than one pixel together, for positive
or negative integers n which satisfy the inequalities in 'restrictions'.
There cannot be more than one integer parameter in the expression s.
It will work with more general s, provided
(1) There is just one solution x(n) of s (or of x=s if s isn't an
equation) for each value of n;
(2) The solutions x(n) depend monotonically on n so that as n is
increased or decreased, they will bracket the interval [xmin,xmax].
(3) If s is an equation, the solutions x(n)
also can't overshoot too far: they must hit the interval
[xmin- 100(xmax-xmin), xmax + 100(xmax-xmin)].
If we want it to work with e.g. s of the form 1/x = npi,
we bring s to the form au+b=0 where u contains x but not n,
and a and b contain n but not x, and where x can be found explicitly
from u. Then we solve for u as above and then get x. This
is not yet implemented.
*/
int fetchvalues(term s, // exported because called in defint.c to break up integral(abs f,x,a,b)
term x, /* the independent variable */
double xmin, double xmax,
term *restrictions,int dimrestrictions, /* as above */
double *scratch, /* already allocated workspace */
int dimscratch, /* dimension of workspace available */
int *m /* return the number of values calculated */
)
/* Determine the integer parameter in s if any;
determine appropriate range of the parameters;
use 'solve' to solve the equation for values of the parameters which
satisfy all inequalities in the array 'restrictions';
and place the computed values in the first part of array 'scratch',
returning the number of computed values in *m. */
/* Return value 0 is success,
1 is too complicated a formula (can't compute singularities)
2 is too many parameters,
3 is too many singularities
*/
/* Can change the value of the independent variable. (This is ok
since the grapher is going to initialize it anyway.) */
{ term nonparams[12];
int nparams; /* how many integer parameters are there in s? */
int err;
int n=0,mm=0,k; /* values of the parameter */
double y,z;
long kk;
term a,b;
int xflag; /* set if the restrictions involve the independent variable */
int i,j;
err = getexistentials(s,x,nonparams,12,&nparams);
if(err)
return err;
if(nparams==0) /* no existential-parameter dependence, e.g. s is pi/2 or x = pi_term/2 */
{ err = check_restrictions(restrictions,dimrestrictions);
/* e.g. if y = x^n we have a restriction n < 0 even though there
are no existential parameters */
if(err) /* for current parameter values there are no singularities */
{ *m = 0;
return 0;
}
if(FUNCTOR(s) == '=')
{ err = solve(s,x,xmin,xmax,scratch);
if(err)
{ *m = 0;
return 0; /* ok if there's no solution */
}
*m = 1;
return 0;
}
/* else just evaluate s */
if(deval(s,scratch))
return 1; /* s undefined */
if(scratch[0] < xmin || scratch[0] > xmax )
{ *m = 0;
return 0;
}
*m = 1;
return 0;
}
if(nparams > 1)
return 2; /* can only deal with one parameter */
/* Do the restrictions mention x? */
for(i=0;i<dimrestrictions;i++)
{ if(contains(restrictions[i],FUNCTOR(x)))
break;
}
xflag = i < dimrestrictions ? 1 : 0;
if(FUNCTOR(s) != '=') /* e.g. s is (2n+1)pi or pi/n */
{ /* first we must get a range of values of n that bracket the
possible solutions. Possibly such a range is already given
in the 'restrictions'; this will be the case when graphing
an indexed sum. Check for this first. */
if(xmax <= 0.0 && (FUNCTOR(s) == LN || FUNCTOR(s) == LOG || FUNCTOR(s) == SQRT))
{ *m = 0;
return 0;
}
for(i=0;i<dimrestrictions;i++)
{ if(interval_as_and(restrictions[i]) && equals(nonparams[0],ARG(1,ARG(0,restrictions[i]))))
break;
}
if(i < dimrestrictions)
{ a = ARG(0,ARG(0,restrictions[i]));
b = ARG(1,ARG(1,restrictions[i]));
deval(a,&y);
deval(b,&z);
if(y==BADVAL || z == BADVAL)
return 1;
if(nearint(y,&kk))
n = (int) kk;
else if(y > 0.0)
n = (int) floor(y) + 1;
else if(y < 0.0)
n = - ((int) floor(-y) + 1);
if(nearint(z,&kk))
mm = (int) kk;
else if(z > 0.0)
mm = (int) floor(y) + 1;
else if(z < 0.0)
mm = - ((int) floor(-y) + 1);
/* Now we have our lower and upper bounds */
}
else
{ err = get_limits(s,nonparams[0],xmin,xmax,&n,&mm);
if(err)
return 1;
}
/* Now n and mm are the limits that make s too small and too large */
if(mm < n)
/* Then swap mm and n so n <= mm */
{ int temp = mm;
mm = n;
n = temp;
}
j = 0;
for(k=n; k <= mm && j < dimscratch; k++)
{ SETVALUE(nonparams[0],k);
if(xflag)
{ double z;
err=deval(s,&z);
if(err)
return 1;
SETVALUE(x,z);
}
if( check_restrictions(restrictions,dimrestrictions))
; /* k is an unusable parameter value, do nothing */
else
{ if(deval(s,&y)) /* value of s for this value of k */
continue; /* e.g. sqrt((2n+1) pi/2) for a negative value of 2n+1 */
/* This means it's not really a singularity */
if( xmin <= y && y <= xmax)
{ scratch[j] = y; /* record the value of s */
j++; /* and get ready to record the next value of s */
}
}
}
if( j >= dimscratch)
return 2;
*m = j;
return 0;
}
if(FUNCTOR(s) == '=')
{ double lo,hi;
lo = xmin - 100*(xmax-xmin);
hi = xmax + 100*(xmax-xmin);
y = xmax;
for( i=0; xmin <= y && i < 308 ; i++)
{ n = (int) pow(10.0,(double) i);
SETVALUE(nonparams[0], n);
err = solve(s,x,lo,hi,&y);
if(!err && y < xmin)
break; /* got the desired solution */
n = -n;
SETVALUE(nonparams[0],n);
err = solve(s,x,lo,hi,&y);
if(!err && y < xmin)
break; /* got the desired solution */
}
if(i==308)
return 1; /* can't bracket the interval */
for( i=0; xmax >= y && i < 308; i++)
{ mm = (int) pow(10.0,(double) i);
SETVALUE(nonparams[0],mm);
err = solve(s,x,lo,hi,&y);
if(!err && y > xmax)
break;
mm = -mm;
SETVALUE(nonparams[0],mm);
solve(s,x,lo,hi,&y);
if(!err && y > xmax)
break;
}
if(i==308)
return 1; /* can't bracket the interval */
/* Now n and mm are the limits that make s too small and too large */
j=0;
for(k=n;k <= mm && j < dimscratch;k++)
{ SETVALUE(nonparams[0],k);
if(xflag)
{ if(ISATOM(ARG(0,s)) && FUNCTOR(ARG(0,s)) == FUNCTOR(x))
{ double z;
err=deval(ARG(1,s),&z);
if(err)
return 1;
SETVALUE(x,z);
}
else
return 1;
}
if( check_restrictions(restrictions,dimrestrictions))
; /* k is an unusable parameter value, do nothing */
else
{ err = solve(s,x,xmin,xmax,&y); /* value of s for this value of k */
if( !err ) /* then xmin <= y <= xmax is automatic */
{ scratch[j] = y; /* record the value of s */
j++; /* and get ready to record the next value of s */
}
}
}
if( j >= dimscratch)
return 2;
*m = j;
return 0;
}
return 0; /* can't get here, but it makes Turbo C happy */
}
/*___________________________________________________________________________*/
int check_restrictions(term *restrictions, int dimrestrictions)
/* restrictions is an array of inequalities of dimension dimrestrictions */
/* return 0 if all of them are true with the current values of variables */
/* otherwise return 1 */
{ int i,err;
unsigned f;
term p;
for(i=0;i<dimrestrictions;i++)
{ p = restrictions[i];
if(interval_as_and(p))
{ err = check_ineq(ARG(0,p));
if(err)
return 1;
err = check_ineq(ARG(1,p));
if(err)
return 1 ;
continue;
}
f = FUNCTOR(restrictions[i]);
if(!INEQUALITY(f))
assert(0);
err = check_ineq(p);
if(err)
return 1;
}
return 0;
}
/*____________________________________________________________*/
static int check_ineq(term p)
/* p is an inequality. Use deval to check it numerically. If it's
true, return 0; else return false.
*/
{ term u = sum(ARG(1,p),tnegate(ARG(0,p)));
double y;
long k;
unsigned short f = FUNCTOR(p);
int err = deval(u,&y);
if(err)
return 1;
if(nearint(y,&k)) /* correct for roundoff error */
y = k;
switch(f)
{ case '<' :
if(y <= 0.0)
return 1;
break;
case '>' :
if(y >= 0.0)
return 1;
break;
case NE:
if(y == 0.0)
return 1;
break;
case LE:
if(y < 0.0)
return 1;
break;
case GE:
if(y > 0.0)
return 1;
break;
case '=' :
if(y != 0.0)
return 1;
break;
default:
return 1;
}
return 0;
}
/*___________________________________________________________________________*/
static int getexistentials(term s, term x, term *ans, int dimans, int *k)
/* x is the independent variable */
/* get all atoms in s which are existential integer variables
(introduced by the prover), and return them in *ans. Return
the number of them in *k. If there are more than dimans of them,
don't overwrite the *ans array (which has dimension dimans), but
just return 1. Otherwise return 0 for success. */
{ return get_ex_aux(s,x,ans,dimans,0,k);
}
/*___________________________________________________________________________*/
static int get_ex_aux(term s, term x, term *ans, int dimans, int j,int *k)
/* x is the independent variable */
/* get all atoms in s which are existential integer variables
(introduced by the prover), and are not among ans[0]... ans[j-1];
Write them to ans[j], ans[j+],..., but do not overwrite the end
of the *ans array, which has dimension dimans.
Increment *k for each one added. Return 1 if we run out of space in
the ans array, 0 otherwise.
*/
{ int i,err;
int n;
if(j >= dimans)
return 1;
if(OBJECT(s))
{ *k = j;
return 0;
}
if(ISATOM(s))
{ if (!ISEXISTENTIALVAR(s))
{ *k = j;
return 0;
}
for(i=0; i<j; i++)
{ if (FUNCTOR(s) == FUNCTOR(ans[i]))
{ *k = j;
return 0;
}
}
ans[j] = s;
*k = j+1;
return 0;
}
/* Now s is a compound term */
n = ARITY(s);
for(i=0;i<n;i++)
{ err = get_ex_aux(ARG(i,s),x,ans,dimans,j,k);
if(err)
return err;
if(*k >=dimans)
return 2;
if(*k != j)
j = *k;
}
return 0;
}
/*__________________________________________________________________*/
#define GRID 200
static int allsolutions(term s,
term x, /* the independent variable */
double xmin, double xmax,
term *restrictions,int dimrestrictions, /* as above */
double *scratch, /* already allocated workspace */
int dimscratch, /* dimension of workspace available */
int *m /* return the number of values calculated */
)
/* s is an equation in x. Find all solutions of s=0 between xmin and xmax
such that the inequalities in the restrictions array are satisfied.
Put the answers in the 'scratch' array, and return the number of values
calculated in *m. If the answers won't all fit, return 1.
Return 0 for success.
It is presumed that s does not contain EXISTENTIAL integer variables;
it may contain ordinary parameters, which will be treated as numbers
(using their current values).
The method is to divide the interval into GRID equal subintervals,
compute the term s at each division point, and if a change of sign
is noticed, call solve to find one solution in that subinterval.
If there are two solutions in a subinterval we will miss one, so
GRID should be pretty large. Also, if there are subintervals that
actually contain solutions, but on which there's no sign change, we
will also miss those solutions.
If on one of the subinterval, s is small at both endpoints (less than 0.2)
then we compute the derivative at the endpoints; if it changes sign
then we solve for a zero of the derivative in the interval and compute
s there.
*/
{ int i,next,err;
term deriv,u,v;
double bderiv,aderiv,ww,zz;
int derivflag=0;
double z,y,a,b,gap,saveit;
err = check_restrictions(restrictions,dimrestrictions);
/* e.g. if y = x^n, we have a restriction n < 0 even though there
are no existential parameters */
if(err) /* for current parameter values there are no singularities */
{ *m = 0;
return 0;
}
if(FUNCTOR(s) == '=')
{ if(ZERO(ARG(1,s)))
s = ARG(0,s);
else if(ZERO(ARG(0,s)))
s = ARG(1,s);
else
{ u = make_term('-',1);
ARGREP(u,0,ARG(1,s));
v = make_term('+',2);
ARGREP(v,0,ARG(0,s));
ARGREP(v,1,u);
s = v;
/* equivalent to s = sum(ARG(0,s), strongnegate(ARG(1,s)));
but without using strongnegate and sum */
}
}
if(FUNCTOR(s) == ABSFUNCTOR)
s = ARG(0,s);
gap = (xmax - xmin)/GRID;
z = xmin;
next=0;
saveit = VALUE(x);
SETVALUE(x,xmin);
err = deval(s,&a);
if(err)
return 1;
for(i=1;i<GRID;i++)
{ y = z;
z += gap;
SETVALUE(x,z);
err = deval(s,&b);
if(err)
return 1;
if( a*b < 0.0) /* sign change */
{ err = solve(s,x,y,z,scratch+next);
if(err)
return 1; /* e.g. if s itself has singularities */
++next;
if(next > dimscratch)
return 1;
}
else if(fabs(a) < 0.2 || fabs(b) < 0.2) /* possible zero at minimum of function */
{ if(contains(s,DIFF) || contains(s,INTEGRAL) || contains(s,ABSFUNCTOR))
return 1;
if(!derivflag)
{ deriv = derivative(s,x);
derivflag = 1; /* don't compute derivative more than once */
}
/* Compute the derivative at a and at b; if the signs are
different, solve for the extremum in between a and b */
err = deval(deriv,&bderiv);
if(err)
return 1;
SETVALUE(x,z-gap);
err = deval(deriv,&aderiv);
if(err)
return 1;
if(aderiv *bderiv < 0)
{ err = solve(deriv,x,y,z,&ww);
if(err)
return 1;
SETVALUE(x,ww);
deval(s,&zz);
if(fabs(zz) < 1.0e-6) /* roundoff error can get this big */
{ scratch[next] = ww;
++next;
}
}
}
a = b;
}
SETVALUE(x,saveit); /* restore original value of x */
*m = next;
return 0;
}
#undef GRID
/*______________________________________________________*/
static int RemoveDoubleDups(double *x, int n)
/* at entrance, x is a sorted array of dimension n.
Remove duplicates and return the new dimension.
*/
{ int i,k=1;
for(i=1;i<n;i++)
{ if(x[i] != x[i-1])
{ x[k] = x[i];
++k;
}
}
return k;
}
/*___________________________________________________________________________*/
static int get_limits(term s, term m, double xmin, double xmax, int *small, int *big)
/* m is an integer variable, s is a term containing it. It is presumed that
s contains no other variables but m. Find values *small
and *big of m such that by evaluating s for all values of m between
*small and *big, we will get all values of s lying between xmin and xmax
(and possibly some extra values, that's OK).
If s is linear in m, this can be done by just trying larger and larger
values of m until we get outside [xmin,xmax] at both ends.
If s is a polynomial of even degree in m, with positive leading term,
we just need the largest and smallest solutions of u = xmax (and take the
greatest integer in the smallest, and 1 + the greatest integer in the largest).
If s is a polynomial of even degree but negative leading term, use xmin
instead of xmax.
If s is a polynomial of odd degree, take the smallest solution of
u = xmin and the largest solution of u = xmax (for positive leading term)
or (for negative leading term) the smallest solution of u = xmax and the
largest solution of u = xmin.
These are the only cases handled at present. Return 0 for success,
1 for failure.
*/
{ int err;
unsigned short degree;
double z,a,swap,minroot,maxroot,saveit;
double x1,x2;
term u1,u2;
double maxint,pp;
POLYnomial p,p1,p2;
unsigned short f = FUNCTOR(s);
term t;
err = polyform(s,m,&p);
if(!err && ARITY(p) == 1)
return 1;
if(err)
{ /* We can also handle cm^p where p is a rational number */
term n,c,ss;
if(f == '*')
{ ncs(s,&n,&c,&ss);
if(FUNCTOR(ss) == '^' && RATIONALP(ARG(1,ss)) &&
equals(ARG(0,ss),m) && ISODD(ARG(1,ARG(1,ss))) &&
ISODD(ARG(0,ARG(1,ss)))
)
/* solve am^zz = xmin to determine *small */
{ deval(make_power(make_fraction(make_double(xmax),product(n,c)),reciprocal(ARG(1,ss))),&pp);
if(pp == BADVAL)
return 1;
*big = (int) pp + 1;
deval(make_power(make_fraction(make_double(xmin),product(n,c)),reciprocal(ARG(1,ss))),&pp);
if(pp == BADVAL)
return 1;
*small = (int) pp -1;
return 0;
}
/* FINISH THIS--you could handle even rational powers too */
}
/* if s = f(t), where f is monotone increasing, then apply get_limits
recursively to t. */
if(f == LN || f == LOG || f == ATAN || f == SQRT)
/* example, graphing tan(e^x), the singularities are ln((2n+1) pi/2) */
{ t = ARG(0,s);
switch(f)
{ case LN:
u1 = make_power(eulere,make_double(xmin));
u2 = make_power(eulere,make_double(xmax));
/* assert(xmax > 0.0) because calling function checks for xmax <= 0.0 */
break;
case LOG:
u1 = make_power(ten,make_double(xmin));
u2 = make_power(ten,make_double(xmax));
break;
case ATAN:
u1 = tan1(make_double(xmin));
u2 = tan1(make_double(xmax));
break;
case SQRT:
u1 = make_power(make_double(xmin),two);
u2 = make_power(make_double(xmax),two);
break;
}
deval(u1,&x1);
if(x1 == BADVAL)
return 1;
deval(u2,&x2);
if(x2 == BADVAL)
return 1;
return get_limits(t,m,x1,x2,small,big);
}
if(f == LOGB || f == ROOT) /* FINISH THIS--handle powers too */
{ t = ARG(1,s);
switch(f)
{ case LOGB:
u1 = make_power(ARG(0,s),make_double(xmin));
u2 = make_power(ARG(0,s),make_double(xmax));
break;
case ROOT:
u1 = make_power(make_double(xmin),ARG(1,s));
u2 = make_power(make_double(xmax),ARG(1,s));
break;
}
deval(u1,&x1);
if(x1 == BADVAL)
return 1;
deval(u2,&x2);
if(x2 == BADVAL)
return 1;
return get_limits(t,m,x1,x2,small, big);
}
return 1; // because polyform failed.
}
degree = (unsigned short)(ARITY(p)-1);
deval(ARG(degree,p),&a); /* leading term */
if(a == BADVAL)
return 1;
if(degree == 1)
return get_linear_limits(s,m,xmin,xmax,small,big);
if(a < 0.0)
{ swap = xmin;
xmin = xmax;
xmax = swap;
p = polyneg(p);
a = -a;
}
if(!(degree & 1))
{ /* even degree */
deval(ARG(0,p),&z);
ARGREP(p,0,make_double(z-xmax));
err = polyrange_aux(p, &minroot, &maxroot);
if(err < 0)
return 1;
}
else
{ /* odd degree */
copy(p,&p1);
copy(p,&p2);
deval(ARG(0,p),&z);
ARGREP(p1,0,make_double(z-xmax));
err = polyrange_aux(p1,&minroot,&maxroot);
if(err < 0)
return 1;
saveit = maxroot;
ARGREP(p2,0,make_double(z-xmin));
err = polyrange_aux(p2,&minroot,&maxroot);
if(err < 0)
return 1;
maxroot = saveit;
}
/* minroot and/or maxroot may be too big to convert to an int.
We have to watch out for that. */
maxint = ((double)(unsigned)(-1))/2.0;
if(fabs(minroot) >= maxint)
return 1;
if(fabs(maxroot) >= maxint)
return 1;
*small = minroot < 0 ? -(int)(-minroot) -1 : (int)minroot;
*big = maxroot < 0 ? - (int)(-maxroot) : (int)maxroot + 1;
return 0;
}
/*_________________________________________________________________________*/
static int get_linear_limits(term s, term m, double xmin, double xmax, int *small, int *big)
/* Do the work of get_limits above, in case s is linear in m.
We just try larger and larger values till we find values of m that make s
outside the interval [xmin, xmax].
Presumes xmin <= xmax.
*/
{ int i;
double y,n=0,mm=0.0;
y = xmax;
if(xmin > xmax)
assert(0);
for( i=0; xmin <= y && i < 308 ; i++)
{ n = pow(10.0,(double) i);
SETVALUE(m,n);
if(deval(s,&y) || y == BADVAL)
return 1; /* s undefined */
if(y < xmin)
break;
n = -n;
SETVALUE(m,n);
if(deval(s,&y))
return 1; /* s undefined */
if(y < xmin)
break;
}
if(i==308)
return 1; /* can't bracket the interval */
for( i=0; xmax >= y && i < 308; i++)
{ mm = pow(10.0,(double) i);
SETVALUE(m,mm);
if(deval(s,&y))
return 1;
if(y > xmax)
break;
mm = -mm;
SETVALUE(m,mm);
if(deval(s,&y))
return 1;
if(y > xmax)
break;
}
if(i==308)
return 1; /* can't bracket the interval */
*small = (int) n;
*big = (int) mm;
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists