Sindbad~EG File Manager

Current Path : /usr/home/beeson/cgraph/
Upload File :
Current File : /usr/home/beeson/cgraph/graph2.c

/* MATHPERT grapher, by M. Beeson*/
/* The functions in this file actually draw the graph lines
4.12.90  original date
8.23.98  modified
11.29.99 added invert_color and modified the 3 lines that call it.
1.9.00 added justpassedflag
12.2.00 modified carefulgraph for jumps.
4.6.07 changed hypot to _hypot
9.21.10 modified at dated lines
10.12.10 modified around lines 630 and 667 to improve graphing functions defined by cases.
6.12.13  modified draw_riemann to compute the correct mathematical area while using another variable for the rectangle
coordinate.
2.24.24 changed include cgraph. to svgGraph.h and svgDevice to svgDevice
          and set_graphpencolor to set_graphpencolor
          and set_graphbackgroundcolor to set_graphbackgroundcolor
3.22.24  added begin_path and end_path in graph_parametric
         and many of them in careful_graph
6.23.24  modified draw_riemann to use g->fillcolor
7.1.24   modified draw_trapezoid and draw_simpson to use g->fillcolor
7.29.24  added lastx, lasty, and some move_to commands
7.29.24  added open_filled_circle
*/

#include <math.h>
#include <assert.h>
#include "globals.h"
#include "graphstr.h"
#include "svgGraph.h"
#include "grapher.h"
#include "heap.h"   /* my own malloc and free */
#include "eqn.h"   /* solve              */
#include "deval.h"
#include "sing.h"  /* check_restrictions */
#include "islinear.h"
#include "pstring.h"

static int badpt(double);
static void solve_edge(double a, double b, double *xp, term f, double *y);
#define OPPOSITE_SIGN(x,y)   (((x) < 0.0 && (y) > 0.0) || ((x) > 0.0 && (y) < 0.0) || ((x == 0.0 || y == 0.0) && x*y != 0.0))

/*___________________________________________________________________*/
/* world_to_pixel handles the problem of pixel coordinates > 32K
   and never returns such coordinates.  Hence badpt doesn't have to
   check for this (as it once did).

   There are three other ways of getting bad points:
             (1) an error from the math library, which matherr intercepts and
                 substitutes BADLIMIT for,
             (2) a floating-point exception, which signal() has told the
                 system to ignore;  nothing is done about these.  In theory
                 only overflow or underflow or TLOSS could occur, see (3)
                 for overflow; underflow comes to zero; and TLOSS is still
                 a possibility.
             (3) a floating-point overflow, for which the 80x87 does not raise
                 a floating-point exception, but rather substitutes a specific
                 'overflow-indication' number for the return value.
*/

#define BADLIMIT 1.0E300   /* this value must mesh with matherr.c and deval.c */

static const double overflow = (double) 1.0e200 * (double) 1.0e200;
/* 80x87 returns this value on overflow instead of generating a floating-point
exception.  This variable is also needed in graph2.c.
   Compiling this line causes a warning message.  This warning message
is suppressed by the pragma commands above and below.  */

static int badpt(double x)
{ if( x > BADLIMIT)
     return 1;      /* depends on definition of matherr() */
  else if(x < -BADLIMIT)
     return 1; /* which returns large values for errors */
  return (x==overflow) ? 1 : 0;
}

/*_____________________________________________________________________*/
/* No checking for singularities in graphparametric */
/* calculates the gap between points dynamically, rather than having
   npts passed in */

