\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{\$SPAD/src/algebra acplot.spad}
\author{Clifton J. Williamson}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{package REALSOLV RealSolvePackage}
<<package REALSOLV RealSolvePackage>>=
)abbrev package REALSOLV RealSolvePackage

RealSolvePackage(): Exports == Implementation where
  ++ This package provides numerical solutions of systems of polynomial
  ++ equations for use in ACPLOT.
  I    ==> Integer
  IE   ==> IndexedExponents Symbol
  L    ==> List
  NF   ==> Float
  P    ==> Polynomial
  RN   ==> Fraction Integer
  SE   ==> Symbol
  RFI  ==> Fraction Polynomial Integer
  LIFT ==> PolynomialCategoryLifting(IE,SE,RN,P RN,RFI)
  SOLV ==> FloatingRealPackage Float

  Exports ==> with
    solve: (P RN,NF) -> L NF
      ++ solve(p,eps) finds the real zeroes of a
      ++ univariate rational polynomial p with precision eps.
    solve: (P I,NF) -> L NF
      ++ solve(p,eps) finds the real zeroes of a univariate
      ++ integer polynomial p with precision eps.
    realSolve: (L P I,L SE,NF) -> L L NF
      ++ realSolve(lp,lv,eps) = compute the list of the real
      ++ solutions of the list lp of polynomials with integer
      ++ coefficients with respect to the variables in lv,
      ++ with precision eps.

  Implementation ==> add

    prn2rfi: P RN -> RFI
    prn2rfi p ==
      map(x +-> x::RFI, x +-> (numer(x)::RFI)/(denom(x)::RFI), p)$LIFT

    pi2rfi: P I -> RFI
    pi2rfi p == p :: RFI

    solve(p:P RN,eps:NF) == realRoots(prn2rfi p,eps)$SOLV

    solve(p:P I,eps:NF) ==
      realRoots(p :: RFI,eps)$SOLV

    realSolve(lp,lv,eps) ==
      realRoots(map(pi2rfi,lp)$ListFunctions2(P I,RFI),lv,eps)$SOLV

@
\section{domain ACPLOT PlaneAlgebraicCurvePlot}
<<domain ACPLOT PlaneAlgebraicCurvePlot>>=
--% PlaneAlgebraicCurvePlot
++ Plot a NON-SINGULAR plane algebraic curve p(x,y) = 0.
++ Author: Clifton J. Williamson
++ Date Created: Fall 1988
++ Date Last Updated: 27 April 1990
++ Keywords: algebraic curve, non-singular, plot
++ Examples:
++ References:

)abbrev domain ACPLOT PlaneAlgebraicCurvePlot

PlaneAlgebraicCurvePlot():Exports == Implementation where
    B           ==> Boolean
    OUT         ==> OutputForm
    SE          ==> Symbol
    L           ==> List
    SEG         ==> Segment
    I           ==> Integer
    PI          ==> PositiveInteger
    RN          ==> Fraction Integer
    NF          ==> Float
    SF          ==> DoubleFloat
    P           ==> Polynomial
    UP          ==> UnivariatePolynomial
    SUP         ==> SparseUnivariatePolynomial
    FR          ==> Factored
    Pt          ==> Point DoubleFloat
    BoundaryPts ==> Record(left:   L Pt,_
                           right:  L Pt,_
                           bottom: L Pt,_
                           top:    L Pt)
    NewPtInfo   ==> Record(newPt: Pt,_
                           type:  String)
    Corners     ==> Record(minXVal: SF,_
                           maxXVal: SF,_
                           minYVal: SF,_
                           maxYVal: SF)
    kinte       ==> solve$RealSolvePackage()
    rsolve      ==> realSolve$RealSolvePackage()

    Exports ==> PlottablePlaneCurveCategory with

      makeSketch:(P I,SE,SE,SEG RN,SEG RN) -> %
        ++ makeSketch(p,x,y,a..b,c..d) creates an ACPLOT of the
        ++ curve \spad{p = 0} in the region {\em a <= x <= b, c <= y <= d}.
        ++ More specifically, 'makeSketch' plots a non-singular algebraic curve
        ++ \spad{p = 0} in an rectangular region {\em xMin <= x <= xMax},
        ++ {\em yMin <= y <= yMax}. The user inputs
        ++ \spad{makeSketch(p,x,y,xMin..xMax,yMin..yMax)}.
        ++ Here p is a polynomial in the variables x and y with
        ++ integer coefficients (p belongs to the domain
        ++ \spad{Polynomial Integer}). The case
        ++ where p is a polynomial in only one of the variables is
        ++ allowed.  The variables x and y are input to specify the
        ++ the coordinate axes.  The horizontal axis is the x-axis and
        ++ the vertical axis is the y-axis.  The rational numbers
        ++ xMin,...,yMax specify the boundaries of the region in
        ++ which the curve is to be plotted.
      refine:(%,SF) -> %
        ++ refine(p,x) \undocumented{}

    Implementation ==> add

      import PointPackage DoubleFloat
      import Plot
      import RealSolvePackage

      singValBetween?:(SF,SF,L SF) -> B
      segmentInfo:(SF -> SF,SF,SF,L SF,L SF,L SF,SF,SF) -> _
             Record(seg:SEG SF,left: SF,lowerVals: L SF,upperVals:L SF)
      swapCoords:Pt -> Pt
      samePlottedPt?:(Pt,Pt) -> B
      findPtOnList:(Pt,L Pt) -> Union(Pt,"failed")
      makeCorners:(SF,SF,SF,SF) -> Corners
      getXMin: Corners -> SF
      getXMax: Corners -> SF
      getYMin: Corners -> SF
      getYMax: Corners -> SF
      SFPolyToUPoly:P SF -> SUP SF
      RNPolyToUPoly:P RN -> SUP RN
      coerceCoefsToSFs:P I -> P SF
      coerceCoefsToRNs:P I -> P RN
      RNtoSF:RN -> SF
      RNtoNF:RN -> NF
      SFtoNF:SF -> NF
      listPtsOnHorizBdry:(P RN,SE,RN,NF,NF) -> L Pt
      listPtsOnVertBdry:(P RN,SE,RN,NF,NF) -> L Pt
      listPtsInRect:(L L NF,NF,NF,NF,NF) -> L Pt
      ptsSuchThat?:(L L NF,L NF -> B) -> B
      inRect?:(L NF,NF,NF,NF,NF) -> B
      onHorzSeg?:(L NF,NF,NF,NF) -> B
      onVertSeg?:(L NF,NF,NF,NF) -> B
      newX:(L L NF,L L NF,NF,NF,NF,RN,RN) -> RN
      newY:(L L NF,L L NF,NF,NF,NF,RN,RN) -> RN
      makeOneVarSketch:(P I,SE,SE,RN,RN,RN,RN,SE) -> %
      makeLineSketch:(P I,SE,SE,RN,RN,RN,RN) -> %
      makeRatFcnSketch:(P I,SE,SE,RN,RN,RN,RN,SE) -> %
      makeGeneralSketch:(P I,SE,SE,RN,RN,RN,RN) -> %
      traceBranches:(P SF,P SF,P SF,SE,SE,Corners,SF,SF,PI,_
                                    L Pt,BoundaryPts) -> L L Pt
      dummyFirstPt:(Pt,P SF,P SF,SE,SE,L Pt,L Pt,L Pt,L Pt) -> Pt
      listPtsOnSegment:(P SF,P SF,P SF,SE,SE,Pt,Pt,Corners,_
                                    SF,SF,PI,L Pt,L Pt) -> L L Pt
      listPtsOnLoop:(P SF,P SF,P SF,SE,SE,Pt,Corners,_
                                    SF,SF,PI,L Pt,L Pt) -> L L Pt
      computeNextPt:(P SF,P SF,P SF,SE,SE,Pt,Pt,Corners,_
                                 SF,SF,PI,L Pt,L Pt) -> NewPtInfo
      newtonApprox:(SUP SF, SF, SF, PI) -> Union(SF, "failed")

