Sindbad~EG File Manager

Current Path : /home/beeson/cgraph/
Upload File :
Current File : //home/beeson/cgraph/graphrng.c

/* M. Beeson, for Mathpert.   Initialize graph ranges */
/* Original date 11.3.96 */
/* Last modified 8.30.98 */
/* 6.24.04 added sqrtflag and code that uses it,  as well as adjust  */
/* 12.11.05 modified at lines 439-440.
4.13.06 modified again at the same place.
12.4.14 modified adjust around line 470.
3.4.24 changed max to MAX,  deleted include cgraph.h and malloc.h
       deleted calls to get_aspect() as aspect ratio in WebMathXpert is always 1
5.31.24 removed multiple includes of polynoms.h
8.8.24  replaced math.h with sincos.h
*/

/* In general it is a recursively unsolvable problem to find, given
a formula for a function, a point where the function is defined.
Also, to determine whether a given equation has a solution or not
is recursively unsolvable, or to determine whether it has finitely
many or infinitely many solutions.  Therefore, the algorithms
in this file are doomed to fail on sufficiently complicated examples,
no matter what those algorithms may be.
*/


#include <assert.h>
#include <sincos.h>  // math.h except sincos
#include <string.h>
#include <stdlib.h>   /* gcvt */
#include "globals.h"
#include "graphstr.h"
#include "grapher.h"
#include "heap.h"
#include "dcomplex.h"
#include "mpmem.h"
#include "deval.h"
#include "sing.h"
#include "polynoms.h"
#include "proot.h"  /* polyroot2 */
#include "mpdoc.h"
#include "islinear.h"  /* is_linear_in */
#include "polyquo.h"   /* make_polyquo */
#include "cflags.h"    /* get_currenttopic */
#include "tdefn.h"
#include "pvalaux.h"
#include "periodic.h"
#include "nperiod.h"
#include "trig.h"      /* TRIGFUNCTOR */
#include "proot.h"     /* polyrange_aux */
#include "prover.h"    /* zeroes      */
#include "order.h"   /* numerical   */
#include "graphrng.h"

#define MAXDEG 40

static int torture_test = 0;   /* define this nonzero to test Mathpert's
                resistance to out-of-memory errors. */
static double next_power_of_two(double x);
static int defined_somewhere(term t, term x, double a, double b, double *y);
static void get_interval(double x, double y, double *a, double *b);
static int rattrig(term t, term x);
static double next_pi(double z);
static int get_zero(term t,term x,double *ans);
static int get_second_zero(term t,term x,double c, double *ans);
static int trig_subarg(term t, term x, term *arg);
static void set_ybounds(graph *g);

/*__________________________________________________________________________*/
static void adjust(int sqrtflag, term w, double *ymin, double *ymax)
/* sqrtflag is either SQRT, ROOT, or '^', and should be the functor of w.
ymin and ymax point to good min and max values for the graph of ARG(0,w) (in
case sqrtflag is SQRT or '^') and for the graph of ARG(1,w) (in case sqrtflag
is ROOT).  Adjust ymin and ymax appropriately to graph w instead.
*/
{ double power;
  int evenflag=0;
  if(sqrtflag == 0)
      return;  // should never be called this way
  if(sqrtflag == SQRT)
      { *ymax  = sqrt(MAXIMUM(*ymax,0.0));
        *ymin  = - floor(0.1 * *ymax);
        if(*ymin == 0.0)
           *ymin = -0.1;
        return;
      }
  if(sqrtflag == ROOT)
     { deval(ARG(0,w),&power);
       if(iseven(ARG(0,w)))
          evenflag = 1;
     }
  else if(sqrtflag == '^')
     { deval(ARG(1,w),&power);
       if(isinteger(ARG(1,w)))
          evenflag = iseven(ARG(1,w));
       else if(FRACTION(ARG(1,w)) && iseven(ARG(1,ARG(1,w))))
          evenflag = 1;
       else if(NEGATIVE(ARG(1,w)) && isinteger(ARG(0,ARG(1,w))))
          evenflag = iseven(ARG(0,ARG(1,w)));
       else if(NEGATIVE(ARG(1,w)) && FRACTION(ARG(0,ARG(1,w))) &&
               iseven(ARG(1,ARG(0,ARG(1,w))))
              )
          evenflag = 1;
     }
  else
     assert(0);
  power = 1.0/power;
  *ymax = pow(*ymax,power);
  if(evenflag)
     { *ymin = -floor(0.1* *ymax);
       if(*ymin == 0.0)
       *ymin = -0.1;
     }
  else
     *ymin = -*ymax;
}