int graphparametric(term xf, term yf,
                      double *tp,  /* value pointer of the independent variable */
                                   /* has to be changed as we go so deval works right */
                      double tmin, double tmax,
                      double xmin, double xmax,
                      double ymin, double ymax)
{ double x,y,nextx,nexty,farawayx,farawayy,nextx2,nexty2;
  int flag,newflag;
  double maxgap,gap;   /* must be dynamically calculated */
  double smallgapx = (xmax-xmin)/300.0;  /* smallest acceptable x-jump */
  double biggapx = smallgapx * 3.0;      /* largest acceptable x-jump */
  double smallgapy = (ymax -ymin)/300.0;
  double biggapy = smallgapy * 3.0;
  double actualgapx,actualgapy;
  double newgap, smallgap, savetp;
  int i;
  *tp = tmin;
  gap = maxgap = (tmax - tmin) / 200.0;   /* for starters */
  deval(xf,&x);
  deval(yf,&y);
  begin_path();
  move_to(x,y);     /*position graphics cursor*/
  flag = ( badpt(y) || badpt(x) );
  while(*tp < tmax)
     { *tp = *tp + gap;
       if( deval(xf,&nextx) || deval(yf,&nexty))
          /* that is, if either xf or yf can't be evaluated */
          { if(flag == 0)
               /* try not to leave a false gap. Get a better approximation
                  to where it starts being undefined. */
               { smallgap = gap/10.0;
                 savetp = *tp;
                 flag = 1;
                 *tp -= gap;
                 for(i=0;i<10;i++)
                    { *tp += smallgap;
                      if(!deval(xf,&nextx2) && !deval(yf,&nexty2) &&
                         nextx2 != BADVAL && nexty2 != BADVAL
                        )
                         { x = nextx2;
                           y = nexty2;
                           line_to(x,y);
                           continue;
                         }
                    }
                 *tp = savetp;
               }
            continue;
          }
       if(flag)  /* previous point was undefined but this one was ok */
          { flag = 0;
            /* again try not to leave a false gap */
            smallgap = gap/10.0;
            savetp = *tp;
            *tp -= gap;
            for(i=0;i<10;i++)
               { *tp += smallgap;
                 if(!deval(xf,&nextx2) && !deval(yf,&nexty2) &&
                    nextx2 != BADVAL && nexty2 != BADVAL
                   )
                    break;
               }
            if(i < 10)
               { nextx = nextx2;
                 nexty = nexty2;
               }
            x = nextx;
            y = nexty;
            move_to(x,y);
            continue;
          }
       actualgapx = fabs(nextx - x);
       actualgapy = fabs(nexty - y);
       /* If we are off-screen we don't want to waste time: */
       farawayx = fabs(x - 0.5*(xmax+xmin));
       farawayy = fabs(y - 0.5*(ymax+ymin));
       if( (farawayx > 2.0 * (xmax-xmin) && actualgapx < 0.8 * farawayx) ||
           (farawayy > 2.0 * (ymax-ymin) && actualgapy < 0.8 * farawayy)
         )
           { newgap = gap * 2.0;
             if(newgap <= maxgap)
                gap = newgap;
             x = nextx;
             y = nexty;
             move_to(x,y);
             continue;
           }
       if(actualgapx > biggapx)
          /* too big a jump, must recalculate */
          { *tp = *tp-gap;  /* it was incremented above */
            gap = gap * 0.9 * (biggapx/actualgapx);  /* decrease the gap */
            *tp = *tp + gap;
            deval(xf,&nextx);
            deval(yf,&nexty);
            actualgapx = fabs(nextx - x);
            actualgapy = fabs(nexty - y);
          }
       if(actualgapy > biggapy)
          { *tp = *tp-gap;
            gap = gap * 0.9 * (biggapy/actualgapy);
            *tp = *tp+gap;
            deval(xf,&nextx);
            deval(yf,&nexty);
            actualgapx = fabs(nextx - x);
            actualgapy = fabs(nexty - y);
          }
       if( *tp > tmax)   /* don't overshoot the endpoint */
          { *tp = tmax;
            deval(xf, &nextx);
            deval(yf, &nexty);
          }
       x = nextx;
       y = nexty;
       /* Now at least we haven't taken too BIG a jump */
       /* But for speed, we don't want the gap to be too small either,
          so increase the gap if too little progress has been made;
          but on the example r = cos^17 t sin(3t), this went wrong because
          all the action takes place in a tiny interval of t values, so
          we increase the gap several times and then skip the next action
          interval entirely!  So, don't increase the gap above 1/200 of the
          total t-interval.  */

       if(actualgapx < smallgapx  && actualgapy < smallgapy)
           { newgap = gap * 2.0;
             if(newgap <= maxgap)
                gap = newgap;
           }
       newflag = (badpt(x) || badpt(y));
                    /*flag refers to point i-1 */
       if (flag == 0 && newflag==0)  /* both points ok */
          line_to(x,y);
       else
          move_to(x,y);     /*without drawing*/
       flag = newflag;                         /*now flag refers to point i */
     }
  end_path();
  return 0;
}

/*______________________________________________________________________*/
int carefulgraph(int whichcase, graph *g)

/* Whichcase serves to distinguish the several situations
   with regard to singularities:
            1 = numerical singularities;
            2 = usable symbolic but not numerical singularities;
            3 = no singularities;
            4 = symbolic singularities too hard to compute, reason unknown
            5 = symbolic singularities computable but contain two
                existential parameters
            6 = symbolic singularities computable, only one existential
                parameter, but too hard to solve for numerical singularities.
                     (In cases 4,5,6 we can't draw them right)

In case (1):  Check the abscissas mentioned in the array. 'singularities'.
As such an abscissa is passed, if it is a singularity, we draw up to it but not
across it, thereby avoiding spurious vertical lines.

In case (2):  Evaluate the formula for the singularities at every step to
see if a singularity is being passed.

In remaining cases:  just draw.  If it comes out wrong, too bad.

Handling extrema:  when graphs are wiggly as in sin(1/x) or sin(44 x) it's
important to plot a point at each extremum.  Therefore the derivative is
passed along with the function.  At each step check whether the derivative
has changed sign.  If so, use Brent's method to find the critical point and be
sure to draw a point exactly there. */

/* This function also watches out not to draw across jump discontinuities.
It can handle both numerical and symbolic expressions for the jump
discontinuities, which are stored in appropriate fields of the graph
structure. */

/* Return values:
  0 for success,
  1 for out-of-space,
  2 for function-undefined-on-entire-interval.
  4 for function defined only on isolated points;
    either 2 or 4 can include a case like y = 1/x^1,000,000,000  in which
    the points all, or all but a few, evaluate to BADVAL.
*/