--% representation

      Rep := Record(poly    : P I,_
                    xVar    : SE,_
                    yVar    : SE,_
                    minXVal : RN,_
                    maxXVal : RN,_
                    minYVal : RN,_
                    maxYVal : RN,_
                    bdryPts : BoundaryPts,_
                    hTanPts : L Pt,_
                    vTanPts : L Pt,_
                    branches: L L Pt)

--% global constants

      EPSILON : NF := 0.000001 -- precision to which realSolve finds roots
      PLOTERR : SF := float(1,-3,10)
        -- maximum allowable difference in each coordinate when
        -- determining if 2 plotted points are equal

--% global flags

      NADA   : String := "nothing in particular"
      BDRY   : String := "boundary point"
      CRIT   : String := "critical point"
      BOTTOM : String := "bottom"
      TOP    : String := "top"

--% hacks

      NFtoSF: NF -> SF
      NFtoSF x == 0 + convert(x)$NF

--% points
      makePt: (SF,SF) -> Pt
      makePt(xx,yy) == point(l : L SF := [xx,yy])

      swapCoords(pt) == makePt(yCoord pt,xCoord pt)

      samePlottedPt?(p0,p1) ==
      -- determines if p1 lies in a square with side 2 PLOTERR
      -- centered at p0
        x0 := xCoord p0; y0 := yCoord p0
        x1 := xCoord p1; y1 := yCoord p1
        (abs(x1-x0) < PLOTERR) and (abs(y1-y0) < PLOTERR)

      findPtOnList(pt,pointList) ==
        for point in pointList repeat
          samePlottedPt?(pt,point) => return point
        "failed"

--% corners

      makeCorners(xMinSF,xMaxSF,yMinSF,yMaxSF) ==
        [xMinSF,xMaxSF,yMinSF,yMaxSF]

      getXMin(corners) == corners.minXVal
      getXMax(corners) == corners.maxXVal
      getYMin(corners) == corners.minYVal
      getYMax(corners) == corners.maxYVal

--% coercions

      SFPolyToUPoly(p) ==
      -- 'p' is of type Polynomial, but has only one variable
        zero? p => 0
        monomial(leadingCoefficient p,totalDegree p) +
           SFPolyToUPoly(reductum p)

      RNPolyToUPoly(p) ==
      -- 'p' is of type Polynomial, but has only one variable
        zero? p => 0
        monomial(leadingCoefficient p,totalDegree p) +
            RNPolyToUPoly(reductum p)

      coerceCoefsToSFs(p) ==
      -- coefficients of 'p' are coerced to be DoubleFloat's
        map(coerce,p)$PolynomialFunctions2(I,SF)

      coerceCoefsToRNs(p) ==
      -- coefficients of 'p' are coerced to be DoubleFloat's
        map(coerce,p)$PolynomialFunctions2(I,RN)

      RNtoSF(r) == coerce(r)@SF
      RNtoNF(r) == coerce(r)@NF
      SFtoNF(x) == convert(x)@NF

--% computation of special points

      listPtsOnHorizBdry(pRN,y,y0,xMinNF,xMaxNF) ==
      -- strict inequality here: corners on vertical boundary
        pointList : L Pt := nil()
        ySF := RNtoSF(y0)
        f := eval(pRN,y,y0)
        roots : L NF := kinte(f,EPSILON)
        for root in roots repeat
          if (xMinNF < root) and (root < xMaxNF) then
            pointList := cons(makePt(NFtoSF root, ySF), pointList)
        pointList

      listPtsOnVertBdry(pRN,x,x0,yMinNF,yMaxNF) ==
        pointList : L Pt := nil()
        xSF := RNtoSF(x0)
        f := eval(pRN,x,x0)
        roots : L NF := kinte(f,EPSILON)
        for root in roots repeat
          if (yMinNF <= root) and (root <= yMaxNF) then
            pointList := cons(makePt(xSF, NFtoSF root), pointList)
        pointList

      listPtsInRect(points,xMin,xMax,yMin,yMax) ==
        pointList : L Pt := nil()
        for point in points repeat
          xx := first point; yy := second point
          if (xMin<=xx) and (xx<=xMax) and (yMin<=yy) and (yy<=yMax) then
            pointList := cons(makePt(NFtoSF xx,NFtoSF yy),pointList)
        pointList

      ptsSuchThat?(points,pred) ==
        for point in points repeat
          if pred point then return true
        false

      inRect?(point,xMinNF,xMaxNF,yMinNF,yMaxNF) ==
        xx := first point; yy := second point
        xMinNF <= xx and xx <= xMaxNF and yMinNF <= yy and yy <= yMaxNF

      onHorzSeg?(point,xMinNF,xMaxNF,yNF) ==
        xx := first point; yy := second point
        yy = yNF and xMinNF <= xx and xx <= xMaxNF

      onVertSeg?(point,yMinNF,yMaxNF,xNF) ==
        xx := first point; yy := second point
        xx = xNF and yMinNF <= yy and yy <= yMaxNF

      newX(vtanPts,singPts,yMinNF,yMaxNF,xNF,xRN,horizInc) ==
        xNewNF := xNF + RNtoNF horizInc
        xRtNF := max(xNF,xNewNF); xLftNF := min(xNF,xNewNF)
        foo : L NF -> B := x +-> inRect?(x, xLftNF, xRtNF, yMinNF, yMaxNF)
        ptsSuchThat?(singPts,foo) =>
          newX(vtanPts,singPts,yMinNF,yMaxNF,xNF,xRN,horizInc/2::RN)
        goo : L NF -> B := x +-> onVertSeg?(x, yMinNF, yMaxNF, xNewNF)
        ptsSuchThat?(vtanPts,goo) =>
          newX(vtanPts,singPts,yMinNF,yMaxNF,xNF,xRN,horizInc/2::RN)
        xRN + horizInc

      newY(htanPts,singPts,xMinNF,xMaxNF,yNF,yRN,vertInc) ==
        yNewNF := yNF + RNtoNF vertInc
        yTopNF := max(yNF,yNewNF); yBotNF := min(yNF,yNewNF)
        foo : L NF -> B := x +-> inRect?(x, xMinNF, xMaxNF, yBotNF, yTopNF)
        ptsSuchThat?(singPts,foo) =>
          newY(htanPts,singPts,xMinNF,xMaxNF,yNF,yRN,vertInc/2::RN)
        goo : L NF -> B := x +-> onHorzSeg?(x, xMinNF, xMaxNF, yNewNF)
        ptsSuchThat?(htanPts,goo) =>
          newY(htanPts,singPts,xMinNF,xMaxNF,yNF,yRN,vertInc/2::RN)
        yRN + vertInc