/*__________________________________________________________________________*/
static void set_yrange(PGRAPHDATA p, term f, term x, term addthis, term savethis, double a, double b, double xmin, double xmax)
/* Called to finish init_graph_ranges.  It's assumed that p points to a valid
graph document, f to the function to be graphed, or the difference of two
sides of an equation;  x to the independent variable,
a < b are two values between xmin and xmax; determine the y-range using
values of f, addthis, and savethis on [a,b], and set them in the graph
structure, and also set the x-ranges to xmin and xmax.
   Addthis and savethis are used in case f is the difference of sides of an
equation, to be sure that we show both graphs; you can't set the y-range based
on the difference alone.  See init_graph_ranges for details about addthis and
savethis.
*/
{ double max,min,y1,y2,y,ymin,ymax;
  int i;
  max = b;
  min = a;
  if(max < 1)
     max = 1.0;
  if(min > -1.0)
     min = -1.0;
  /* The preceding lines ensures that the axes both show. */
  if(!ZERO(addthis))
     { defined_somewhere(addthis,x,min,max,&y1);
       defined_somewhere(savethis,x,min,max,&y2);
       if(y1 == y2)
          { /* example, abs(x-1) + aqs(x+1) = 2 */
            defined_somewhere(f,x,min,max,&y);
            if(fabs(y) < 1.0)
               { xmin = -2.0;
                 xmax = 2.0;
                 y = y1;
               }
          }
       else
          y = y1 > y2 ? y1 : y2;
     }
  else
     defined_somewhere(f,x,min,max,&y);
  ymax = next_power_of_two(y);
  ymin = -ymax;  /* The x-axis will be centered */
  for(i=0;i<p->ngraphs;i++)
     { p->graphs[i]->xmin = xmin;
       p->graphs[i]->xmax = xmax;
       p->graphs[i]->ymin = ymin;
       p->graphs[i]->ymax = ymax;
     }
}
/*________________________________________________________________________*/
static int get_singularities_range(PGRAPHDATA p, term x, double *xmin, double *xmax)
/* look up the pre-calculated singularities of the graphs in p,
if any, and compute the min and max of the singularities.  Presumes
that x is the independent variable.  If the singularities formulas
contain existential variables, it will only find some of them, as it just
uses deval to compute the min and max.  The existential variables will deval
to 1 probably.  Return the number of singularities found.
If it's zero, *xmin and *max can be garbage, otherwise they are the min and max
of the values of the singularities.  Singularities formulas that are not solved
for x are ignored.
*/
{ double min,max,z;
  int i,j,count = 0;
  term u;
  min = max = 0.0;
  for(i=0;i<p->ngraphs;i++)
     { if(p->graphs[i]->dimslist == 0)
          continue;
       for(j=0;j<p->graphs[i]->dimslist;j++)
          { u = p->graphs[i]->slist[j];
            if(FUNCTOR(u) == '=' && equals(ARG(0,u),x))
               { deval(ARG(1,u),&z);
                 /* Hopefully if the singularities contain existential
                    variables, deval evaluates them to 1  */
                 if(z == BADVAL)
                    continue;
                 ++count;
                 if(count == 0)
                    min = max = z;
                 else if(z < min)
                     min = z;
                 else if(z > max)
                     max = z;
               }
          }
     }
  *xmin = min;
  *xmax = max;
  return count;
}
/*_________________________________________________________________________*/
static int get_root_range(term roots, term x, double *xmin, double *xmax)
/* roots is a formula for the zeroes of some function, so it's an AND
of equations x = ...,  or a single such equation, or 'false', or some
more complicated formulas can appear in the list.  Ignore the more
complicated ones and 'false', deval the right-hand sides of the equations,
and return in *xmin and *xmax the min and max of the values thus calculated.
The return value is the number of terms evaluated.  If it is zero,
*xmin and *xmax are garbage.
*/
{ double z,min,max;
  int i,count;
  unsigned short n;
  term u;
  if(equals(roots,falseterm))
     return 0;
  if(FUNCTOR(roots) == '=' && equals(ARG(0,roots),x))
     { deval(ARG(1,roots),&z);
       if(z == BADVAL)
          return 0;
       *xmin = *xmax = z;
       return 1;
     }
  if(FUNCTOR(roots) != AND)
     return 0;
  n = ARITY(roots);
  count = 0;
  for(i=0;i<n;i++)
     { u = ARG(i,roots);
       if(FUNCTOR(u) != '=' || !equals(ARG(0,u),x))
          continue;
       deval(ARG(1,u),&z);
       if(z == BADVAL)
          continue;
       if(count == 0)
          min = max = z;
       else
          { if(z > max)
               max = z;
            if(z < min)
               min = z;
          }
       ++count;
     }
  if(count == 0)
     return 0;
  *xmin = min;
  *xmax = max;
  return count;
}
/*_________________________________________________________________________*/

void init_graph_ranges(PGRAPHDATA p)
/* p is a graph document, probably created by the use of the Graph Button.
If the function is an inequality, replace it by the difference of the sides.

   (a) if the function is linear or polynomial, be sure all zeroes are showing.
   (b) if the function can be simplified to a quotient of polynomials,
       be sure all zeroes of the denom and num are showing.
   (c) Otherwise search for a point where the function is defined and
       then use Newton's method to try to find a solution, and choose a range
       symmetric about the origin that includes that solution.
   (d) if the equation contains trig functions of x or rational multiples of x,
       make the final x-range of the form -2npi to 2npi.
   (e) If a solution cannot be found by these methods then do nothing.

If the problemtype of p->brotherdoc is SIMPLIFY, just search for a place
where the function is defined, and take a range 2^-n to 2^n including that
place, or if the function contains trig functions as in (c), a range from
-2npi to 2npi.
*/