{ int err;
  int justpassedflag = 0;
  int nlines = 0;
  svgDevice *device = get_device();
  term f = g->function;          /* the function to be graphed */
  term fprime = g->fprime;       /* its derivative */
  term xvar = g->independent_variable;
  double *xp =(double *) ARGPTR(xvar);        /* pointer to value of the independent variable */
  double xmin = g->xmin;
  double xmax = g->xmax;
  double ymin = g->ymin;
  double ymax = g->ymax;
  int m = g->numberofpoints;      /* number of points to be plotted */
  int msing = g->nsingularities;  /* dimension of array of singularities */
  int mjumps = g->njumps;
  int lastsing = -1;
  double *numjumps = g->jumps;
  double *singularities = g->singularities;  /* sorted array of numerical singularities */
  term *sing = g->slist;
  int dim = g->dimslist;

  int flag,newflag;  /* nonzero value of flag says last point was bad */
  int initial_singularity=0;
  double x,y,oldy,slope,yprime,oldyprime,nextsing,oldsing;
  double gap = (xmax - xmin) / (m - 1);
  double nominalgap = gap;
  double epsilon  = gap/1000.0;
  double roundoff = VERYSMALL * (xmax-xmin);
  double deltayprime;
  double pix = device->xpixel;
  double savex,ext;
  double slopeone = (ymax-ymin)/(xmax-xmin);
  double xjump,yjump,xplus,xminus,yplus,yminus;
  double lastx, lasty;
  int i=0;
  int next=0;   /* index of next singularity to watch out for */
  int nextjump = 0;  /* index of next jump to watch out for */
  term left,right;
  int singflag,backup,jj;
  double *singstore;  /* space to store singularities computations */
  double t,t1,t2;
  int k;
  int hradius;    /* pixel radius of circles to draw at jumps */
  double wradius;
  hradius =(int)(0.30 * device->fontheight); /* works for printer or screen */
  wradius = hradius * device->xpixel;  /* world coordinates of radius in x-direction */
  x = *xp = xmin;
  if(whichcase==2) /* usable symbolic singularities */
     { assert(dim > 0);
       singstore = (double *) callocate(dim,sizeof(double));
       if(!singstore)
          nospace();
       for(k=0;k<dim;k++)
        { if(FUNCTOR(sing[k])=='=')
             { deval(ARG(1,sing[k]),&t2);
               deval(ARG(0,sing[k]),&t1);
               singstore[k] = t2-t1;
             }
          else
             { deval(sing[k],&singstore[k]);
             }
        }
     }
  deval(f,&y);
  flag = badpt(y);  /* 1 if y is a bad point, 0 if not */
  while(flag)  /* we only enter this loop if first point was bad */
     { x += gap;
       *xp = x;
       if(x >= xmax)
           { if(whichcase==2)
                free2(singstore);
             return 2;   /* function undefined */
           }
       if(fabs(x) < roundoff)
           x = *xp = 0.0;
          /* probably it's really zero, this is roundoff error */
          /* For example graphing log x, we get a small positive x
             which should be zero.  Without this we get a spurious line
             because we have crossed a singularity  */
       if(next < msing && fabs(x-singularities[next]) < roundoff)
           x = *xp = singularities[next];
           /* similarly in case the domain begins with a singularity
              somewhere other than zero */

          /* There can be 'singularities'  that are not in the (closure of the)
             domain of the function, e.g. when graphing �(x tan^2x).
             See sing_exp in file singular.c for more explanation.
          */
       if(whichcase == 1 && next < msing && x > singularities[next])
          { ++next;
            initial_singularity = 1;
          }
       else
          initial_singularity = 0;
       deval(f,&y);  /* keep advancing till we get a good starting point */
       ++i;         /* e.g. if the function is undefined for a while */
       flag = badpt(y);
       if(!flag)
          { /* try backing up a little bit so as not to skip the first little
               piece of the graph */
            solve_edge(x-gap,x,xp,f,&y);
            x = *xp;
            if(initial_singularity && next > 0 && x < singularities[next-1])
               { /* previous point had f undefined, but between two points there was first a
                    portion where the graph WAS defined, then a singularity.  E.g. when
                    the range is 256 and the domain begins at x=0 and there's a singularity
                    at x=1, gap is 1.28 so we skip from 0 to 1.28 */
                 initial_singularity = 0;
                 --next;
               }
            break;
          }
     }
  oldy = y;
  deval(fprime,&yprime);
  begin_path();
  if(initial_singularity)
     { /* then we've just passed a singularity at the edge of the domain, so we
          need to draw a line to (x,y) from (x-gap, BADVAL) or (x-gap, - BADVAL)
       */
       savex = *xp;
       nextsing = singularities[next-1];
       *xp = nextsing + epsilon;
       deval(fprime,&slope);
       if(slope == BADVAL)
          { *xp = nextsing + 10*epsilon;
            deval(fprime,&slope);
            if(slope == BADVAL)
               { *xp = nextsing + 100 *epsilon;
                 deval(fprime,&slope);
               }
          }
       *xp = savex;
       if(slope > slopeone )
          /* then we are coming on from below */
          { move_to(nextsing,device->ymin-device->ycliplimit);
            line_to(x,y);
            lastx = x; lasty = y;
            ++nlines;
          }
       else if( slope < -slopeone ) /* coming on from above */
          { move_to(nextsing,device->ycliplimit);
            line_to(x,y);
            lastx = x; lasty = y;
            ++nlines;
          }
       /* else if the slope wasn't large, something was fishy...don't draw */
       else
          move_to(x,y);
     }
  else if(whichcase == 1 && next > 0 && next-1 < msing)
     { if (x-gap <=  singularities[next-1] &&
           singularities[next-1] < x + roundoff
          )
          /* a singularity AFTER x-gap but before or equal to x*/
          { if(singularities[next-1] >= x)
               /* move x a little to the right so it's certainly past the
                  singularity.  Without this, x can be within roundoff error
                  of the singularity and not register as crossing it yet. */
               { x += roundoff;
                 *xp = x;
                 deval(fprime,&yprime);
                 deval(f,&y);
               }
            if(yprime > 0.0 && yprime != BADVAL)
               { move_to(singularities[next-1],-device->ycliplimit);
                 line_to(x,y);
                 lastx = x; lasty = y;
                 ++nlines;
               }
            else if(yprime < 0.0 && yprime != BADVAL)
               { move_to(singularities[next-1],device->ycliplimit);
                 line_to(x,y);
                 lastx = x; lasty = y;
                 ++nlines;
               }
          }
       else
          move_to(x,y);
     }
  else
     move_to(x,y);  /* initialize graphics cursor (if nothing was drawn yet) */
     /* Now here comes the main for-loop that draws segment after segment */
  for (++i; x <= xmax ;i++)   /* i is initialized to 1 + the number of initial bad points */
     { *xp = x = x+gap;   /*  x[i]   */
       oldy = y;          /*  y[i-1] */
       justpassedflag = 0;
       if( whichcase == 1 && next < msing  &&
           x <= singularities[next] &&
           x + roundoff > singularities[next]
         )
          { /* Within roundoff error of a singularity.  Push x a
              little to the right to cross the singularity now,
              because otherwise the code below that is intended
              to plot exact maxima can cause mistaken lines: if fprime
              doesn't deval to BADVAL, ssolve gets called and can even
              succeed, resulting in plotting a spurious "maximum". */
            *xp = x = x + roundoff;
            justpassedflag = 1;
          }
       if( whichcase == 1 && next < msing &&
           x > singularities[next] &&
           x - 1.1 * roundoff < singularities[next]
          )
          { /* just past a singularity.  The trouble here is that deval
               tries to 'forgive' roundoff error and e.g. returns 0 for
               the square root of a tiny negative number, so it can
               return a value when the function is really not defined
               to the right of the singularity */
            justpassedflag = 1;
            *xp = x + epsilon;
            deval(f,&y);
            newflag = badpt(y);
            if(!newflag)    // the function is apparently defined at x + epsilon, but maybe that's not really true, as remarked above, so let's check it.
               { *xp = singularities[next] + roundoff;
                 deval(f,&y);
                 newflag = badpt(y);
               }
          }
       else
          { deval(f,&y);       /*  y[i]   */
            newflag = badpt(y);   /*flag refers to point i-1 */
          }
       if(flag && newflag)
          { if(next < msing && x > singularities[next])
               ++next;    /* crossed a 'singularity' outside domain of function */
            if(nextjump < mjumps && x > numjumps[nextjump])
               ++nextjump;  /* crossed a 'jump' outside domain of function */
            flag = newflag;
            continue;       /* function was undefined at previous point
                             and also at this point */
          }
       if(!flag && !newflag)     /* previous point and this one both defined */
          { oldyprime = yprime;
            deval(fprime,&yprime);
            if(yprime != BADVAL && !justpassedflag)
            /* if the second derivative is large we should decrease the gap */
            /* or what amounts to the same thing, if y' is changing too rapidly */
            /* unless the gap is already pixel-sized */
               { deltayprime = fabs(yprime-oldyprime);
                 if(deltayprime > slopeone && gap > pix)
                    { gap /= 2.0;
                      *xp = x = x-gap;
                      deval(f,&y);
                      newflag = badpt(y);
                      deval(fprime,&yprime);
                    }
                 else if(deltayprime < 0.1 * slopeone && gap < nominalgap)
                   /* increase the gap again after getting through the wiggly part */
                    gap *= 2.0;
               }
                   /* now test for crossing the next critical point */
            if (oldyprime != BADVAL && yprime != BADVAL && !justpassedflag &&
                OPPOSITE_SIGN(oldyprime,yprime) &&
                /* Even if they aren't BADVAL, it's conceivable we might be crossing
                   a pole of even order here; but nothing will be drawn in that case
                   anyway.  A worse danger here is that roundoff error may result
                   in yprime having an incorrect sign. */
                !(whichcase == 1 && next < msing && x > singularities[next]) &&
                !( nextjump < mjumps && x > numjumps[nextjump])  // added 9.21.10
                /* be sure we're not crossing a singularity or a jump here; if we are,
                   don't worry about the critical point we may also be passing. */
               )

               { err = solve(fprime,xvar,x-gap,x,&ext);  /* Using Brent's method, find
                                                   the exact critical point */
                 *xp = x;  /* solve changes it */
                 if(!err)  /* although we know the root is bracketed here, it can
                              still fail, e.g. when graphing x sin x^3 across x=0,
                              due to "too many iterations"  */
                    { savex = *xp;
                      *xp = x = ext;
                      deval(f,&t);
                      if (t != BADVAL)
                         { line_to(ext,t);    /* then draw!  */
                           lastx = ext; lasty = t;
                           ++nlines;
                         }
                      else
                         { move_to(ext,t);     /* without drawing */
                           if(nextjump < mjumps && x > numjumps[nextjump])
                               ++nextjump;
                         }
                      *xp = x = savex;
                      if (t != BADVAL)
                         { line_to(x,y);    /* then draw!  */
                           lastx = x; lasty = y;
                           ++nlines;
                         }
                      else
                         { move_to(x,y);     /* without drawing */
                           // we've crossed a jump, so put in any closed or open circles that
                           // need to be drawn at that jump.
                           if(nextjump < mjumps && x > numjumps[nextjump])
                              ++nextjump;
                         }
                      flag = newflag;
                      continue;
                    }
                 /*  we get here (1) when the curve is too FLAT for Brent's
                     method, so we aren't in danger of too many wiggles;
                     or (2) when crossing an even-order singularity.  But
                     we don't know which yet, so we can't draw. */
                }
          }
    /* That takes care of getting extrema drawn correctly.  Now let's
       worry about getting singularities and jumps drawn correctly.
       The code within the following switch draws singularities.  The
       code AFTER the switch draws circles on jumps.  Thus we should
       escape from the switch with 'continue' unless we have passed
       a jump. */

       switch(whichcase)
          {  case  1:   /* numerical singularities */
                if ( (next >= msing) || (x < singularities[next]))
              /* Not crossing a singularity.  */
                   { if (flag == 0 && newflag==0 && /* both points ok */
                         (nextjump >= mjumps || x <= numjumps[nextjump])
                          /* and not crossing a jump */
                        )
                        { line_to(x,y);    /* then draw!  */
                          lastx = x; lasty = y;
                          ++nlines;
                          flag =newflag;
                          continue;
                        }
                     else if(flag && !newflag && lastsing >= 0 && y < ymax && y > ymin &&
                             x-singularities[lastsing] < 5.0*gap
                             /* if it's more than that, chances are this is a
                             gap in the domain. */
                            )
                        { /* have crossed a singularity and still got y = BADVAL
                             one or more times after the singularity.   Is this
                             due to the function being undefined, or just due to
                             its being too large to deval?  If the latter, now
                             it's time to draw. */
                          savex = *xp;
                          *xp = singularities[lastsing] + epsilon;
                          deval(fprime,&slope);
                          backup = 10;
                          for(jj = 0; jj < 4 && slope == BADVAL;jj++)
                                { *xp = singularities[lastsing] + backup*epsilon;
                                  deval(fprime,&slope);
                                  if(slope != BADVAL)
                                     break;
                                  backup *= 10;
                                }
                          *xp = savex;
                          if(slope > slopeone )
                          /* then we are probably coming on from below */
                             { move_to(singularities[lastsing],device->ymin-device->ycliplimit);
                               line_to(x,y);
                               lastx = x; lasty = y;
                               ++nlines;
                             }
                          else if( slope < -slopeone ) /* coming on from above */
                             { move_to(singularities[lastsing],device->ycliplimit);
                               line_to(x,y);
                               ++nlines;
                             }
                             /* else probably spurious */
                          *xp += gap;
                          lastsing = -1;
                        }
                     else if(!flag || !newflag)  // modified 10.21.10
                        { if(nextjump < mjumps && x + epsilon > numjumps[nextjump])
                             break;  /* crossing a jump */
                        }
                     /* else one or both of x and oldx are bad values!
                        Don't draw */
                     if(newflag == 0)
                        move_to(x,y);
                   }

                else   /* crossing singularity, don't just draw, but make sure we
                          draw if necessary to make the graph go off-screen,
                          and finally update next. */
                   { nextsing = singularities[next];
                     crossing:
                        /* Check if the last point was on-screen: */
                        if( i > 0 && oldy < ymax && oldy > ymin)
                           { /* then it was on-screen so we have to draw something*/
                             /* But which way? up or down? */
                             backup = 10;
                             savex = *xp;
                             *xp = nextsing-epsilon;
                             deval(fprime,&slope);
                             for(jj = 0; jj < 6 && slope == BADVAL;jj++)
                                { *xp = nextsing - backup*epsilon;
                                  deval(fprime,&slope);
                                  if(slope != BADVAL)
                                     break;
                                  backup *= 10;
                                }
                             *xp = savex;
                             if(slope != BADVAL && nextjump < mjumps && fabs(numjumps[nextjump]-nextsing)< epsilon)
                                { // this might be a one-sided singularity in a function defined by cases
                                  if( (slope > slopeone && fabs(y-ymax) > 0.2*(ymax-ymin))
                                      || (slope < -slopeone && fabs(y-ymin) > 0.2*(ymax-ymin))
                                    )
                                       slope = 0;  // prevents drawing in next few lines
                                }
                             if(slope > slopeone)  /* going up */
                                { line_to(nextsing,device->ycliplimit);  /* go offscreen up */
                                  lastx = nextsing; lasty = device->ycliplimit;
                                  ++nlines;
                                }
                             else if(slope < -slopeone)  /* going down */
                                { line_to(nextsing,device->ymin-device->ycliplimit); /*go offscreen down*/
                                   lastx = nextsing; lasty = device->ycliplimit;
                                   ++nlines;
                                }
                             /* else it's probably a spurious singularity
                                as in 1/x -1/(x-a) when a=0--why else wouldn't it be
                                achieving a slope of one in device coordinates when
                                we're only epsilon from the singularity?  It could
                                also be a one-sided singularity in a function defined
                                by cases.
                             */
                           }
                        /* Now on the other side of the singularity */
                        if(y == BADVAL)
                           lastsing = next;
                        if (y < ymax && y > ymin) /* then must draw*/
                           { savex = *xp;
                             *xp = nextsing + epsilon;
                             deval(fprime,&slope);
                             backup = 10;
                             for(jj = 0; jj < 6 && slope == BADVAL;jj++)
                                { *xp = nextsing + backup*epsilon;
                                  deval(fprime,&slope);
                                  if(slope != BADVAL)
                                     break;
                                  backup *= 10;
                                }
                             *xp = savex;
                             if(slope != BADVAL && nextjump < mjumps && fabs(numjumps[nextjump]-nextsing)< epsilon)
                                { // this might be a one-sided singularity in a function defined by cases
                                    if( (slope < -slopeone && fabs(y-ymax) > 0.2*(ymax-ymin))
                                      || (slope > slopeone && fabs(y-ymin) > 0.2*(ymax-ymin))
                                    )
                                     slope = 0;  // prevent drawing in next few lines
                                }
                             if(slope > slopeone)
                             /* then we are coming on from below */
                                { move_to(nextsing,device->ymin-device->ycliplimit);
                                  line_to(x,y);
                                  lastx = x; lasty = y;
                                  ++nlines;
                                }
                             else if( slope < -slopeone ) /* coming on from above */
                                { move_to(nextsing,device->ycliplimit);
                                  line_to(x,y);
                                  lastx = x; lasty = y;
                                  ++nlines;
                                }
                             /*else probably spurious, see above */
                             *xp += gap;
                           }
                        move_to(x,y);  /* harmless if we're already there, necessary if not */
                        ++next;  /* prepare for the next singularity crossing */
                        if(mjumps  && mjumps > nextjump && x > numjumps[nextjump])
                           {  /* in graphing a CASES term the jumps and
                                 singularities may coincide */
                              /* do we need to draw a filled circle? */
                              double savexp = *xp;
                              double y3;
                              *xp = numjumps[nextjump];
                              deval(f,&y3);
                              *xp = savexp;
                              if(y3 != BADVAL)
                                {  end_path();
                                   filled_circle(*xp,y3, 1.4*wradius);
                              /* But if it comes out BADVAL, we don't know whether
                                 or where to draw an open_filled_circle.  */
                                   begin_path();
                                }
                              ++nextjump;
                           }
                   }
                flag = newflag;                         /* now flag refers to point i */
                continue;

             case 2:   /* symbolic singularities with no existential parameters */
                if(flag || newflag)  /* one point or the other was bad */
                   { move_to(x,y);
                     flag = newflag;
                     continue;
                   }
                /* are we crossing a singularity? */
                singflag = 0;
                for(k=0;k<dim;k++)
                   { if(FUNCTOR(sing[k])== '=')
                        { left = ARG(0,sing[k]);
                          right = ARG(1,sing[k]);
                          deval(left,&t1);
                          deval(right,&t2);
                          t = t2-t1;
                          if ( !singflag && OPPOSITE_SIGN(t,singstore[k]))
                             /* we did cross a putative singularity--
                                but, maybe it doesn't satisfy one of the restrictions--
                                we have to check that before concluding that we
                                REALLY passed a singularity. */
                             { int derr = check_restrictions(g->srestrictions,g->dimsrestrictions);
                               if(derr)  /* failed to satisfy one or more restrictions */
                                  singflag = 0;   /* flunked one of the restrictions */
                               else
                                  { term temp;
                                    temp = make_term('=',2);
                                    ARGREP(temp,0,left);
                                    ARGREP(temp,1,right);
                                    if(k == 0)
                                       oldsing = xmin;
                                    savex = *xp;
                                    err = solve(temp,xvar,oldsing,x,&nextsing);
                                    *xp = savex;
                                    singflag = err ? 0 : 1;
                                    RELEASE(temp);
                                    break;
                                  }
                             }
                        }
                   }
                if(k < dim)
                   { singstore[k] = t;
                     oldsing = *xp;
                   }
                if(singflag)  /* we did cross a singularity */
                   goto crossing;
                else   /* are we crossing a jump? */
                   { if(nextjump < mjumps && x >= numjumps[nextjump])
                        break;
                     else
                        { line_to(x,y);
                          lastx = x; lasty = y;
                          ++nlines;
                        }
                   }
                flag = newflag;
                continue;

             default:  /* draw if both points are good.  If not, don't draw;
                   i.e., don't assume there was a singularity if you
                   couldn't calculate the singularities */
                if(nextjump < mjumps && x+epsilon > numjumps[nextjump])
                   break;  /* crossing a jump */
                if(!flag && !newflag)
                   { line_to(x,y);    /* then draw!  */
                     lastx = x; lasty = y;
                     ++nlines;
                   }
                else if(flag && !newflag)
                   { /* try backing up a little bit so as not to skip the first little
                        piece of the graph, e.g. on x^x just past the origin;
                        since (-2)^(-2) is defined, we are here instead of
                        at the first place where solve_edge is called. */
                        solve_edge(x-gap,x,xp,f,&y);
                        x = *xp;
                        move_to(x,y);
                   }
                else
                   move_to(x,y);
                flag = newflag;
                continue;
          }  /* close switch */
       /* Now we have to cross a jump, or we have a bad point */
       /* Put in the open or closed circles for this jump */
       xjump = numjumps[nextjump];
       ++nextjump;
       *xp = xjump;
       deval(f,&yjump);
       if(flag) /* probably oldx == the jump */
          { assert(oldy==BADVAL);
            *xp = xjump-epsilon;
            deval(f,&oldy);
            flag = 0;
          }
       if(newflag) /* probably x == the jump */
          { assert(y == BADVAL);
            *xp = x = xjump+epsilon;
            deval(f,&y);
            newflag = 0;
          }
       if(yjump == BADVAL)
          { /* Function not defined at the jump;
               draw open circles at both ends
            */
            end_path();
            open_filled_circle(xjump,oldy,wradius);
            open_filled_circle(xjump,y,wradius);
            begin_path();
          }
       else /* Function defined at the jump */
          { double hyp, costheta,y2;
            xplus = xjump + epsilon;
            xminus = xjump - epsilon;
            *xp = xminus;
            deval(f,&yminus);
            *xp = xplus;
            deval(f,&yplus);
            *xp = (x-wradius)/10.0;
            deval(f,&y2);
            if(fabs(yminus-yjump) > 2*hradius*device->ypixel)
               { end_path();
                 open_filled_circle(xjump,yminus,wradius);
                 begin_path();
                 move_to(lastx,lasty);  // path must begin with a move, not a line
                 /* Be sure we draw right up to the circle */
                 /* This must be done BEFORE drawing the circle,
                    or the line will stick into the circle visibly.  */
                 hyp = hypot(wradius/(10.0 *pix),(yminus-y2)/device->ypixel);
                 costheta = wradius /(10 * pix *hyp);
                 *xp = xjump - wradius *costheta;
                 deval(f,&y2);
                 if(y2 != BADVAL)
                    { line_to(*xp,y2);
                      // lastx = *xp; lasty = y2; Don't set them here!
                      x = *xp;
                    }
                 end_path();
                 open_filled_circle(xjump,yminus,wradius);
                 begin_path();
                 move_to(lastx,lasty);  // path must begin with a move, not a line
               }
            if(fabs(yplus-yjump) > 2*hradius*device->ypixel)
               {
                 end_path();
                 open_filled_circle(xjump,yplus,wradius);
                 begin_path();
                 move_to(lastx,lasty);  // path must begin with a move, not a line
               }
            if(fabs(yplus-yminus) <= 10*epsilon)
               { line_to(xjump,yjump);
                 lastx = xjump; lasty = yjump;
                 continue;  /* this isn't really a jump, e.g.  x = 0 in x floor(x) */
               }
            end_path();
            filled_circle(xjump,yjump,1.4*wradius);
            begin_path();
            move_to(lastx,lasty);  // path must begin with a move, not a line
          }

              /* Now, try not to write through any open circles you've just drawn */
       if(
          (nextjump >= mjumps || *xp < numjumps[nextjump]) &&
          (next >= msing || *xp < singularities[next]) &&
          fabs(yplus-yjump) > 2*hradius*device->ypixel  /* open circle at yplus */
         )
          { double y2;
            *xp = xjump + wradius;
            deval(f,&y2);
            if(y2 != BADVAL)
               { double hyp = hypot(wradius/pix,(y2-yplus)/device->ypixel);
                 double costheta = wradius/(pix *hyp);
                 *xp = xjump + wradius *costheta;
                 deval(f,&y2);
                 if(y2 != BADVAL)
                    move_to(*xp,y2);
                 else
                    move_to(x,y);
               }
            else
               move_to(x,y);
          }
       else if(newflag == 0)
          { if(fabs(yminus-yjump) > 2*hradius*device->ypixel)
               { x = xjump;  /* drawing line from the center of a closed circle */
                 *xp = xjump;
                 deval(f,&y);
               }
            move_to(x,y);
          }
     }  /*close for*/
  end_path();
  return nlines ? 0 : 4;
}
/*________________________________________________________________________*/
static unsigned  invert_color(unsigned  background)
 /* return white for a black background, etc. */
{ unsigned  ans = ~background;
  /* but this is correct only in the lower three bytes which are used for
     the color.  We need to correct byte 3, which should be 2  (to indicate
     don't use a hatched color). */
  ans =  0x00ffffffUL & ans;  /* sets bit 3 to zero */
  ans =  0x02000000UL | ans;  /* sets bit 3 to 2    */
  return ans;
}
/*________________________________________________________________________*/
void draw_riemann(graph *g)
/* draw Riemann sums, by drawing a lot of filled rectangles.
   For Riemann sums, there are two graph structures; the
   second one has graphtype ORDINARY_GRAPH and draws the curve.
   This one draws the rectangles only.  It computes the value of
   the Riemann sum (the signed area 'under' the rectangles) and
   stashes it in g->area.  It uses g->riemannflag to determine
   whether to use left, right, or midpoint Riemann sums.  It takes
   the number of intervals from g->nintervals, which is a term,
   so it can contain a parameter.  The left and right endpoints
   are taken from g->left and g->right.
*/
{ int i;
  double x1,x2,xmin,xmax,xeval,yeval,u;
  double *xp = (double *) ARGPTR(g->independent_variable);
  double dnintervals,gap,area;
  int nintervals;
  int type = g->riemannflag;  /* 0 = left, 1 = right, 2 = midpoint */
  term t = g->function;
  deval(g->nintervals,&dnintervals);
  if(dnintervals > 512.0)
     dnintervals = 512.0;  /* The maximum allowed */
  if(dnintervals < 1.0)
     dnintervals = 1.0;    /* The minimum allowed */
  nintervals = (int) dnintervals;
  set_graphpencolor(g->graphcolor);  /* for outlining the rectangles */
  set_graphbackgroundcolor(g->fillcolor);
        // invert_color(g->background));  /* white for a black background */
  xmin = g->left;
  xmax = g->right;
  if (ARITY(g->function) == 5)  // an integral_test problem
                                // then the gap is always 1 
     gap = 1.0;
  else
     gap = (xmax-xmin)/nintervals;
  area = 0.0;
  for(i=0;i<nintervals;i++)
     { x1 = xmin + i*gap;
       x2 = x1 + gap;
       xeval = (type == 2 ? x2 : type == 0 ? x1 : 0.5 *(x1+x2));
       *xp = xeval;
       deval(t,&yeval);
       /* Now avoid clipping problems:  Windows draws nonsense
       if you pass too-large pixel coordinates */
       if(yeval > g->ymax)
          u = g->ymax + (g->ymax-g->ymin);
       else if(yeval < g->ymin)
          u = g->ymin - (g->ymax-g->ymin);
       else
          u = yeval;
       filled_rect(x1,u,x2,0.0);
       area += yeval * (x2-x1);   /* it's the signed area actually */
     }
  set_graphbackgroundcolor(g->background);
  g->area = area;
}
/*________________________________________________________________________*/
void draw_simpson(graph *g)
/* draw a Simpson's rule approximation to an integral. As
   for Riemann sums, there are two graph structures; the
   second one has graphtype ORDINARY_GRAPH and draws the curve.
   This one draws the 'rectangles' (with curved tops) only.
   It computes the value of the signed area of the approximation and
   stashes it in g->area. It takes the number of intervals from
   g->nintervals, which is a term, so it can contain a parameter.
   The left and right endpoints are taken from g->left and g->right.
*/
{ int i,k;
  double x,x1,y1,x2,y2,x3,y3,xmin,xmax,h,xgap;
  double dnintervals,gap,area,a,b,c;
  int nboundarypts;
  double *xp = (double *) ARGPTR(g->independent_variable);
  double *xboundary;
  double *yboundary;
  int enough;  /* dimension of the xboundary, yboundary arrays */
  int nintervals;
  term t = g->function;
  deval(g->nintervals,&dnintervals);
  if(dnintervals > 512.0)
     dnintervals = 512.0;  /* The maximum allowed */
  if(dnintervals < 1.0)
     dnintervals = 1.0;    /* The minimum allowed */
  nintervals = (int) dnintervals;
  enough = g->numberofpoints/ nintervals + 10;
  xboundary = (double *) callocate(enough, sizeof(double));
  yboundary = (double *) callocate(enough, sizeof(double));
  if(!xboundary || !yboundary)
     nospace();
  set_graphbackgroundcolor(g->fillcolor);
  set_graphpencolor(g->graphcolor);  /* this color is used for outlining the rectangles */
  xmin = g->left;
  xmax = g->right;
  gap = (xmax-xmin)/nintervals;
  h = 0.5 * gap;
  area = 0.0;
  *xp = xmin;
  deval(t,&y3);  /* to start the loop */
  for(i=0;i<nintervals;i++)
     { x1 = xmin + i*gap;
       x3 = x1 + gap;
       x2 = 0.5*(x3+x1);
       y1 = y3;
       *xp = x2;
       deval(t,&y2);
       *xp = x3;
       deval(t,&y3);
       /* compute the coefficients of a parabola ax^2 + bx + c passing
          through (-h,y1), (0,y2), and (h,y3).  See Stewart page 459 */
       a = (y1 - 2*y2 + y3)/(2*h*h);
       b = (y3-y1)/gap;
       c = y2;
       /* Draw and fill the parabola-topped 'rectangle' */
       xboundary[0] = x3;   /* right side */
       yboundary[0] = y3;
       xboundary[1] = x3;
       yboundary[1] = 0.0;
       xboundary[2] = x1;
       yboundary[2] = 0.0;  /* bottom */
       xboundary[3] = x1;
       yboundary[3] = y1;   /* left side */
       nboundarypts = g->numberofpoints/nintervals +3;
       xgap = gap/(nboundarypts-3);
       for(k=4; k<nboundarypts;k++)
          { x = x1+ (k-3)*xgap - x2;
            xboundary[k] =  x2 + x;
            yboundary[k] = (a*x + b)*x + c;  /* only 2 multiplications */
          }
       polygon(xboundary,yboundary,nboundarypts);
       area += (y1 + 4*y2 + y3) * h/3;
     }
  set_graphbackgroundcolor(g->background);
  g->area = area;
  free2(xboundary);
  free2(yboundary);
}