--% creation of sketches

      makeSketch(p,x,y,xRange,yRange) ==
        xMin := lo xRange; xMax := hi xRange
        yMin := lo yRange; yMax := hi yRange
        -- test input for consistency
        xMax <= xMin =>
          error "makeSketch: bad range for first variable"
        yMax <= yMin =>
          error "makeSketch: bad range for second variable"
        varList := variables p
        # varList > 2 =>
          error "makeSketch: polynomial in more than 2 variables"
        # varList = 0 =>
          error "makeSketch: constant polynomial"
        -- polynomial in 1 variable
        # varList = 1 =>
          (not member?(x,varList)) and (not member?(y,varList)) =>
            error "makeSketch: bad variables"
          makeOneVarSketch(p,x,y,xMin,xMax,yMin,yMax,first varList)
        -- polynomial in 2 variables
        (not member?(x,varList)) or (not member?(y,varList)) =>
          error "makeSketch: bad variables"
        totalDegree p = 1 =>
          makeLineSketch(p,x,y,xMin,xMax,yMin,yMax)
        -- polynomial is linear in one variable
        -- y is a rational function of x
        degree(p,y) = 1 =>
          makeRatFcnSketch(p,x,y,xMin,xMax,yMin,yMax,y)
        -- x is a rational function of y
        degree(p,x) = 1 =>
          makeRatFcnSketch(p,x,y,xMin,xMax,yMin,yMax,x)
        -- the general case
        makeGeneralSketch(p,x,y,xMin,xMax,yMin,yMax)