{ int ngraphs,nroots,nsing,err,savefactorflag,savegcdflag;
  double xmin,xmax,ymin,ymax,width,a,b,dperiod,savexmax;
  term temp,roots,period;
  int sqrtflag = 0;
  void  *savenode;
  term x,f,w,q,addthis,savethis,u,v,poly,savearg;
  double min,max,y,z,y1,y2,z1,z2,minsing,maxsing,minroot,maxroot;
  POLYnomial numpoly, denompoly;
  int i,count,heapflag,nvars;
  int currenttopic = get_currenttopic();
  term *atomlist;
  unsigned short g = FUNCTOR(p->DocControlData.minmax_interval);
  term interval;
  graph *savegraphpointer;
  ngraphs = p->ngraphs;
  x = p->graphs[0]->independent_variable;
  if(g != 0)
     { /* it's a MINMAX problem */
       interval = p->DocControlData.minmax_interval;
       if(g == AND)
          { deval(ARG(0,ARG(0,interval)),&min);
            deval(ARG(1,ARG(1,interval)),&max);
            if(min == BADVAL || max == BADVAL)
               goto oops;
          }
       else if(g == '<' || g == LE)
          { if(equals(ARG(0,interval),x))
               { deval(ARG(1,interval),&max);
                 if(max == BADVAL)
                    goto oops;
                 min = max - 100.0;
               }
            else if(equals(ARG(1,interval),x))
               { deval(ARG(0,interval),&min);
                 if(min == BADVAL)
                    goto oops;
                 max = min + 100.0;
               }
            else goto oops;
          }
       else if(g == '>' || g == GE)
          { if(equals(ARG(0,interval),x))
               { deval(ARG(1,interval),&min);
                 max = min + 100.0;
               }
            else if(equals(ARG(1,interval),x))
               { deval(ARG(0,interval),&max);
                 min = max - 100.0;
               }
            else
               goto oops;
          }
       else
          goto oops;  /* e.g. if interval is 'true', meaning no interval was specified */
       p->graphs[0]->xmin = floor(min + 0.11)- 0.5;
       p->graphs[0]->xmax = floor(max - 0.11) + 1.5;
       set_ybounds(p->graphs[0]);
       p->ngraphs = 1;

       return;
     }
  oops:   /* not a minmax problem, or no interval specified, or something went wrong using minmax_interval */
  if(p->mainchoice == COMPARE_DIFFERENT &&
     (currenttopic == _comparefandfprime || currenttopic == _comparetwoderivs)
    )
     { /* set all the x-ranges the same */
       p->mainchoice = ORDINARY;
       p->ngraphs = 1;
       init_graph_ranges(p);
       p->ngraphs = ngraphs;
       p->mainchoice = COMPARE_DIFFERENT;
       xmin = p->graphs[0]->xmin;
       xmax = p->graphs[0]->xmax;
       x = p->graphs[0]->independent_variable;
       /* set each graph y-range separately. */
       for(i=1;i<ngraphs;i++)
          { p->graphs[i]->xmin = xmin;
            p->graphs[i]->xmax = xmax;
            defined_somewhere(p->graphs[i]->function,x,xmin,xmax,&y);
            ymax = next_power_of_two(y);
            p->graphs[i]->ymin = -ymax;
            p->graphs[i]->ymax = ymax;
          }
       p->ngraphs = ngraphs;
       return;
     }

  if(p->mainchoice == COMPARE_DIFFERENT)
     { /* set each graph range separately.  Temporarily set p->ngraphs to 1
          and point p->graphs at each graph individually, then make a
          recursive call. */
       p->ngraphs = 1;
       p->mainchoice = ORDINARY;
       savegraphpointer = p->graphs[0];
       for(i=0;i<ngraphs;i++)
          { p->graphs[0] = p->graphs[i];
            init_graph_ranges(p);
          }
       p->graphs[0] = savegraphpointer;
       p->mainchoice = COMPARE_DIFFERENT;
       p->ngraphs = ngraphs;
       return;
     }
  if(p->mainchoice == RELATION)
     { /* as happens for problem type IMPLICIT_DIFF */
       return;
     }
  if(p->mainchoice == POLYROOT && !makepoly(p->graphs[0]->function,x,&poly))
     { /* as happens for topics _complex_quadratics and _complex_cubics */
       int nroots = ARITY(poly)-1;
       double maxroot,maxrootsq,next;
       dcomplex *roots = (dcomplex *) callocate(nroots, sizeof(dcomplex));
       if(!roots)
          { nospace();
            return;
          }
       err = polyroot2(poly,roots,&nroots);
       if(err)
          /* assert(0) */
          { free2(roots);
            return;
          }
       /* compute the maximum of the absolute values of the roots */
       maxrootsq = 0.0;
       for(i=0;i<nroots;i++)
          { next = roots[i].r * roots[i].r + roots[i].i * roots[i].i;
            if(next > maxrootsq)
                maxrootsq = next;
          }
       maxroot = sqrt(maxrootsq);
       p->graphs[0]->xmax = next_power_of_two( 1.4 * maxroot);
       p->graphs[0]->xmin = -p->graphs[0]->xmax;
       p->graphs[0]->ymax = p->graphs[0]->xmax;
       p->graphs[0]->ymin = p->graphs[0]->xmin;
       free2(roots);
       return;
     }
  if(p->mainchoice == RIEMANNSUMS ||
     p->mainchoice == TRAPEZOIDRULE ||
     p->mainchoice == SIMPSONSRULE
    )
     { /* as happens for definite integration */
       if(p->graphs[0]->ymax > 10000.0)
          { p->graphs[0]->ymax = 128.0;
            p->graphs[0]->ymin = -128.0;
            p->graphs[1]->ymax = 128.0;
            p->graphs[1]->ymin = -128.0;
            return;
          }
        return;  /* default_range has already set the ranges appropriately */
     }
  if(ngraphs > 2)
     return;  /* assert(0) */
  if(ngraphs == 2)
     { /* it must be an equation-solving graph or an inequality */
       if(
          INEQUALITY(FUNCTOR(p->graphs[0]->function)) &&
          INEQUALITY(FUNCTOR(p->graphs[1]->function))
         )
         { addthis = ARG(1,p->graphs[1]->function);
           savethis = ARG(1,p->graphs[0]->function);
           f = sum(savethis,tnegate(addthis));
           /* e.g. in abs x < 5, we have y >  abs x   in graphs[0] and y < 5 in graphs[1] */
         }
       else if(equals(p->graphs[1]->function,falseterm))  // e.g.  sin(x) > 1/2
         { savethis = p->graphs[0]->xfunction;
           addthis = p->graphs[1]->yfunction;
           f = sum(savethis,tnegate(addthis));
         }
       else
         { savethis = p->graphs[0]->function;
           addthis = p->graphs[1]->function;
           f = sum(savethis,tnegate(addthis));
         }
     }
  else
     { f = p->graphs[0]->function;
       addthis = zero;
       if(FUNCTOR(f) == '=')
          return;  /* assert(0) */
       if(INEQUALITY(FUNCTOR(f)))
          { f = sum(ARG(0,f),tnegate(ARG(1,f)));
            addthis = ARG(1,f);
            savethis = ARG(0,f);
          }
     }
  savefactorflag = get_polyvalfactorflag();
  savegcdflag = get_polyvalgcdflag();
  set_polyvalgcdflag(1);
  set_polyvalfactorflag(1);  /* otherwise it won't factor and cancel
                                to simplify rational functions */
  polyval(f,&w);
  set_polyvalfactorflag(savefactorflag);
  set_polyvalgcdflag(savegcdflag);
  if(FUNCTOR(w) == SQRT)
     { f = ARG(0,w);
       sqrtflag = SQRT;
     }
  else if(FUNCTOR(w) == ROOT)
     { f = ARG(1,w);
       sqrtflag = ROOT;
     }
  else if(FUNCTOR(w) == '^' && numerical(ARG(1,w)))
     { f = ARG(0,w);
       sqrtflag = '^';
     }
  else
     f = w;
  err = makepoly(f,x,&q);
  if(!err)
     { /* it's a polynomial.  Make sure its real zeroes all show. */
       nroots = polyrange_aux(q,&min,&max);
       if(nroots < 0)
          { /* polynomial of large degree */
            for(i=0;i<ngraphs;i++)
               { p->graphs[0]->xmin = -2.0;
                 p->graphs[0]->xmax = 2.0;
                 p->graphs[0] ->ymax = 16000.0;
                 p->graphs[0]->ymin = -p->graphs[i]->ymax;
               }
            return;
          }
       if(nroots == 0)
          { /* constant polynomial.  Make sure it shows */
            for(i=0;i<ngraphs;i++)
               { p->graphs[i]->xmin = -2.0;
                 p->graphs[i]->xmax = 2.0;
               }
            deval(f,&z);
            ymax = next_power_of_two(z);
            ymin = -ymax;
            if(sqrtflag)
               adjust(sqrtflag,w,&ymin,&ymax);
            if(ngraphs == 1)
               { p->graphs[0]->ymin = ymin;
                 p->graphs[0]->ymax = ymax;
                 return;
               }
            else
               {  /* The two sides of the equation
                    differ by a constant.  Make sure they both show.
                   */
                 defined_somewhere(addthis,x,min,max,&y1);
                 defined_somewhere(savethis,x,min,max,&y2);
                 y = y1 > y2 ? y1 : y2;
                 y2 = -y;
                 if(sqrtflag)
                    adjust(sqrtflag,w,&y2,&y);
                 for(i=0;i<ngraphs;i++)
                    { p->graphs[i]->ymin = y2;
                      p->graphs[i]->ymax = y;
                    }
                 return;
               }
          }
       /* now min and max are the smallest and largest real roots */
       if(nroots && min == max)
           /* linear function, or for example y = x^n */
          { xmax = next_power_of_two(fabs(min) + 0.1);
            /* the 0.1 makes sure we don't leave the zero at the edge of the
               graph, e.g. if the min is -1.9999999  */
            xmin = -xmax;
            SETVALUE(x,xmin);
            deval(f,&y);
            if(!ZERO(addthis))
               { deval(addthis,&y1);
                 y2 = y + y1;
               }
            SETVALUE(x,xmax);
            deval(f,&z);
            if(!ZERO(addthis))
               { deval(addthis,&z1);
                 z2 = z + z1;
               }
            if(!ZERO(addthis))
               { y = fabs(y1) > fabs(y2) ? fabs(y1) : fabs(y2);
                 z = fabs(z1) > fabs(z2) ? fabs(z1) : fabs(z2);
               }
            y = fabs(y);
            z = fabs(z);
            if(z > y)
               y = z;
            ymax = next_power_of_two(y);
            ymin = -ymax;
            if(sqrtflag)
               adjust(sqrtflag,w,&ymin,&ymax);
            for(i=0;i<p->ngraphs;i++)
               { p->graphs[i]->xmin = xmin;
                 p->graphs[i]->xmax = xmax;
                 p->graphs[i]->ymin = ymin;
                 p->graphs[i]->ymax = ymax;
               }
            return;
          }
       get_interval(min,max,&xmin,&xmax);
       for(i=0;i<ngraphs;i++)
          { p->graphs[i]->xmin = xmin;
            p->graphs[i]->xmax = xmax;
          }
       width = 0.05 *(max-min);
       if(fabs(width) < VERYSMALL)
           return;
       /* Find some approximation to the max of abs(f(x)) on the interval
          containing the roots.  It's all right if the graph goes off-screen
          to the top or bottom outside the interval containing the roots.
       */
       if(!ZERO(addthis))
          { defined_somewhere(addthis,x,min,max,&y1);
            defined_somewhere(savethis,x,min,max,&y2);
            y = y1 > y2 ? y1 : y2;
          }
       else
          defined_somewhere(f,x,min,max,&y);
       ymax = next_power_of_two(y);
       ymin = -ymax;
       adjust(sqrtflag,w,&ymin,&ymax);
       for(i=0;i<ngraphs;i++)
          { p->graphs[i]->ymin = ymin;
            p->graphs[i]->ymax = ymax;
          }
       return;
     }
  if(sqrtflag)
     { savearg = f;
       f = w;   /* as it was originally */
     }
  /* Trap rational functions of trig functions here */
  if(rattrig(f,x))
      { z = 2 * PI_DECIMAL;
        err = 0;
        xmax = next_pi(1.2 * z);
        set_yrange(p,f,x,addthis,savethis,-z,z,-xmax,xmax);
        return;
      }
  if(contains_trig(f) &&
     !periodic_in(f,x,&period) &&
     !deval(period,&dperiod) &&
     dperiod != BADVAL
    )
      { xmax = next_pi(1.2 * dperiod);
        z = dperiod;
        set_yrange(p,f,x,addthis,savethis,-z,z,-xmax,xmax);
        return;
      }

  if(contains_trig(f) &&
     !near_periodic(f,x,&u,&v,&period) &&
     !deval(period,&dperiod) &&
     dperiod != BADVAL
    )
      { xmax = next_pi(1.2 * dperiod);
        z = dperiod;
        set_yrange(p,f,x,addthis,savethis,-z,z,-xmax,xmax);
        return;
      }

  /* Now it's not a polynomial or periodic or near_periodic.  It could still be a
     product of polynomials or a rational function, for example.
     We don't want to go immediately to make_polyquo which expands numerator
     and denominator, although that will be the last resort for rational
     functions.  */

  /* Example: sec^5 (x^3); this becomes periodic after a substitution,
     but maxsub won't find it because x^3 occurs only once.  */
  if(trig_subarg(f,x,&u))
     { /* in the example, u will be x^3.  Try to choose a range for x such
          that u will not vary more than 4 pi.  Of course u may not be
          monotonic in x so this may be mere guesswork. */
       max = min = 0.0;
       for(i=0;i<10;i++)
          { SETVALUE(x,i);
            deval(u,&z);
            if(z != BADVAL)
               { if(z > max)
                    max = z;
                 else if(z < min)
                    min = z;
               }
            SETVALUE(x,-i);
            deval(u,&z);
            if(z != BADVAL)
               { if(z > max)
                    max = z;
                 else if(z < min)
                    min = z;
               }
            if(max - min >= 4*PI_DECIMAL)
               break;
          }
       /* In the example, i will be 2  since 2^3 = 8 > 2pi */
       if(i < 10)
          { if(i < 2)
               /* make sure we don't set xmin = xmax = 0.0 */
               { xmin = -2.0;
                 xmax = 2.0;
               }
            else
               { xmin = -i;
                 xmax = i;
               }
            set_yrange(p,f,x,addthis,savethis,min,max,xmin,xmax);
            return;
          }
     }
  heapflag = 0;
  nvars = variablesin(f,&atomlist);
  if(nvars > 1)
     { /* it's too hard to compute zeroes when parameters are present; it
          leads to out-of-space errors sometimes.  Instead, we'll substitute
          the current parameter values for the parameters, and THEN call
          zeroes. */
       term x = p->graphs[0]->independent_variable;
       for(i=0;i<10;i++)
          { SETVALUE(x,0.123 + i);
            deval(f,&z);
            if(z != BADVAL)
               break;
          }
       if(i==10)
          { for(i=0;i<10;i++)
               { SETVALUE(x,-0.123 -i);
                 deval(f,&z);
                 if(z != BADVAL)
                    break;
               }
          }
       if(i==10)
          { xmin =-2.0;
            xmax = 2.0;
            set_yrange(p,f,x,addthis,savethis,xmin,xmax,xmin < 0 ? -next_power_of_two(xmin) : -1.0,next_power_of_two(xmax));
            free2(atomlist);
            return;
          }
       for(i=0;i<nvars;i++)
          { if(equals(x,atomlist[i]))
               continue;
            deval(atomlist[i],&z);
            subst(make_double(z),atomlist[i],f,&temp);
            f = temp;
          }
       free2(atomlist);
     }
  /* Now f has only one variable */
  savenode = heapmax();
  if(!contains_trig(f) &&
     (torture_test || grat(f,x)) &&
     (heapflag = 1) != 0 &&  /* this sets heapflag without provoking a warning */
     !zeroes(f,&roots)
    )
     { count = get_singularities_range(p,x,&min,&max);
       nroots = get_root_range(roots,x,&xmin,&xmax);
       if(count && nroots)
          { xmin = min < xmin ? min : xmin;
            xmax = max > xmax ? max : xmax;
          }
       else if(count)
          { xmin = min;
            xmax = max;
          }
       else if(!count && !nroots)
          { xmin = -2.0;
            xmax = 2.0;
          }
       if(xmin == xmax)
          { xmin -= 1.0;
            xmax += 1.0;
          }
       xmin *= 1.1;
       xmax *= 1.1;
       set_yrange(p,f,x,addthis,savethis,xmin,xmax,xmin < 0 ? -next_power_of_two(xmin) : -1.0,next_power_of_two(xmax));
       return;
     }
  if(heapflag)  /* heapflag is set above just before zeroes is called */
     { reset_heap(savenode);  /* recover memory used in zeroes */
       heapflag = 0;
     }
  if(sqrtflag)
      f = savearg;
  if(grat(f,x) && (heapflag = 1) != 0 && !make_polyquo(f,x,&numpoly,&denompoly))
     { /* f is equivalent to numpoly / denompoly */
       save_and_reset(and(numpoly,denompoly),savenode,&temp);
       numpoly = ARG(0,temp);
       denompoly = ARG(1,temp);
       nroots = polyrange_aux(numpoly,&minroot,&maxroot);
       nsing = polyrange_aux(denompoly,&minsing,&maxsing);
       if(nroots < 0 || nsing < 0)
          { min = -2.0;
            max = 2.0;
            defined_somewhere(f,x,min,max,&y);
            ymax = next_power_of_two(y);
            for(i=0;i<ngraphs;++i)
               { p->graphs[i]->ymin = -ymax;
                 p->graphs[i]->ymax = ymax;
                 p->graphs[i]->xmin = min;
                 p->graphs[i]->xmax = max;
               }
          }
       else if(nsing == 0 && nroots != 0)
          { min = minroot;
            max = maxroot;
          }
       else if(nroots == 0 && nsing != 0)
          { min = minsing;
            max = maxsing;
          }
       else if(nroots == 0 && nsing == 0)
          { min = -2.0;
            max = 2.0;
          }
       else
          { min = minroot < minsing ? minroot : minsing;
            max = maxroot > maxsing ? maxroot : maxsing;
          }
       if(min == max)
          { min = min - 1.0;
            max = min + 2.0;
          }
       get_interval(min,max,&xmin,&xmax);
       for(i=0;i<ngraphs;i++)
          { p->graphs[i]->xmin = xmin;
            p->graphs[i]->xmax = xmax;
          }
       width = 0.05 *(max-min);
       if(fabs(width) < VERYSMALL)
          /* a linear function */
           { min = xmin;
             max = xmax;
           }
       /* Find some approximation to the max of abs(f(x)) on the interval
          containing the roots.  It's all right if the graph goes off-screen
          to the top or bottom outside the interval containing the roots.
       */
       if(!ZERO(addthis))
          { defined_somewhere(addthis,x,min,max,&y1);
            defined_somewhere(savethis,x,min,max,&y2);
            y = y1 > y2 ? y1 : y2;
          }
       else
          defined_somewhere(f,x,min,max,&y);
       ymax = next_power_of_two(y);
       ymin = - ymax;
       if(sqrtflag)
          adjust(sqrtflag,w,&ymin,&ymax);
       for(i=0;i<ngraphs;++i)
          { p->graphs[i]->ymin = ymin;
            p->graphs[i]->ymax = ymax;
          }
       return;
     }
  if(sqrtflag)
     f = w;  /* original value */
  if(heapflag)
     { reset_heap(savenode);   /* recover memory used in rational function arithmetic */
       heapflag = 0;
     }
  if(!get_zero(f,x,&z))
     { if(!is_linear_in(f,x) && !get_second_zero(f,x,z,&z2))
          { a = z < z2 ? z : z2;
            b = z < z2 ? z2 : z;
          }
       else if(fabs(z) < 1.0)
          { a = -1.1;
            b = 1.1;
          }
       else
          { a = -1.1*z;
            b =  1.1*z;
          }
       /* Try to include the singularities if possible */
       if(!contains_trig(f) && get_singularities_range(p,x,&min,&max))
          { if(min < a)
                a = min;
            if(max > b)
                b = max;
          }
       set_yrange(p,f,x,addthis,savethis,a,b,a < 0 ? -next_power_of_two(a): -1.0,next_power_of_two(b));
       return;
     }
  /* Now, we couldn't find a zero */
  /* If it has singularities pre-calculated, be sure to include them: */
  count = get_singularities_range(p,x,&min,&max);
  if(count)
     { /* make sure xmin < min < max < xmax */
       if(fabs(min) > fabs(max))
          max = fabs(min);
       else
          max = fabs(max);
       if(rattrig(f,x))
          xmax = PI_DECIMAL * next_power_of_two( 1.2 * max/ PI_DECIMAL);
       else
          xmax = next_power_of_two(1.2 * max);
       xmin = -xmax;
       if(!ZERO(addthis))
          { if(defined_somewhere(addthis,x,xmin,xmax,&y1) &&
               defined_somewhere(savethis,x,xmin,xmax,&y2)
              )
               y = y1 > y2 ? y1 : y2;
            else
               y = 8.0;
          }
       else if(!defined_somewhere(f,x,xmin,xmax,&y))
          y = 8.0;
       ymax = next_power_of_two(y);
       ymin = -ymax;
       for(i=0;i<p->ngraphs;i++)
          { p->graphs[i]->xmin = xmin;
            p->graphs[i]->xmax = xmax;
            p->graphs[i]->ymin = ymin;
            p->graphs[i]->ymax = ymax;
          }
       return;
     }

  /* Now just look for a place where it's defined */
  if(rattrig(f,x))
     { xmin =  -2* PI_DECIMAL;
       xmax = savexmax =  2 * PI_DECIMAL;
     }
  else
     { xmin = (int)(-2.0);
       xmax = savexmax = (int) 2.0;
     }
  for(count = 0; count < 20; count++)
     { if(!ZERO(addthis))
          { if(defined_somewhere(addthis,x,xmin,xmax,&y1) &&
               defined_somewhere(savethis,x,xmin,xmax,&y2)
              )
              { y = y1 > y2 ? y1 : y2;
                break;
              }
          }
       else if(defined_somewhere(f,x,xmin,xmax,&y))
          break;
       xmin *= 2.0;
       xmax *= 2.0;
     }
  if(count == 20)
     { xmax = savexmax;
       xmin = -savexmax;
       ymax = 8.0;
       ymin = -8.0;
     }
  else
     { ymax = next_power_of_two(y);
       ymin = -ymax;
     }
  for(i=0;i<p->ngraphs;i++)
     { p->graphs[i]->xmin = xmin;
       p->graphs[i]->xmax = xmax;
       p->graphs[i]->ymin = ymin;
       p->graphs[i]->ymax = ymax;
     }
}