/*________________________________________________________________________*/
void draw_trapezoid(graph *g)
/* draw a trapezoidal rule approximation to an integral.
   Similar to draw_simpson, above.
*/
{ int i;
  double x1,y1,x2,y2,xmin,xmax;
  double dnintervals,gap,area;
  int nboundarypts;
  double *xp = (double *) ARGPTR(g->independent_variable);
  double xboundary[4];  /* boundary of a trapezoid only needs 4 points */
  double yboundary[4];
  int nintervals;
  term t = g->function;
  deval(g->nintervals,&dnintervals);
  if(dnintervals > 512.0)
     dnintervals = 512.0;  /* The maximum allowed */
  if(dnintervals < 1.0)
     dnintervals = 1.0;    /* The minimum allowed */
  nintervals = (int) dnintervals;
  set_graphbackgroundcolor(g->fillcolor);
  set_graphpencolor(g->graphcolor);
  xmin = g->left;
  xmax = g->right;
  gap = (xmax-xmin)/nintervals;
  area = 0.0;
  *xp = xmin;
  deval(t,&y2);  /* to start the loop */
  for(i=0;i<nintervals;i++)
     { x1 = xmin + i*gap;
       x2 = x1 + gap;
       y1 = y2;
       *xp = x2;
       deval(t,&y2);
       /* Draw and fill the trapezoid */
       xboundary[0] = x2;   /* right side */
       yboundary[0] = y2;
       xboundary[1] = x2;
       yboundary[1] = 0.0;
       xboundary[2] = x1;
       yboundary[2] = 0.0;  /* bottom */
       xboundary[3] = x1;
       yboundary[3] = y1;   /* left side */
       nboundarypts = 4;
       polygon(xboundary,yboundary,nboundarypts);
       area += 0.5 * gap *(y1+y2);
     }
  set_graphbackgroundcolor(g->background);
  g->area = area;
}

/*__________________________________________________________________________*/
static void solve_edge(double a, double b, double *xp, term f, double *y)
/* deval(f,&z) has failed when *xp = a, and succeeded when *xp = b.
It's assumed that a < b. Find the "smallest value" of *xp between a and b such
that deval(f,&z) succeeds.  Return the corresponding value of f in *y.
*/
{ double z;
  double lastgoodx = b;
  double lastgoodz = *y;
  int count = 0;
  *xp = b;
  deval(f,&z);
  lastgoodz = z;
  if(z == BADVAL)
     assert(0);  /* precondition of the function call */
  start:
     *xp = a + 0.5 *(b-a);
     deval(f,&z);
     if(z == BADVAL)
        a = *xp;
     else
        { b = *xp;
          if(count >= 10)
             { *y = z;
               return;
             }
          lastgoodx = *xp;
          lastgoodz = z;
        }
     ++count;
     if(count >= 11)
        { *xp = lastgoodx;
          *y = lastgoodz;
          return;
        }
     goto start;
}


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