--% special cases

      makeOneVarSketch(p,x,y,xMin,xMax,yMin,yMax,var) ==
      -- the case where 'p' is a polynomial in only one variable
      -- the graph consists of horizontal or vertical lines
        if var = x then
          minVal := RNtoNF xMin
          maxVal := RNtoNF xMax
        else
          minVal := RNtoNF yMin
          maxVal := RNtoNF yMax
        lf : L Pt := nil(); rt : L Pt := nil()
        bt : L Pt := nil(); tp : L Pt := nil()
        htans : L Pt := nil(); vtans : L Pt := nil()
        bran : L L Pt := nil()
        roots := kinte(p,EPSILON)
        sketchRoots : L SF := nil()
        for root in roots repeat
          if (minVal <= root) and (root <= maxVal) then
              sketchRoots := cons(NFtoSF root,sketchRoots)
        null sketchRoots =>
          [p,x,y,xMin,xMax,yMin,yMax,[lf,rt,bt,tp],htans,vtans,bran]
        if var = x then
          yMinSF := RNtoSF yMin; yMaxSF := RNtoSF yMax
          for rootSF in sketchRoots repeat
              tp := cons(pt1 := makePt(rootSF,yMaxSF),tp)
              bt := cons(pt2 := makePt(rootSF,yMinSF),bt)
              branch : L Pt := [pt1,pt2]
              bran := cons(branch,bran)
        else
          xMinSF := RNtoSF xMin; xMaxSF := RNtoSF xMax
          for rootSF in sketchRoots repeat
              rt := cons(pt1 := makePt(xMaxSF,rootSF),rt)
              lf := cons(pt2 := makePt(xMinSF,rootSF),lf)
              branch : L Pt := [pt1,pt2]
              bran := cons(branch,bran)
        [p,x,y,xMin,xMax,yMin,yMax,[lf,rt,bt,tp],htans,vtans,bran]

      makeLineSketch(p,x,y,xMin,xMax,yMin,yMax) ==
      -- the case where p(x,y) = a x + b y + c with a ~= 0, b ~= 0
      -- this is a line which is neither vertical nor horizontal
        xMinSF := RNtoSF xMin; xMaxSF := RNtoSF xMax
        yMinSF := RNtoSF yMin; yMaxSF := RNtoSF yMax
        -- determine the coefficients a, b, and c
        a := ground(coefficient(p,x,1)) :: SF
        b := ground(coefficient(p,y,1)) :: SF
        c := ground(coefficient(coefficient(p,x,0),y,0)) :: SF
        lf : L Pt := nil(); rt : L Pt := nil()
        bt : L Pt := nil(); tp : L Pt := nil()
        htans : L Pt := nil(); vtans : L Pt := nil()
        branch : L Pt := nil(); bran : L L Pt := nil()
        -- compute x coordinate of point on line with y = yMin
        xBottom := (- b*yMinSF - c)/a
        -- compute x coordinate of point on line with y = yMax
        xTop    := (- b*yMaxSF - c)/a
        -- compute y coordinate of point on line with x = xMin
        yLeft   := (- a*xMinSF - c)/b
        -- compute y coordinate of point on line with x = xMax
        yRight  := (- a*xMaxSF - c)/b
        -- determine which of the above 4 points are in the region
        -- to be plotted and list them as a branch
        if (xMinSF < xBottom) and (xBottom < xMaxSF) then
            bt := cons(pt := makePt(xBottom,yMinSF),bt)
            branch := cons(pt,branch)
        if (xMinSF < xTop) and (xTop < xMaxSF) then
            tp := cons(pt := makePt(xTop,yMaxSF),tp)
            branch := cons(pt,branch)
        if (yMinSF <= yLeft) and (yLeft <= yMaxSF) then
            lf := cons(pt := makePt(xMinSF,yLeft),lf)
            branch := cons(pt,branch)
        if (yMinSF <= yRight) and (yRight <= yMaxSF) then
            rt := cons(pt := makePt(xMaxSF,yRight),rt)
            branch := cons(pt,branch)
        bran := cons(branch,bran)
        [p,x,y,xMin,xMax,yMin,yMax,[lf,rt,bt,tp],htans,vtans,bran]

      singValBetween?(xCurrent,xNext,xSingList) ==
        for xVal in xSingList repeat
          (xCurrent < xVal) and (xVal < xNext) => return true
        false

      segmentInfo(f,lo,hi,botList,topList,singList,minSF,maxSF) ==
        repeat
          -- 'current' is the smallest element of 'topList' and 'botList'
          -- 'currentFrom' records the list from which it was taken
          if null topList then
            if null botList then
              return [segment(lo,hi),hi,nil(),nil()]
            else
              current := first botList
              botList := rest  botList
              currentFrom := BOTTOM
          else
            if null botList then
              current := first topList
              topList := rest  topList
              currentFrom := TOP
            else
              bot := first botList
              top := first topList
              if bot < top then
                current := bot
                botList := rest botList
                currentFrom := BOTTOM
              else
                current := top
                topList := rest topList
                currentFrom := TOP
          -- 'nxt' is the next smallest element of 'topList'
          --  and 'botList'
          -- 'nextFrom' records the list from which it was taken
          if null topList then
            if null botList then
              return [segment(lo,hi),hi,nil(),nil()]
            else
              nxt := first botList
              botList := rest botList
              nextFrom := BOTTOM
          else
            if null botList then
              nxt := first topList
              topList := rest topList
              nextFrom := TOP
            else
              bot := first botList
              top := first topList
              if bot < top then
                nxt := bot
                botList := rest botList
                nextFrom := BOTTOM
              else
                nxt := top
                topList := rest topList
                nextFrom := TOP
          if currentFrom = nextFrom then
            if singValBetween?(current,nxt,singList) then
              return [segment(lo,current),nxt,botList,topList]
            else
              val := f((nxt - current)/2::SF)
              if (val <= minSF) or (val >= maxSF) then
                return [segment(lo,current),nxt,botList,topList]
          else
            if singValBetween?(current,nxt,singList) then
              return [segment(lo,current),nxt,botList,topList]

      makeRatFcnSketch(p,x,y,xMin,xMax,yMin,yMax,depVar) ==
      -- the case where p(x,y) is linear in x or y
      -- Thus, one variable is a rational function of the other.
      -- Therefore, we may use the 2-dimensional function plotting
      -- package.  The only problem is determining the intervals on
      -- on which the function is to be plotted.
      --!! corners: e.g. upper left corner is on graph with y' > 0
        factoredP := p :: FR P I
        numberOfFactors(factoredP) > 1 =>
            error "reducible polynomial"  --!! sketch each factor
        dpdx := differentiate(p,x)
        dpdy := differentiate(p,y)
        pRN := coerceCoefsToRNs p
        xMinSF := RNtoSF xMin; xMaxSF := RNtoSF xMax
        yMinSF := RNtoSF yMin; yMaxSF := RNtoSF yMax
        xMinNF := RNtoNF xMin; xMaxNF := RNtoNF xMax
        yMinNF := RNtoNF yMin; yMaxNF := RNtoNF yMax
        -- 'p' is of degree 1 in the variable 'depVar'.
        -- Thus, 'depVar' is a rational function of the other variable.
        num := -coefficient(p,depVar,0)
        den :=  coefficient(p,depVar,1)
        numUPolySF := SFPolyToUPoly(coerceCoefsToSFs(num))
        denUPolySF := SFPolyToUPoly(coerceCoefsToSFs(den))
        -- this is the rational function
        f  : SF -> SF := s +-> elt(numUPolySF, s)/elt(denUPolySF, s)
        -- values of the dependent and independent variables
        if depVar = x then
          indVarMin   := yMin;   indVarMax   := yMax
          indVarMinNF := yMinNF; indVarMaxNF := yMaxNF
          indVarMinSF := yMinSF; indVarMaxSF := yMaxSF
          depVarMin   := xMin;   depVarMax   := xMax
          depVarMinSF := xMinSF; depVarMaxSF := xMaxSF
        else
          indVarMin   := xMin;   indVarMax   := xMax
          indVarMinNF := xMinNF; indVarMaxNF := xMaxNF
          indVarMinSF := xMinSF; indVarMaxSF := xMaxSF
          depVarMin   := yMin;   depVarMax   := yMax
          depVarMinSF := yMinSF; depVarMaxSF := yMaxSF
        -- Create lists of critical points.
        htanPts := rsolve([p,dpdx],[x,y],EPSILON)
        vtanPts := rsolve([p,dpdy],[x,y],EPSILON)
        htans := listPtsInRect(htanPts,xMinNF,xMaxNF,yMinNF,yMaxNF)
        vtans := listPtsInRect(vtanPts,xMinNF,xMaxNF,yMinNF,yMaxNF)
        -- Create lists which will contain boundary points.
        lf : L Pt := nil(); rt : L Pt := nil()
        bt : L Pt := nil(); tp : L Pt := nil()
        -- Determine values of the independent variable at the which
        -- the rational function has a pole as well as the values of
        -- the independent variable for which there is a point on the
        -- upper or lower boundary.
        singList : L SF :=
          roots : L NF := kinte(den,EPSILON)
          outList : L SF := nil()
          for root in roots repeat
            if (indVarMinNF < root) and (root < indVarMaxNF) then
              outList := cons(NFtoSF root,outList)
          sort((x, y) +-> x < y, outList)
        topList : L SF :=
          roots : L NF := kinte(eval(pRN,depVar,depVarMax),EPSILON)
          outList : L SF := nil()
          for root in roots repeat
            if (indVarMinNF < root) and (root < indVarMaxNF) then
              outList := cons(NFtoSF root,outList)
          sort((x, y) +-> x < y, outList)
        botList : L SF :=
          roots : L NF := kinte(eval(pRN,depVar,depVarMin),EPSILON)
          outList : L SF := nil()
          for root in roots repeat
            if (indVarMinNF < root) and (root < indVarMaxNF) then
              outList := cons(NFtoSF root,outList)
          sort((x, y) +-> x < y, outList)
        -- We wish to determine if the graph has points on the 'left'
        -- and 'right' boundaries, so we compute the value of the
        -- rational function at the lefthand and righthand values of
        -- the dependent variable.  If the function has a singularity
        -- on the left or right boundary, then 'leftVal' or 'rightVal'
        -- is given a dummy valuewhich will convince the program that
        -- there is no point on the left or right boundary.
        denUPolyRN := RNPolyToUPoly(coerceCoefsToRNs(den))
        if elt(denUPolyRN,indVarMin) = 0$RN then
          leftVal  := depVarMinSF - (abs(depVarMinSF) + 1$SF)
        else
          leftVal  := f(indVarMinSF)
        if elt(denUPolyRN,indVarMax) = 0$RN then
          rightVal := depVarMinSF - (abs(depVarMinSF) + 1$SF)
        else
          rightVal := f(indVarMaxSF)
        -- Now put boundary points on the appropriate lists.
        if depVar = x then
          if (xMinSF < leftVal) and (leftVal < xMaxSF) then
            bt := cons(makePt(leftVal,yMinSF),bt)
          if (xMinSF < rightVal) and (rightVal < xMaxSF) then
            tp := cons(makePt(rightVal,yMaxSF),tp)
          for val in botList repeat
            lf := cons(makePt(xMinSF,val),lf)
          for val in topList repeat
            rt := cons(makePt(xMaxSF,val),rt)
        else
          if (yMinSF < leftVal) and (leftVal < yMaxSF) then
            lf := cons(makePt(xMinSF,leftVal),lf)
          if (yMinSF < rightVal) and (rightVal < yMaxSF) then
            rt := cons(makePt(xMaxSF,rightVal),rt)
          for val in botList repeat
            bt := cons(makePt(val,yMinSF),bt)
          for val in topList repeat
            tp := cons(makePt(val,yMaxSF),tp)
        bran : L L Pt := nil()
        -- Determine segments on which the rational function is to
        -- be plotted.
        if (depVarMinSF < leftVal) and (leftVal < depVarMaxSF) then
          lo := indVarMinSF
        else
          if null topList then
            if null botList then
              return [p,x,y,xMin,xMax,yMin,yMax,[lf,rt,bt,tp],_
                                              htans,vtans,bran]
            else
              lo := first botList
              botList := rest botList
          else
            if null botList then
              lo := first topList
              topList := rest topList
            else
              bot := first botList
              top := first topList
              if bot < top then
                lo := bot
                botList := rest botList
              else
                lo := top
                topList := rest topList
        hi := 0$SF  -- @#$%^&* compiler
        if (depVarMinSF < rightVal) and (rightVal < depVarMaxSF) then
          hi := indVarMaxSF
        else
          if null topList then
            if null botList then
              error "makeRatFcnSketch: plot domain"
            else
              hi := last botList
              botList := remove(hi,botList)
          else
            if null botList then
              hi := last topList
              topList := remove(hi,topList)
            else
              bot := last botList
              top := last topList
              if bot > top then
                hi := bot
                botList := remove(hi,botList)
              else
                hi := top
                topList := remove(hi,topList)
        if (depVar = x) then
          (minSF := xMinSF; maxSF := xMaxSF)
        else
          (minSF := yMinSF; maxSF := yMaxSF)
        segList : L SEG SF := nil()
        repeat
          segInfo := segmentInfo(f,lo,hi,botList,topList,singList,_
                                      minSF,maxSF)
          segList := cons(segInfo.seg,segList)
          lo := segInfo.left
          botList := segInfo.lowerVals
          topList := segInfo.upperVals
          if lo = hi then break
        for segment in segList repeat
          RFPlot : Plot := plot(f,segment)
          curve := first(listBranches(RFPlot))
          if depVar = y then
            bran := cons(curve,bran)
          else
            bran := cons(map(swapCoords,curve),bran)
        [p,x,y,xMin,xMax,yMin,yMax,[lf,rt,bt,tp],htans,vtans,bran]