/*_________________________________________________________________________*/
static double next_power_of_two(double x)
/* Return the least positive power of two greater than fabs(1.01 * x),
   or 2^21 if x is larger than 2^21.
*/
{ int i;
  double z;
  if(x < 0.0)
     x = -x;
  if(x < 1.0)
     return 1.0;
  if(x < 2.0)
     return 2.0;
  z = 2.0;
  for(i=1; z <= (1.01) * x && i < 20; i++)
      z *= 2.0;
  return z;
}
/*________________________________________________________________________*/
static int defined_somewhere(term t, term x, double a, double b, double *y)
/* return 1 if t is defined on one of some 20 values of x between a and b,
and return in *y  the value such that the y-range should be set to
-2^n, 2^n where 2^n is the next power of 2 greater than *y.  If zero is
returned, *y will be 8.0.
*/

{ double gap = 0.05 *(b-a);
  int i;
  int count = 0;
  double sum=0.0,z;
  double max[3]; /* three largest values */
  double average,average2;
  *y = 8.0;
  if(fabs(a-b) < VERYSMALL)
     { SETVALUE(x,a);
       deval(t,&z);
       if(z == BADVAL)
            return 0;
       *y = fabs(z);
       return 1;
     }
  max[0] = max[1] = max[2] = 0.0;
  for(i=0;i<20;i++)
     { SETVALUE(x,a + i*gap);
       deval(t,&z);
       if(z == BADVAL)
          continue;
       z = fabs(z);
       ++count;
       sum += z;
       if(z > max[0])
          { max[2] = max[1];
            max[1] = max[0];
            max[0] = z;
          }
       else if(z > max[1])
          { max[2] = max[1];
            max[1] = z;
          }
       else if(z > max[2])
          max[2] = z;
     }
  if(count == 0)
     return 0;
  average = sum / count;
  average2 = (sum - max[0] - max[1])/(count-2);
  if(max[0] < 2 * max[2])
     *y = max[0];
  else if( max[1] < 10 * max[0] && max[2] >= 0.5 * max[1])
     *y =  max[1];  /* probably max[0] is near a singularity */
  else if( max[1] < 30 * max[0])
     *y = max[1];   /* almost certainly max[0] is near a singularity */
  else if(max[1] < 5 * average)
     *y = 2*average;
  else if(max[1] < 10 * average)
     *y = 4 * average;
  else
     *y = 4 * average2;
  return 1;
}

/*____________________________________________________________________*/
static void get_interval(double x, double y, double *a, double *b)
/* given that the roots of f are between x and y, return the
graph interval [a,b] to show on the x-axis */
{ double max;
  if(x > 0)
     { *a = -1.0;
       *b = next_power_of_two(y);
       return;
     }
  if(y < 0)
     { *b = 1.0;
       *a = -next_power_of_two(x);
       return;
     }
  max = fabs(x);
  if(fabs(y) > max)
     max = fabs(y);
  *b = next_power_of_two(max);
  *a = -*b;
}

/*_______________________________________________________________________*/
int rattrig(term t, term x)
/* return 1 if t contains a trig function whose arg is
a linear function  ax + b where a doesn't contain pi.
*/
{ unsigned short n,f;
  int i;
  term u;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(f == SIN || f == COS || f == TAN || f == COT || f == SEC || f == CSC)
     { u = ARG(0,t);
       if(!is_linear_in(u,x))
          return 0;
       if(!contains(u,PI_ATOM))
          return 1;
       /* but what about sin(pi-x)? */
       if(FUNCTOR(u) != '+')
          return 0;
       for(i=0;i<ARITY(u);i++)
          { if(contains(ARG(i,u),FUNCTOR(x)) && contains(ARG(i,u),PI_ATOM))
               return 0;
          }
       return 1;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(rattrig(ARG(i,t),x))
          return 1;
     }
  return 0;
}