--% the general case

      makeGeneralSketch(pol,x,y,xMin,xMax,yMin,yMax) ==
        --!! corners of region should not be on curve
        --!! enlarge region if necessary
        factoredPol := pol :: FR P I
        numberOfFactors(factoredPol) > 1 =>
            error "reducible polynomial"  --!! sketch each factor
        p := nthFactor(factoredPol,1)
        dpdx := differentiate(p,x); dpdy := differentiate(p,y)
        xMinNF := RNtoNF xMin; xMaxNF := RNtoNF xMax
        yMinNF := RNtoNF yMin; yMaxNF := RNtoNF yMax
        -- compute singular points; error if singularities in region
        singPts := rsolve([p,dpdx,dpdy],[x,y],EPSILON)
        foo : L NF -> B := s +-> inRect?(s, xMinNF, xMaxNF, yMinNF, yMaxNF)
        ptsSuchThat?(singPts,foo) =>
          error "singular pts in region of sketch"
        -- compute critical points
        htanPts := rsolve([p,dpdx],[x,y],EPSILON)
        vtanPts := rsolve([p,dpdy],[x,y],EPSILON)
        critPts := append(htanPts,vtanPts)
        -- if there are critical points on the boundary, then enlarge
        -- the region, but be sure that the new region does not contain
        -- any singular points
        hInc : RN := (1/20) * (xMax - xMin)
        vInc : RN := (1/20) * (yMax - yMin)
        foo : L NF -> B := s +-> onVertSeg?(s, yMinNF, yMaxNF, xMinNF)
        if ptsSuchThat?(critPts,foo) then
          xMin := newX(critPts,singPts,yMinNF,yMaxNF,xMinNF,xMin,-hInc)
          xMinNF := RNtoNF xMin
        foo : L NF -> B := s +-> onVertSeg?(s, yMinNF, yMaxNF, xMaxNF)
        if ptsSuchThat?(critPts,foo) then
          xMax := newX(critPts,singPts,yMinNF,yMaxNF,xMaxNF,xMax,hInc)
          xMaxNF := RNtoNF xMax
        foo : L NF -> B := s +-> onHorzSeg?(s, xMinNF, xMaxNF, yMinNF)
        if ptsSuchThat?(critPts,foo) then
          yMin := newY(critPts,singPts,xMinNF,xMaxNF,yMinNF,yMin,-vInc)
          yMinNF := RNtoNF yMin
        foo : L NF -> B := s +-> onHorzSeg?(s, xMinNF, xMaxNF, yMaxNF)
        if ptsSuchThat?(critPts,foo) then
          yMax := newY(critPts,singPts,xMinNF,xMaxNF,yMaxNF,yMax,vInc)
          yMaxNF := RNtoNF yMax
        htans := listPtsInRect(htanPts,xMinNF,xMaxNF,yMinNF,yMaxNF)
        vtans := listPtsInRect(vtanPts,xMinNF,xMaxNF,yMinNF,yMaxNF)
        crits := append(htans,vtans)
        -- conversions to DoubleFloats
        xMinSF := RNtoSF xMin; xMaxSF := RNtoSF xMax
        yMinSF := RNtoSF yMin; yMaxSF := RNtoSF yMax
        corners := makeCorners(xMinSF,xMaxSF,yMinSF,yMaxSF)
        pSF := coerceCoefsToSFs p
        dpdxSF := coerceCoefsToSFs dpdx
        dpdySF := coerceCoefsToSFs dpdy
        delta := min((xMaxSF - xMinSF)/25,(yMaxSF - yMinSF)/25)
        err := min(delta/100,PLOTERR/100)
        bound : PI := 10
        -- compute points on the boundary
        pRN := coerceCoefsToRNs(p)
        lf : L Pt := listPtsOnVertBdry(pRN,x,xMin,yMinNF,yMaxNF)
        rt : L Pt := listPtsOnVertBdry(pRN,x,xMax,yMinNF,yMaxNF)
        bt : L Pt := listPtsOnHorizBdry(pRN,y,yMin,xMinNF,xMaxNF)
        tp : L Pt := listPtsOnHorizBdry(pRN,y,yMax,xMinNF,xMaxNF)
        bdPts : BoundaryPts := [lf,rt,bt,tp]
        bran := traceBranches(pSF,dpdxSF,dpdySF,x,y,corners,delta,err,_
                               bound,crits,bdPts)
        [p,x,y,xMin,xMax,yMin,yMax,bdPts,htans,vtans,bran]

      refine(plot,stepFraction) ==
        p := plot.poly; x := plot.xVar; y := plot.yVar
        dpdx := differentiate(p,x); dpdy := differentiate(p,y)
        pSF := coerceCoefsToSFs p
        dpdxSF := coerceCoefsToSFs dpdx
        dpdySF := coerceCoefsToSFs dpdy
        xMin := plot.minXVal; xMax := plot.maxXVal
        yMin := plot.minYVal; yMax := plot.maxYVal
        xMinSF := RNtoSF xMin; xMaxSF := RNtoSF xMax
        yMinSF := RNtoSF yMin; yMaxSF := RNtoSF yMax
        corners := makeCorners(xMinSF,xMaxSF,yMinSF,yMaxSF)
        pSF := coerceCoefsToSFs p
        dpdxSF := coerceCoefsToSFs dpdx
        dpdySF := coerceCoefsToSFs dpdy
        delta :=
          stepFraction * min((xMaxSF - xMinSF)/25,(yMaxSF - yMinSF)/25)
        err := min(delta/100,PLOTERR/100)
        bound : PI := 10
        crits := append(plot.hTanPts,plot.vTanPts)
        bdPts := plot.bdryPts
        bran := traceBranches(pSF,dpdxSF,dpdySF,x,y,corners,delta,err,_
                               bound,crits,bdPts)
        htans := plot.hTanPts; vtans := plot.vTanPts
        [p,x,y,xMin,xMax,yMin,yMax,bdPts,htans,vtans,bran]

      traceBranches(pSF,dpdxSF,dpdySF,x,y,corners,delta,err,bound,_
                        crits,bdPts) ==
        -- for boundary points, trace curve from boundary to boundary
        -- add the branch to the list of branches
        -- update list of boundary points by deleting first and last
        -- points on this branch
        -- update list of critical points by deleting any critical
        -- points which were plotted
        lf := bdPts.left; rt := bdPts.right
        tp := bdPts.top ; bt := bdPts.bottom
        bdry := append(append(lf,rt),append(bt,tp))
        bran : L L Pt := nil()
        while not null bdry repeat
          pt := first bdry
          p0 := dummyFirstPt(pt,dpdxSF,dpdySF,x,y,lf,rt,bt,tp)
          segInfo := listPtsOnSegment(pSF,dpdxSF,dpdySF,x,y,p0,pt,_
                           corners,delta,err,bound,crits,bdry)
          bran  := cons(first segInfo,bran)
          crits := second segInfo
          bdry  := third segInfo
        -- trace loops beginning and ending with critical points
        -- add the branch to the list of branches
        -- update list of critical points by deleting any critical
        -- points which were plotted
        while not null crits repeat
          pt := first crits
          segInfo := listPtsOnLoop(pSF,dpdxSF,dpdySF,x,y,pt,_
                           corners,delta,err,bound,crits,bdry)
          bran  := cons(first segInfo,bran)
          crits := second segInfo
        bran

      dummyFirstPt(p1,dpdxSF,dpdySF,x,y,lf,rt,bt,tp) ==
      -- The function 'computeNextPt' requires 2 points, p0 and p1.
      -- When computing the second point on a branch which starts
      -- on the boundary, we use the boundary point as p1 and the
      -- 'dummy' point returned by this function as p0.
        x1 := xCoord p1; y1 := yCoord p1
        zero := 0$SF; one := 1$SF
        px := ground(eval(dpdxSF,[x,y],[x1,y1]))
        py := ground(eval(dpdySF,[x,y],[x1,y1]))
        if px * py < zero then       -- positive slope at p1
          member?(p1,lf) or member?(p1,bt) =>
            makePt(x1 - one,y1 - one)
          makePt(x1 + one,y1 + one)
        else
          member?(p1,lf) or member?(p1,tp) =>
            makePt(x1 - one,y1 + one)
          makePt(x1 + one,y1 - one)

      listPtsOnSegment(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                                     delta,err,bound,crits,bdry) ==
      -- p1 is a boundary point; p0 is a 'dummy' point
        bdry := remove(p1,bdry)
        pointList : L Pt := [p1]
        ptInfo := computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                                     delta,err,bound,crits,bdry)
        p2 := ptInfo.newPt
        ptInfo.type = BDRY =>
          bdry := remove(p2,bdry)
          pointList := cons(p2,pointList)
          [pointList,crits,bdry]
        if ptInfo.type = CRIT then crits := remove(p2,crits)
        pointList := cons(p2,pointList)
        repeat
          pt0 := second pointList; pt1 := first pointList
          ptInfo := computeNextPt(pSF,dpdxSF,dpdySF,x,y,pt0,pt1,corners,_
                                       delta,err,bound,crits,bdry)
          p2 := ptInfo.newPt
          ptInfo.type = BDRY =>
            bdry := remove(p2,bdry)
            pointList := cons(p2,pointList)
            return [pointList,crits,bdry]
          if ptInfo.type = CRIT then crits := remove(p2,crits)
          pointList := cons(p2,pointList)
        --!! delete next line (compiler bug)
        [pointList,crits,bdry]


      listPtsOnLoop(pSF,dpdxSF,dpdySF,x,y,p1,corners,_
                                     delta,err,bound,crits,bdry) ==
        x1 := xCoord p1; y1 := yCoord p1
        px := ground(eval(dpdxSF,[x,y],[x1,y1]))
        py := ground(eval(dpdySF,[x,y],[x1,y1]))
        p0 := makePt(x1 - 1$SF,y1 - 1$SF)
        pointList : L Pt := [p1]
        ptInfo := computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                                     delta,err,bound,crits,bdry)
        p2 := ptInfo.newPt
        ptInfo.type = BDRY =>
          error "boundary reached while on loop"
        if ptInfo.type = CRIT then
          p1 = p2 =>
            error "first and second points on loop are identical"
          crits := remove(p2,crits)
        pointList := cons(p2,pointList)
        repeat
          pt0 := second pointList; pt1 := first pointList
          ptInfo := computeNextPt(pSF,dpdxSF,dpdySF,x,y,pt0,pt1,corners,_
                                       delta,err,bound,crits,bdry)
          p2 := ptInfo.newPt
          ptInfo.type = BDRY =>
            error "boundary reached while on loop"
          if ptInfo.type = CRIT then
            crits := remove(p2,crits)
            p1 = p2 =>
              pointList := cons(p2,pointList)
              return [pointList,crits,bdry]
          pointList := cons(p2,pointList)
        --!! delete next line (compiler bug)
        [pointList,crits,bdry]

      computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                                     delta,err,bound,crits,bdry) ==
      -- p0=(x0,y0) and p1=(x1,y1) are the last two points on the curve.
      -- The function computes the next point on the curve.
      -- The function determines if the next point is a critical point
      -- or a boundary point.
      -- The function returns a record of the form
      -- Record(newPt:Pt,type:String).
      -- If the new point is a boundary point, then 'type' is
      -- "boundary point" and 'newPt' is a boundary point to be
      -- deleted from the list of boundary points yet to be plotted.
      -- Similarly, if the new point is a critical point, then 'type' is
      -- "critical point" and 'newPt' is a critical point to be
      -- deleted from the list of critical points yet to be plotted.
      -- If the new point is neither a critical point nor a boundary
      -- point, then 'type' is "nothing in particular".
        xMinSF := getXMin corners; xMaxSF := getXMax corners
        yMinSF := getYMin corners; yMaxSF := getYMax corners
        x0 := xCoord p0; y0 := yCoord p0
        x1 := xCoord p1; y1 := yCoord p1
        px := ground(eval(dpdxSF,[x,y],[x1,y1]))
        py := ground(eval(dpdySF,[x,y],[x1,y1]))
        -- let m be the slope of the tangent line at p1
        -- if |m| < 1, we will increment the x-coordinate by delta
        -- (indicated by 'incVar = x'), find an approximate
        -- y-coordinate using the tangent line, then find the actual
        -- y-coordinate using a Newton iteration
        if abs(py) > abs(px) then
          incVar0 := incVar := x
          deltaX := (if x1 > x0 then delta else -delta)
          x2Approx := x1 + deltaX
          y2Approx := y1 + (-px/py)*deltaX
        -- if |m| >= 1, we interchange the roles of the x- and y-
        -- coordinates
        else
          incVar0 := incVar := y
          deltaY := (if y1 > y0 then delta else -delta)
          x2Approx := x1 + (-py/px)*deltaY
          y2Approx := y1 + deltaY
        lookingFor := NADA
        -- See if (x2Approx,y2Approx) is out of bounds.
        -- If so, find where the line segment connecting (x1,y1) and
        -- (x2Approx,y2Approx) intersects the boundary and use this
        -- point as (x2Approx,y2Approx).
        -- If the resulting point is on the left or right boundary,
        -- we will now consider x as the 'incremented variable' and we
        -- will compute the y-coordinate using a Newton iteration.
        -- Similarly, if the point is on the top or bottom boundary,
        -- we will consider y as the 'incremented variable' and we
        -- will compute the x-coordinate using a Newton iteration.
        if x2Approx >= xMaxSF then
          incVar := x
          lookingFor := BDRY
          x2Approx := xMaxSF
          y2Approx := y1 + (-px/py)*(x2Approx - x1)
        else
          if x2Approx <= xMinSF then
            incVar := x
            lookingFor := BDRY
            x2Approx := xMinSF
            y2Approx := y1 + (-px/py)*(x2Approx - x1)
        if y2Approx >= yMaxSF then
          incVar := y
          lookingFor := BDRY
          y2Approx := yMaxSF
          x2Approx := x1 + (-py/px)*(y2Approx - y1)
        else
          if y2Approx <= yMinSF then
            incVar := y
            lookingFor := BDRY
            y2Approx := yMinSF
            x2Approx := x1 + (-py/px)*(y2Approx - y1)
        -- set xLo = min(x1,x2Approx), xHi = max(x1,x2Approx)
        -- set yLo = min(y1,y2Approx), yHi = max(y1,y2Approx)
        if x1 < x2Approx then
          xLo := x1
          xHi := x2Approx
        else
          xLo := x2Approx
          xHi := x1
        if y1 < y2Approx then
          yLo := y1
          yHi := y2Approx
        else
          yLo := y2Approx
          yHi := y1
        -- check for critical points (x*,y*) with x* between
        -- x1 and x2Approx or y* between y1 and y2Approx
        -- store values of x2Approx and y2Approx
        x2Approxx := x2Approx
        y2Approxx := y2Approx
        -- xPointList will contain all critical points (x*,y*)
        -- with x* between x1 and x2Approx
        xPointList : L Pt := nil()
        -- yPointList will contain all critical points (x*,y*)
        -- with y* between y1 and y2Approx
        yPointList : L Pt := nil()
        for pt in crits repeat
          xx := xCoord pt; yy := yCoord pt
          -- if x1 = x2Approx, then p1 is a point with horizontal
          -- tangent line
          -- in this case, we don't want critical points with
          -- x-coordinate x1
          if xx = x2Approx and not (xx = x1) then
            if min(abs(yy-yLo),abs(yy-yHi)) < delta then
              xPointList := cons(pt,xPointList)
          if ((xLo < xx) and (xx < xHi)) then
            if min(abs(yy-yLo),abs(yy-yHi)) < delta then
              xPointList := cons(pt,nil())
              x2Approx := xx
              if xx < x1 then xLo := xx else xHi := xx
          -- if y1 = y2Approx, then p1 is a point with vertical
          -- tangent line
          -- in this case, we don't want critical points with
          -- y-coordinate y1
          if yy = y2Approx and not (yy = y1) then
              yPointList := cons(pt,yPointList)
          if ((yLo < yy) and (yy < yHi)) then
            if min(abs(xx-xLo),abs(xx-xHi)) < delta then
              yPointList := cons(pt,nil())
              y2Approx := yy
              if yy < y1 then yLo := yy else yHi := yy
        -- points in both xPointList and yPointList
        if (not null xPointList) and (not null yPointList) then
          xPointList = yPointList =>
          -- this implies that the lists have only one point
            incVar := incVar0
            if incVar = x then
              y2Approx := y1 + (-px/py)*(x2Approx - x1)
            else
              x2Approx := x1 + (-py/px)*(y2Approx - y1)
            lookingFor := CRIT        -- proceed
          incVar0 = x =>
          -- first try Newton iteration with 'y' as incremented variable
            x2Temp := x1 + (-py/px)*(y2Approx - y1)
            f := SFPolyToUPoly(eval(pSF,y,y2Approx))
            x2New := newtonApprox(f,x2Temp,err,bound)
            x2New case "failed" =>
              y2Approx := y1 + (-px/py)*(x2Approx - x1)
              incVar := x
              lookingFor := CRIT      -- proceed
            y2Temp := y1 + (-px/py)*(x2Approx - x1)
            f := SFPolyToUPoly(eval(pSF,x,x2Approx))
            y2New := newtonApprox(f,y2Temp,err,bound)
            y2New case "failed" =>
              return computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                            abs((x2Approx-x1)/2),err,bound,crits,bdry)
            pt1 := makePt(x2Approx,y2New :: SF)
            pt2 := makePt(x2New :: SF,y2Approx)
            critPt1 := findPtOnList(pt1,crits)
            critPt2 := findPtOnList(pt2,crits)
            (critPt1 case "failed") and (critPt2 case "failed") =>
              abs(x2Approx - x1) > abs(x2Temp - x1) =>
                return [pt1,NADA]
              return [pt2,NADA]
            (critPt1 case "failed") =>
              return [critPt2::Pt,CRIT]
            (critPt2 case "failed") =>
              return [critPt1::Pt,CRIT]
            abs(x2Approx - x1) > abs(x2Temp - x1) =>
              return [critPt2::Pt,CRIT]
            return [critPt1::Pt,CRIT]
          y2Temp := y1 + (-px/py)*(x2Approx - x1)
          f := SFPolyToUPoly(eval(pSF,x,x2Approx))
          y2New := newtonApprox(f,y2Temp,err,bound)
          y2New case "failed" =>
            x2Approx := x1 + (-py/px)*(y2Approx - y1)
            incVar := y
            lookingFor := CRIT      -- proceed
          x2Temp := x1 + (-py/px)*(y2Approx - y1)
          f := SFPolyToUPoly(eval(pSF,y,y2Approx))
          x2New := newtonApprox(f,x2Temp,err,bound)
          x2New case "failed" =>
            return computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                          abs((y2Approx-y1)/2),err,bound,crits,bdry)
          pt1 := makePt(x2Approx,y2New :: SF)
          pt2 := makePt(x2New :: SF,y2Approx)
          critPt1 := findPtOnList(pt1,crits)
          critPt2 := findPtOnList(pt2,crits)
          (critPt1 case "failed") and (critPt2 case "failed") =>
            abs(y2Approx - y1) > abs(y2Temp - y1) =>
              return [pt2,NADA]
            return [pt1,NADA]
          (critPt1 case "failed") =>
            return [critPt2::Pt,CRIT]
          (critPt2 case "failed") =>
            return [critPt1::Pt,CRIT]
          abs(y2Approx - y1) > abs(y2Temp - y1) =>
            return [critPt1::Pt,CRIT]
          return [critPt2::Pt,CRIT]
        if (not null xPointList) and (null yPointList) then
          y2Approx := y1 + (-px/py)*(x2Approx - x1)
          incVar0 = x =>
            incVar := x
            lookingFor := CRIT        -- proceed
          f := SFPolyToUPoly(eval(pSF,x,x2Approx))
          y2New := newtonApprox(f,y2Approx,err,bound)
          y2New case "failed" =>
            x2Approx := x2Approxx
            y2Approx := y2Approxx     -- proceed
          pt := makePt(x2Approx,y2New::SF)
          critPt := findPtOnList(pt,crits)
          critPt case "failed" =>
            return [pt,NADA]
          return [critPt :: Pt,CRIT]
        if (null xPointList) and (not null yPointList) then
          x2Approx := x1 + (-py/px)*(y2Approx - y1)
          incVar0 = y =>
            incVar := y
            lookingFor := CRIT        -- proceed
          f := SFPolyToUPoly(eval(pSF,y,y2Approx))
          x2New := newtonApprox(f,x2Approx,err,bound)
          x2New case "failed" =>
            x2Approx := x2Approxx
            y2Approx := y2Approxx     -- proceed
          pt := makePt(x2New::SF,y2Approx)
          critPt := findPtOnList(pt,crits)
          critPt case "failed" =>
            return [pt,NADA]
          return [critPt :: Pt,CRIT]
        if incVar = x then
          x2 := x2Approx
          f := SFPolyToUPoly(eval(pSF,x,x2))
          y2New := newtonApprox(f,y2Approx,err,bound)
          y2New case "failed" =>
            return computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                                   abs((x2-x1)/2),err,bound,crits,bdry)
          y2 := y2New :: SF
        else
          y2 := y2Approx
          f := SFPolyToUPoly(eval(pSF,y,y2))
          x2New := newtonApprox(f,x2Approx,err,bound)
          x2New case "failed" =>
            return computeNextPt(pSF,dpdxSF,dpdySF,x,y,p0,p1,corners,_
                                   abs((y2-y1)/2),err,bound,crits,bdry)
          x2 := x2New :: SF
        pt := makePt(x2,y2)
        --!! check that 'pt' is not out of bounds
        -- check if you've gotten a critical or boundary point
        lookingFor = NADA =>
          [pt,lookingFor]
        lookingFor = BDRY =>
          bdryPt := findPtOnList(pt,bdry)
          bdryPt case "failed" =>
            error "couldn't find boundary point"
          [bdryPt :: Pt,BDRY]
        critPt := findPtOnList(pt,crits)
        critPt case "failed" =>
          [pt,NADA]
        [critPt :: Pt,CRIT]