/*________________________________________________________________________*/
static double next_pi(double x)
/* return the least number of the form 2^n pi which is > fabs(x); but
never return less than 2pi or more than 2^21 pi
*/

{ int i;
  double z;
  if(x < 0.0)
     x = -x;
  if(x < 2.0 * PI_DECIMAL)
     return 2.0 * PI_DECIMAL;
  z = 2.0 * PI_DECIMAL;
  for(i=1; z <= (1.01) * x && i < 20; i++)
      z *= 2.0;
  return z;
}
/*_________________________________________________________________________*/
static int get_zero(term t,term x,double *ans)
/* Try, by various numerical methods, to find a zero of t(x).
If you succeed, return the answer in *ans, and return 0 for success.
If you can find a zero which is not zero, take that one; otherwise
zero will do.
   Return 1 for failure.
   If there's a zero near origin, we want to find it, e.g. so that
we don't graph trig functions over a  range.  So we start with a small
interval, look for  zero there, and if it doesn't work, increase the interval.
*/

{ int err;
  double a,y,oldy=0.0; /* initialize oldy to avoid a warning message only */
  /* first try finding a sign-change of t and calling Brent's method. */
  double xmin = -2.0;
  double gap = 1.0;
  int count = 0;
  int maxiterations = 18;
  int zeroflag = 0;
  if(obviously_positive(t))  /* Not clear whether this saves time or not */
     return 1;
  if(obviously_negative(t))
     return 1;
  start:
  for(a = xmin ; a <= -xmin; a += gap)
     { SETVALUE(x,a);
       deval(t,&y);
       if(y == BADVAL)
           continue;
       if(y == 0.0)
          { *ans = a;
            return 0;
          }
       if(a > xmin &&
          ((oldy < 0.0 && y > 0.0) || (oldy > 0.0 && y < 0.0))
         )
           { err = solve(t,x,a-gap,a,ans);
             if(!err && fabs(*ans) > VERYSMALL)
                return 0;
             if(!err)
                zeroflag = 1;
           }
       oldy = y;
     }
  xmin *= 1.5;
  ++count;
  if(count == maxiterations)
     { if(zeroflag)
          { *ans = 0.0;
            return 0;
          }
       return 1;
     }
  goto start;
}

/*_________________________________________________________________________*/
static int get_second_zero(term t,term x,double c, double *ans)
/* Try, by various numerical methods, to find a zero of t(x), greater
than c, which is presumed to be a given zero of t(x).
If you succeed, return the answer in *ans, and return 0 for success.
   Return 1 for failure.
*/

{ int err;
  double a,y,oldy=0.0; /* initialize oldy to avoid a warning message only */
  /* first try finding a sign-change of t and calling Brent's method. */
  double xmax = 100.0;
  double gap = 1.0;
  for(a = c ; a <= xmax; a += gap)
     { SETVALUE(x,a);
       deval(t,&y);
       if(y == BADVAL)
           continue;
       if(y == 0.0 && a > c)
          { *ans = a;
            return 0;
          }
       if(a > c &&
          ((oldy < 0.0 && y > 0.0) || (oldy > 0.0 && y < 0.0))
         )
           { err = solve(t,x,a-gap,a,ans);
             if(!err)
                return 0;
           }
       oldy = y;
     }
  return 1;
}


/*______________________________________________________________________*/
static int trig_subarg(term t, term x, term *arg)
/* return 1 if t contains a trig function containing x;
in that case *arg will be the arg of the first trig function containing x.
*/

{ unsigned short n,f;
  int i;
  if(ATOMIC(t))
     return 0;
  f = FUNCTOR(t);
  if(TRIGFUNCTOR(f) && contains(ARG(0,t),FUNCTOR(x)))
     { *arg = ARG(0,t);
       return 1;
     }
  n = ARITY(t);
  for(i=0;i<n;i++)
     { if(trig_subarg(ARG(i,t),x,arg))
          return 1;
     }
  return 0;
}