--% Newton iterations

      newtonApprox(f,a0,err,bound) ==
      -- Newton iteration to approximate a root of the polynomial 'f'
      -- using an initial approximation of 'a0'
      -- Newton iteration terminates when consecutive approximations
      -- are within 'err' of each other
      -- returns "failed" if this has not been achieved after 'bound'
      -- iterations
        Df := differentiate f
        oldApprox := a0
        newApprox := a0 - elt(f,a0)/elt(Df,a0)
        i : PI := 1
        while abs(newApprox - oldApprox) > err repeat
          i = bound => return "failed"
          oldApprox := newApprox
          newApprox := oldApprox - elt(f,oldApprox)/elt(Df,oldApprox)
          i := i+1
        newApprox

--% graphics output

      listBranches(acplot) == acplot.branches

--% terminal output

      coerce(acplot:%) ==
        pp := acplot.poly :: OUT
        xx := acplot.xVar :: OUT
        yy := acplot.yVar :: OUT
        xLo := acplot.minXVal :: OUT
        xHi := acplot.maxXVal :: OUT
        yLo := acplot.minYVal :: OUT
        yHi := acplot.maxYVal :: OUT
        zip := message(" = 0")
        com := message(",   ")
        les := message(" <= ")
        l : L OUT :=
          [pp,zip,com,xLo,les,xx,les,xHi,com,yLo,les,yy,les,yHi]
        f : L OUT := nil()
        for branch in acplot.branches repeat
          ll : L OUT := [p :: OUT for p in branch]
          f := cons(vconcat ll,f)
        ff := vconcat(hconcat l,vconcat f)
        vconcat(message "ACPLOT",ff)

@
\section{License}
<<license>>=
--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@
<<*>>=
<<license>>

<<package REALSOLV RealSolvePackage>>
<<domain ACPLOT PlaneAlgebraicCurvePlot>>

@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}