/*________________________________________________________________________________________*/
static void set_ybounds(graph *g)
/* called in minmax problems to set g->ymin and g->ymax so that
(1) the x-axis is visible, i.e. ymin < 0 < ymax, and
(2) the min and max are visible, and
(3) the graph looks pretty.

This assumes that minmax_interval is an AND, and that
g->xmin and g->xmax have been set already.  They have been
set a bit beyond the bounds of the minmax interval, so the
function can be undefined near the edges of the graph,
and it can have singularities at or beyond the endpoints.
*/

{ double a = g->xmin;
  double b = g->xmax;
  term x = g->independent_variable;
  term u = g->function;
  int i;
  const int m = 20;
  double gap = (b-a)/m;
  double z=a;
  double max,min,val;
  SETVALUE(x,a);
  deval(u,&val);
  if(val != BADVAL)
     { min = max = val;
       i=1;
       z = a+gap;
     }
  else
     { i = 0;
       while(val == BADVAL && i < 30)
          { z += gap;
            SETVALUE(x,z);
            deval(u,&val);
            ++i;
          }
       min = max = val;
       if(val == BADVAL)
          assert(0);
       ++i;
       z = a + i*gap;
     }
  for( ;i<m;i++,z+=gap) /* i and z are already initialized */
     { SETVALUE(x,z);
       deval(u,&val);
       if(val == BADVAL)
          continue;    /* This can only happen if there's a singularity at the
                          left or right endpoint */
       if(fabs(val) > 1.0e+010)  /* probably just missed a singularity at the
                                    left endpoint */
          continue;
       if(val > max)
          max = val;
       else if (val < min)
          min = val;
     }
  SETVALUE(x,b);
  deval(u,&val);
  if(val != BADVAL)
     { if(val > max)
          max = val;
       else if(val < min)
          min = val;
     }
  if(min >= 0.0)
     min = - ceil(max/2.0);
  if(max <= 0.0)
     max = ceil(-min/2.0);
  if(min == max)
     { min -= 2.0;
       max += 2.0;
     }
  max *= 1.5;
  min *= 1.5;
  g->ymin = - ceil(-min);
  g->ymax = ceil(max);
}

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