/*******************************************************************/
/*******************************************************************/
/*                                                                 */
/*                     PLOT EN HAUTE RESOLUTION                    */
/*                                                                 */
/*                       copyright Babe Cool                       */
/*                                                                 */
/*******************************************************************/
/*******************************************************************/
/* $Id: plotport.c,v 2.0.0.2 1997/12/14 20:11:49 karim Exp karim $ */
#include "genpari.h"
#include "rect.h"

void push_val(entree *ep, GEN a);
void pop_val(entree *ep);
void postdraw0(long *w, long *x, long *y, long lw);
void rectdraw0(long *w, long *x, long *y, long lw, long do_free);
static void PARI_get_psplot();

long current_color[NUMRECT];
Rect **rectgraph;
PARI_plot pari_plot, pari_psplot;

#define STRINGRECT (NUMRECT-2)
#define DRAWRECT (NUMRECT-1)

#define PLOTH_NUMPOINTS 1000
#define PARAM_NUMPOINTS 1500
#define RECUR_NUMPOINTS 15

#define RECUR_MAXDEPTH 10
#define RECUR_PREC 0.001
#define PARAMR_MAXDEPTH 10

/********************************************************************/
/**                                                                **/
/**                         LOW-RES PLOT                           **/
/**                                                                **/
/********************************************************************/

#define ISCR 60
#define JSCR 21
#define BLANK ' '
#define ZERO '-'
#define YY '|'
#define XX '-'
#define FF 'x'

void
plot(entree *ep, GEN a, GEN b, char *ch)
{
  long av = avma, av2,limite,jz,j,i,sig;
  GEN p1,p2,ysml,ybig,x,diff,dyj,dx,y[ISCR+1];
  char scr[ISCR+1][JSCR+1];

  sig=gcmp(b,a); if (!sig) return;
  if (sig<0) { x=a; a=b; b=x; }
  x = cgetr(5); gaffect(a,x); push_val(ep, x);
  for (i=1; i<=ISCR; i++) y[i]=cgetr(3);
  p1 = gdivgs(gsub(b,a), ISCR-1);
  dx = cgetr(5); gaffect(p1, dx); 
  ysml=gzero; ybig=gzero;
  for (j=1; j<=JSCR; j++) scr[1][j]=scr[ISCR][j]=YY;
  for (i=2; i<ISCR; i++)
  {
    scr[i][1]=scr[i][JSCR]=XX;
    for (j=2; j<JSCR; j++) scr[i][j]=BLANK;
  }
  av2=avma; limite=(av2+bot)>>1;
  for (i=1; i<=ISCR; i++)
  {
    gaffect(lisexpr(ch),y[i]);
    if (gcmp(y[i],ysml)<0) ysml=y[i];
    if (gcmp(y[i],ybig)>0) ybig=y[i];
    x = addrr(x,dx);
    if (low_stack(limite, (av2+bot)>>1))
    {
      long tetpil=avma; 
      if (DEBUGMEM>1) err(warnmem,"plot");
      x = gerepile(av2,tetpil,rcopy(x));
    }
    ep->value = (void*)x;
  }
  avma=av2; diff=gsub(ybig,ysml);
  if (gcmp0(diff)) { ybig=gaddsg(1,ybig); diff=gun; }
  dyj=gdivsg(JSCR-1,diff); jz=1-gtolong(gmul(ysml,dyj));
  av2=avma;
  for (i=1; i<=ISCR; i++)
  {
    scr[i][jz]=ZERO; j=1+gtolong(gmul(gsub(y[i],ysml),dyj));
    scr[i][j]=FF; avma=av2;
  }
  p1=cgetr(4); gaffect(ybig,p1); pariputc('\n');
  pariputsf(" %10.3f ",rtodbl(p1));
  for (i=1; i<=ISCR; i++) pariputc(scr[i][JSCR]);
  pariputc('\n');
  for (j=(JSCR-1); j>=2; j--)
  {
    pariputs("            ");
    for (i=1; i<=ISCR; i++) pariputc(scr[i][j]);
    pariputc('\n');
  }
  p1=cgetr(4); gaffect(ysml,p1);
  pariputsf(" %10.3f ",rtodbl(p1));
  for (i=1; i<=ISCR; i++)  pariputc(scr[i][1]);
  pariputc('\n');
  p1=cgetr(4); p2=cgetr(4); gaffect(a,p1); gaffect(b,p2);
  pariputsf("%8s %10.3f %44s %10.3f\n"," ",rtodbl(p1)," ",rtodbl(p2));
  pariputc('\n'); pop_val(ep); avma=av;
}

/********************************************************************/
/**                                                                **/
/**                      RECTPLOT FUNCTIONS                        **/
/**                                                                **/
/********************************************************************/
void
init_graph()
{
  int n;

  rectgraph = (Rect**) gpmalloc(sizeof(Rect*)*NUMRECT);
  for (n=0; n<NUMRECT; n++)
  {
    Rect *e = (Rect*) gpmalloc(sizeof(Rect));

    e->head = e->tail = NULL;
    e->sizex = e->sizey = 0;
    current_color[n] = DEFAULT_COLOR;
    rectgraph[n] = e;
  }
}

void
free_graph()
{
  int i;

  for (i=0; i<NUMRECT; i++)
  {
    Rect *e=rectgraph[i];
    
    if (RHead(e)) killrect(i);
    free((void *)e);
  }
  free((void *)rectgraph);
}

static Rect *
check_rect(long ne)
{
  if (!GOODRECT(ne))
    err(talker,"not an rplot vector type in graphic function");
  return rectgraph[ne];
}

static Rect *
check_rect_init(long ne)
{
  Rect *e = check_rect(ne);
  if (!RHead(e))
    err(talker,"you must initialize the rectwindow first");
  return e;
}

void
initrect(long ne, long x, long y)
{
  Rect *e;
  RectObj *z;

  if (x<=1 || y<=1) err(talker,"incorrect dimensions in initrect");
  e = check_rect(ne); if (RHead(e)) killrect(ne);

  z = (RectObj*) gpmalloc(sizeof(RectObj));
  RoNext(z) = NULL;
  RoType(z) = ROt_NULL;
  RHead(e)=RTail(e)=z;
  RXsize(e)=x; RYsize(e)=y;
  RXcursor(e)=0; RYcursor(e)=0;
  RXscale(e)=1; RXshift(e)=0;
  RYscale(e)=1; RYshift(e)=0;
}

GEN
rectcursor(long ne)
{
  Rect *e = check_rect_init(ne);
  GEN z=cgetg(3,t_VEC);

  z[1] = lstoi((long)RXcursor(e)); z[2] = lstoi((long)RYcursor(e));
  return z;
}

static void
rectscale0(long ne, double x1, double x2, double y1, double y2)
{
  Rect *e = check_rect_init(ne);
  double p2,p3;

  p2 = RXshift(e) + RXscale(e) * RXcursor(e);
  p3 = RYshift(e) + RYscale(e) * RYcursor(e);
  RXscale(e) = RXsize(e)/(x2-x1); RXshift(e) = -x1*RXscale(e);
  RYscale(e) = RYsize(e)/(y1-y2); RYshift(e) = -y2*RYscale(e);
  RXcursor(e) = (p2 - RXshift(e)) / RXscale(e);
  RYcursor(e) = (p3 - RYshift(e)) / RYscale(e);
}

void
rectscale(long ne, GEN x1, GEN x2, GEN y1, GEN y2)
{
  rectscale0(ne, gtodouble(x1), gtodouble(x2), gtodouble(y1), gtodouble(y2));
}

/*
 * #ifdef LONG_IS_64BIT
 * #  define MAXDTOL 9223372036854775807.0
 * #else
 * #  define MAXDTOL 2147483647.0
 * #endif
 *
 *
 * long
 * DTOL(double t)
 * {
 *   if (fabs(t) > MAXDTOL) err(affer2);
 *   return (long)t;
 * }
 */

#define DTOL(t) ((long)(t))

static void
rectmove0(long ne, double x, double y, long relative)
{
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObj1P));

  if (relative)
   { RXcursor(e) += x; RYcursor(e) += y; }
  else
   { RXcursor(e) = x; RYcursor(e) = y; }
  RoNext(z) = 0; RoType(z) = ROt_MV;
  RoMVx(z) = DTOL(RXcursor(e) * RXscale(e) + RXshift(e));
  RoMVy(z) = DTOL(RYcursor(e) * RYscale(e) + RYshift(e));
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
}

void
rectmove(long ne, GEN x, GEN y) /* code = 0 */
{
  rectmove0(ne,gtodouble(x),gtodouble(y),0);
}

void
rectrmove(long ne, GEN x, GEN y) /* code = 0 */
{
  rectmove0(ne,gtodouble(x),gtodouble(y),1);
}

void
rectpoint0(long ne, double x, double y,long relative) /* code = 1 */
{
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObj1P));

  if (relative)
   { RXcursor(e) += x; RYcursor(e) += y; }
  else
   { RXcursor(e) = x; RYcursor(e) = y; }
  RoNext(z)=0;
  RoPTx(z) = DTOL(RXcursor(e)*RXscale(e) + RXshift(e));
  RoPTy(z) = DTOL(RYcursor(e)*RYscale(e) + RYshift(e));
  RoType(z) = ((RoPTx(z)<0)||(RoPTy(z)<0)||(RoPTx(z)>RXsize(e))
	       ||(RoPTy(z)>RYsize(e))) ? ROt_MV : ROt_PT;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
  RoCol(z)=current_color[ne];
}

void
rectpoint(long ne, GEN x, GEN y) /* code = 1 */
{
  rectpoint0(ne,gtodouble(x),gtodouble(y),0);
}

void
rectrpoint(long ne, GEN x, GEN y) /* code = 1 */
{
  rectpoint0(ne,gtodouble(x),gtodouble(y),1);
}

void
rectcolor(long ne, long color)
{
  check_rect(ne);
  if (!GOODCOLOR(color)) err(talker,"This is not a valid color");
  current_color[ne]=color;
}

void
rectline0(long ne, double gx2, double gy2, long relative) /* code = 2 */
{
  long dx,dy,dxy,xmin,xmax,ymin,ymax,x1,y1,x2,y2;
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObj2P));

  x1 = DTOL(RXcursor(e)*RXscale(e) + RXshift(e));
  y1 = DTOL(RYcursor(e)*RYscale(e) + RYshift(e));
  if (relative)
    { RXcursor(e)+=gx2; RYcursor(e)+=gy2; }
  else
    { RXcursor(e)=gx2; RYcursor(e)=gy2; }
  x2 = DTOL(RXcursor(e)*RXscale(e) + RXshift(e));
  y2 = DTOL(RYcursor(e)*RYscale(e) + RYshift(e));
  xmin = max(min(x1,x2),0); xmax = min(max(x1,x2),RXsize(e));
  ymin = max(min(y1,y2),0); ymax = min(max(y1,y2),RYsize(e));
  dxy = x1*y2 - y1*x2; dx = x2-x1; dy = y2-y1;
  if (dy)
  {
    if (dx*dy<0)
      { xmin = max(xmin,(dxy+RYsize(e)*dx)/dy); xmax=min(xmax,dxy/dy); }
    else
      { xmin=max(xmin,dxy/dy); xmax=min(xmax,(dxy+RYsize(e)*dx)/dy); }
  }
  if (dx)
  {
    if (dx*dy<0)
      { ymin=max(ymin,(RXsize(e)*dy-dxy)/dx); ymax=min(ymax,-dxy/dx); }
    else
      { ymin=max(ymin,-dxy/dx); ymax=min(ymax,(RXsize(e)*dy-dxy)/dx); }
  }
  RoNext(z)=0;
  RoLNx1(z) = xmin; RoLNx2(z) = xmax;
  if (dx*dy<0) { RoLNy1(z) = ymax; RoLNy2(z) = ymin; }
  else { RoLNy1(z) = ymin; RoLNy2(z) = ymax; }
  RoType(z) = ((xmin>xmax)||(ymin>ymax)) ? ROt_MV : ROt_LN;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
  RoCol(z)=current_color[ne];
}

void
rectline(long ne, GEN gx2, GEN gy2) /* code = 2 */
{
  rectline0(ne, gtodouble(gx2), gtodouble(gy2),0);
}

void
rectrline(long ne, GEN gx2, GEN gy2) /* code = 2 */
{
  rectline0(ne, gtodouble(gx2), gtodouble(gy2),1);
}

void
rectbox0(long ne, double gx2, double gy2, long relative)
{
  long x1,y1,x2,y2,xmin,ymin,xmax,ymax;
  double xx,yy;
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObj2P));

  x1 = DTOL(RXcursor(e)*RXscale(e) + RXshift(e));
  y1 = DTOL(RYcursor(e)*RYscale(e) + RYshift(e));
  if (relative)
  { xx = RXcursor(e)+gx2; yy = RYcursor(e)+gy2; }
  else
  {  xx = gx2; yy = gy2; }
  x2=DTOL(xx*RXscale(e) + RXshift(e));
  y2=DTOL(yy*RYscale(e) + RYshift(e));
  xmin = max(min(x1,x2),0); xmax = min(max(x1,x2),RXsize(e));
  ymin = max(min(y1,y2),0); ymax = min(max(y1,y2),RYsize(e));

  RoNext(z)=0; RoType(z) = ROt_BX;
  RoBXx1(z) = xmin; RoBXy1(z) = ymin;
  RoBXx2(z) = xmax; RoBXy2(z) = ymax;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
  RoCol(z)=current_color[ne];
}

void
rectbox(long ne, GEN gx2, GEN gy2) /* code = 3 */
{
  rectbox0(ne, gtodouble(gx2), gtodouble(gy2), 0);
}

void
rectrbox(long ne, GEN gx2, GEN gy2) /* code = 3 */
{
  rectbox0(ne, gtodouble(gx2), gtodouble(gy2), 1);
}

void
killrect(long ne)
{
  RectObj *p1,*p2;
  Rect *e = check_rect_init(ne);

  current_color[ne]=DEFAULT_COLOR;
  p1=RHead(e);
  RHead(e) = RTail(e) = NULL;
  RXsize(e) = RYsize(e) = 0;
  RXcursor(e) = RYcursor(e) = 0;
  RXscale(e) = RYscale(e) = 1;
  RXshift(e) = RYshift(e) = 0;
  while (p1)
  {
    if (RoType(p1)==ROt_MP || RoType(p1)==ROt_ML)
    {
      free(RoMPxs(p1)); free(RoMPys(p1));
    }
    if (RoType(p1)==ROt_ST) free(RoSTs(p1));
    p2=RoNext(p1); free(p1); p1=p2;
  }
}

void
rectpoints0(long ne, double *listx, double *listy, long lx) /* code = 4 */
{
  long *ptx,*pty,x,y,i,cp=0;
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObjMP));

  ptx=(long*) gpmalloc(lx*sizeof(long));
  pty=(long*) gpmalloc(lx*sizeof(long));
  for (i=0; i<lx; i++)
  {
    x=DTOL(RXscale(e)*listx[i] + RXshift(e));
    y=DTOL(RYscale(e)*listy[i] + RYshift(e));
    if ((x>=0)&&(y>=0)&&(x<=RXsize(e))&&(y<=RYsize(e)))
    {
      ptx[cp]=x; pty[cp]=y; cp++;
    }
  }
  RoNext(z)=0; RoType(z) = ROt_MP;
  RoMPcnt(z) = cp; RoMPxs(z) = ptx; RoMPys(z) = pty;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
  RoCol(z)=current_color[ne];
}

void
rectpoints(long ne, GEN listx, GEN listy) /* code = 4 */
{
  long i,lx, tx=typ(listx), ty=typ(listy);
  double *px,*py;

  if (!is_matvec_t(tx) || !is_matvec_t(ty))
  {
    rectpoint(ne, listx, listy); return;
  }
  if (tx == t_MAT || ty == t_MAT) err(ploter4);
  lx=lg(listx); if (lg(listy)!=lx) err(ploter5);
  lx--; if (!lx) return;

  px = (double*) gpmalloc(lx*sizeof(double));
  py = (double*) gpmalloc(lx*sizeof(double));
  for (i=0; i<lx; i++)
  {
    px[i]=gtodouble((GEN)listx[i+1]); py[i]=gtodouble((GEN)listy[i+1]);
  }
  rectpoints0(ne,px,py,lx);
  free(px); free(py);
}

void
rectlines0(long ne, double *x, double *y, long lx, long flag) /* code = 5 */
{
  long i,I,*ptx,*pty;
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObj2P));

  I = flag ? lx+1 : lx;
  ptx = (long*) gpmalloc(I*sizeof(long));
  pty = (long*) gpmalloc(I*sizeof(long));
  for (i=0; i<lx; i++)
  {
    ptx[i]=DTOL(RXscale(e)*x[i] + RXshift(e));
    pty[i]=DTOL(RYscale(e)*y[i] + RYshift(e));
  }
  if (flag)
  {
    ptx[i]=DTOL(RXscale(e)*x[0] + RXshift(e));
    pty[i]=DTOL(RYscale(e)*y[0] + RYshift(e));
  }
  RoNext(z)=0; RoType(z)=ROt_ML;
  RoMLcnt(z)=lx; RoMLxs(z)=ptx; RoMLys(z)=pty;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
  RoCol(z) = current_color[ne];
}

void
rectlines(long ne, GEN listx, GEN listy, long flag) /* code = 5 */
{
  long tx=typ(listx), ty=typ(listy), lx=lg(listx), i;
  double *x, *y;

  if (!is_matvec_t(tx) || !is_matvec_t(ty))
  {
    rectline(ne, listx, listy); return;
  }
  if (tx == t_MAT || ty == t_MAT) err(ploter4);
  if (lg(listy)!=lx) err(ploter5);
  lx--; if (!lx) return;

  x = (double*) gpmalloc(lx*sizeof(double));
  y = (double*) gpmalloc(lx*sizeof(double));
  for (i=0; i<lx; i++)
  {
    x[i] = gtodouble((GEN)listx[i+1]); y[i] = gtodouble((GEN)listy[i+1]);
  }
  rectlines0(ne,x,y,lx,flag);
  free(x); free(y);
}

static void
put_string(long win, long x, long y, char *str)
{
  rectmove0(win,(double)x,(double)y,0); rectstring(win,str);
}

/* Allocate memory, then put string */
void
rectstring(long ne, char *str) /* code = 6 */
{
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObjST));
  long l = strlen(str);
  char *s = (char *) gpmalloc(l+1);

  strcpy(s,str);
  RoNext(z)=0; RoType(z) = ROt_ST;
  RoSTl(z) = l; RoSTs(z) = s;
  RoSTx(z) = DTOL(RXscale(e)*RXcursor(e)+RXshift(e));
  RoSTy(z) = DTOL(RYscale(e)*RYcursor(e)+RYshift(e));
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
  RoCol(z)=current_color[ne];
}

void
rectpointtype(long ne, long type) /* code = 0 */
{
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObjPN));

  RoNext(z) = 0; RoType(z) = ROt_PTT;
  RoPTTpen(z) = type;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
}

void
rectlinetype(long ne, long type)
{
  Rect *e = check_rect_init(ne);
  RectObj *z = (RectObj*) gpmalloc(sizeof(RectObjPN));

  RoNext(z) = 0; RoType(z) = ROt_LNT;
  RoLNTpen(z) = type;
  if (!RHead(e)) RHead(e)=RTail(e)=z;
  else { RoNext(RTail(e))=z; RTail(e)=z; }
}

void
rectcopy(long source, long dest, long xoff, long yoff)
{
  Rect *s = rectgraph[source], *d = rectgraph[dest];
  RectObj *p1 = RHead(s);
  RectObj *tail = RTail(d);
  RectObj *next;
  int i;

  while (p1)
  {
    switch(RoType(p1))
    {
      case ROt_PT:
	next = (RectObj*) gpmalloc(sizeof(RectObj1P));
	memcpy(next,p1,sizeof(RectObj1P));
	RoPTx(next) += xoff; RoPTy(next) += yoff;
	RoNext(tail) = next; tail = next;
	break;
      case ROt_LN: case ROt_BX:
	next = (RectObj*) gpmalloc(sizeof(RectObj2P));
	memcpy(next,p1,sizeof(RectObj2P));
	RoLNx1(next) += xoff; RoLNy1(next) += yoff;
	RoLNx2(next) += xoff; RoLNy2(next) += yoff;
	RoNext(tail) = next; tail = next;
	break;
      case ROt_MP: case ROt_ML:
	next = (RectObj*) gpmalloc(sizeof(RectObjMP));
	memcpy(next,p1,sizeof(RectObjMP));
	RoMPxs(next) = (long*) gpmalloc(sizeof(long)*RoMPcnt(next));
	RoMPys(next) = (long*) gpmalloc(sizeof(long)*RoMPcnt(next));
	memcpy(RoMPxs(next),RoMPxs(p1),sizeof(long)*RoMPcnt(next));
	memcpy(RoMPys(next),RoMPys(p1),sizeof(long)*RoMPcnt(next));
	for (i=0; i<RoMPcnt(next); i++)
	{
	  RoMPxs(next)[i] += xoff; RoMPys(next)[i] += yoff;
	 }
	RoNext(tail) = next; tail = next;
	break;
      case ROt_ST:
	next = (RectObj*) gpmalloc(sizeof(RectObjMP));
	memcpy(next,p1,sizeof(RectObjMP));
	RoSTs(next) = (char*) gpmalloc(RoSTl(p1)+1);
	memcpy(RoSTs(next),RoSTs(p1),RoSTl(p1)+1);
	RoSTx(next) += xoff; RoSTy(next) += yoff;
	RoNext(tail) = next; tail = next;
	break;
      case ROt_PTT: case ROt_LNT:
	next = (RectObj*) gpmalloc(sizeof(RectObjPN));
	memcpy(next,p1,sizeof(RectObjPN));
	RoNext(tail) = next; tail = next;
	break;
    }
    p1=RoNext(p1);
  }
  RoNext(tail) = NULL; RTail(d) = tail;
}

/********************************************************************/
/**                                                                **/
/**                        HI-RES PLOT                             **/
/**                                                                **/
/********************************************************************/

static void
Appendx(dblPointList *f, dblPointList *l,double x)
{
  (l->d)[l->nb++]=x;
  if (x < f->xsml) f->xsml=x;
  else if (x > f->xbig) f->xbig=x;
}

static void
Appendy(dblPointList *f, dblPointList *l,double y)
{
  (l->d)[l->nb++]=y;
  if (y < f->ysml) f->ysml=y;
  else if (y > f->ybig) f->ybig=y;
}

/* Convert data from GEN to double before we call rectplothrawin */
static dblPointList*
gtodblList(GEN data, long flags)
{
  dblPointList *l;
  double xsml,xbig,ysml,ybig;
  long tx=typ(data), ty, nl=lg(data)-1, lx1,lx;
  register long i,j,u,v;
  long param=(flags & PLOT_PARAMETRIC);
  GEN x,y;

  if (! is_vec_t(tx)) err(talker,"not a vector in gtodblList");
  if (!nl) return NULL;
  lx1=lg(data[1]);

  /* Allocate memory, then convert coord. to double */
  l=(dblPointList*) gpmalloc(nl*sizeof(dblPointList));
  for (i=0; i<nl-1; i+=2)
  {
    u=i+1;
    tx = typ(x=(GEN)data[u]);
    ty = typ(y=(GEN)data[u+1]);
    if (!is_vec_t(tx) || !is_vec_t(ty)) err(ploter4);
    lx=lg(x); if (lg(y)!=lx) err(ploter5);
    if (!param && lx != lx1) err(ploter5);
    lx--;

    if (lx)
    {
      l[i].d = (double*) gpmalloc(lx*sizeof(double));
      l[u].d = (double*) gpmalloc(lx*sizeof(double));

      for (j=0; j<lx; j=v)
      {
	v=j+1;
	l[i].d[j]=gtodouble((GEN)x[v]);
	l[u].d[j]=gtodouble((GEN)y[v]);
      }
    }
    l[i].nb=l[u].nb=lx;
  }

  xsml=xbig=l[0].d[0]; ysml=ybig=l[1].d[0];

  /* Now compute extremas */
  if (param)
  {
    l[0].nb=nl/2;
    for (i=0; i<l[0].nb; i+=2)
    {
      u=i+1;
      for (j=0; j<l[u].nb; j++)
      {
	if (l[i].d[j]<xsml)
	  xsml=l[i].d[j];
	else
	  if (l[i].d[j]>xbig) xbig=l[i].d[j];

	if (l[u].d[j]<ysml)
	  ysml=l[u].d[j];
	else
	  if (l[u].d[j]>ybig) ybig=l[u].d[j];
      }
    }
  }
  else
  {
    l[0].nb=nl-1;
    for (j=0; j<l[1].nb; j++)
    {
      if (l[0].d[j]<xsml)
	xsml=l[0].d[j];
      else
	if (l[0].d[j]>xbig) xbig=l[0].d[j];
    }
    for (i=1; i<=l[0].nb; i++)
    {
      for (j=0; j<l[i].nb; j++)
      {
	if (l[i].d[j]<ysml)
	  ysml=l[i].d[j];
	else
	  if (l[i].d[j]>ybig) ybig=l[i].d[j];
      }
    }
  }
  l[0].xsml=xsml; l[0].xbig=xbig;
  l[0].ysml=ysml; l[0].ybig=ybig;
  return l;
}

static void
single_recursion(dblPointList *pl,char *ch,entree *ep,GEN xleft,GEN yleft,
  GEN xright,GEN yright,long depth)
{
  GEN xx,yy;
  long av=avma;
  double dy=pl[0].ybig - pl[0].ysml;

  if (depth==RECUR_MAXDEPTH) return;

  yy=cgetr(3); xx=gdivgs(gadd(xleft,xright),2);
  ep->value = (void*) xx; gaffect(lisexpr(ch), yy);

  if (dy)
  {
    if (fabs(rtodbl(yleft)+rtodbl(yright)-2*rtodbl(yy))/dy < RECUR_PREC)
      return;
  }
  single_recursion(pl,ch,ep, xleft,yleft, xx,yy, depth+1);

  Appendx(&pl[0],&pl[0],rtodbl(xx));
  Appendy(&pl[0],&pl[1],rtodbl(yy));

  single_recursion(pl,ch,ep, xx,yy, xright,yright, depth+1);

  avma=av;
}

static void
param_recursion(dblPointList *pl,char *ch,entree *ep, GEN tleft,GEN xleft,
  GEN yleft, GEN tright,GEN xright,GEN yright, long depth)
{
  GEN tt,xx,yy, p1;
  long av=avma;
  double dy=pl[0].ybig - pl[0].ysml;
  double dx=pl[0].xbig - pl[0].xsml;

  if (depth==PARAMR_MAXDEPTH) return;

  xx=cgetr(3); yy=cgetr(3); tt=gdivgs(gadd(tleft,tright),2);
  ep->value = (void*)tt; p1=lisexpr(ch);
  gaffect((GEN)p1[1],xx); gaffect((GEN)p1[2],yy);

  if (dx && dy)
  {
    if (fabs(rtodbl(xleft)+rtodbl(xright)-2*rtodbl(xx))/dx < RECUR_PREC &&
       fabs(rtodbl(yleft)+rtodbl(yright)-2*rtodbl(yy))/dy < RECUR_PREC)
        return;
  }
  param_recursion(pl,ch,ep, tleft,xleft,yleft, tt,xx,yy, depth+1);

  Appendx(&pl[0],&pl[0],rtodbl(xx));
  Appendy(&pl[0],&pl[1],rtodbl(yy));

  param_recursion(pl,ch,ep, tt,xx,yy, tright,xright,yright, depth+1);

  avma=av;
}

/*
 *  Pure graphing. If testpoints is 0, it is set to the default.
 *  Returns a dblPointList of (absolute) coordinates.
 */
static dblPointList *
rectplothin(entree *ep, GEN a, GEN b, char *ch, long prec, ulong flags,
            long testpoints)
{
  long single_c;
  long param=flags & PLOT_PARAMETRIC;
  long recur=flags & PLOT_RECURSIVE;
  GEN p1,dx,x,xleft,xright,yleft,yright,tleft,tright;
  dblPointList *pl;
  long tx,av = avma,av2,i,j,sig,nc,nl,nbpoints;
  double xsml,xbig,ysml,ybig,fx,fy;

  if (!testpoints)
  {
    if (recur)
      testpoints = RECUR_NUMPOINTS;
    else
      testpoints = param? PARAM_NUMPOINTS : PLOTH_NUMPOINTS;
  }
  if (recur)
    nbpoints = testpoints << (param? PARAMR_MAXDEPTH : RECUR_MAXDEPTH);
  else
    nbpoints = testpoints;

  sig=gcmp(b,a); if (!sig) return 0;
  if (sig<0) { x=a; a=b; b=x; }
  dx=gdivgs(gsub(b,a),testpoints-1);

  x = cgetr(prec); gaffect(a,x); push_val(ep, x);
  av2=avma; p1=lisexpr(ch); tx=typ(p1);
  if (!is_matvec_t(tx))
  {
    xsml = gtodouble(a); xbig=gtodouble(b);
    ysml = ybig = gtodouble(p1);
    nc=1; nl=2; /* nc = nb of curves; nl = nb of coord. lists */
    if (param)
      err(warner,"flag PLOT_PARAMETRIC ignored");
    single_c=1; param=0;
  }
  else
  {
    if (tx != t_VEC) err(talker,"not a row vector in ploth");
    nl=lg(p1)-1; if (!nl) { avma=av; return 0; }
    single_c=0;
    if (param) nc=nl/2; else { nc=nl; nl++; }
    if (recur && nc > 1) err(talker,"multi-curves cannot be plot recursively");

    if (param)
    {
      xbig=xsml=gtodouble((GEN)p1[1]); ybig=ysml=gtodouble((GEN)p1[2]);
      for (i=3; i<=nl; i++)
      {
	fx=gtodouble((GEN)p1[i]); i++; fy=gtodouble((GEN)p1[i]);
	if (xsml>fx) xsml=fx; else if (xbig<fx) xbig=fx;
	if (ysml>fy) ysml=fy; else if (ybig<fy) ybig=fy;
      }
    }
    else
    {
      xsml=gtodouble(a); xbig=gtodouble(b);
      ysml=gtodouble(vecmin(p1)); ybig=gtodouble(vecmax(p1));
    }
  }

  pl=(dblPointList*) gpmalloc(nl*sizeof(dblPointList));
  for (i = 0; i < nl; i++)
  {
    pl[i].d = (double*) gpmalloc((nbpoints+1)*sizeof(dblPointList));
    pl[i].nb=0;
  }
  pl[0].xsml=xsml; pl[0].ysml=ysml; pl[0].xbig=xbig; pl[0].ybig=ybig;

  if (recur) /* recursive plot */
  {
    xleft=cgetr(3); xright=cgetr(3); yleft=cgetr(3); yright=cgetr(3);
    if (param)
    {
      tleft=cgetr(prec); tright=cgetr(prec);
      av2=avma;
      gaffect(a,tleft); ep->value = (void*)tleft; p1=lisexpr(ch);
      gaffect((GEN)p1[1],xleft); gaffect((GEN)p1[2],yleft);
      for (i=0; i<testpoints-1; i++)
      {
	if (i) {
	  gaffect(tright,tleft); gaffect(xright,xleft); gaffect(yright,yleft);
	 }
	gaddz(tleft,dx,tright); ep->value = (void*)tright; p1=lisexpr(ch);
        gaffect((GEN)p1[1],xright); gaffect((GEN)p1[2],yright);

	Appendx(&pl[0],&pl[0],rtodbl(xleft));
	Appendy(&pl[0],&pl[1],rtodbl(yleft));

	param_recursion(pl,ch,ep, tleft,xleft,yleft, tright,xright,yright, 0);
	avma=av2;
      }
      Appendx(&pl[0],&pl[0],rtodbl(xright));
      Appendy(&pl[0],&pl[1],rtodbl(yright));
    }
    else /* single_c */
    {
      av2=avma;
      gaffect(a,xleft); ep->value = (void*) xleft;
      gaffect(lisexpr(ch),yleft);
      for (i=0; i<testpoints-1; i++)
      {
        gaddz(xleft,dx,xright); ep->value = (void*) xright;
	gaffect(lisexpr(ch),yright);

	Appendx(&pl[0],&pl[0],rtodbl(xleft));
	Appendy(&pl[0],&pl[1],rtodbl(yleft));

        single_recursion(pl,ch,ep,xleft,yleft,xright,yright,0);
        avma=av2;
        gaffect(xright,xleft); gaffect(yright,yleft);
      }
      Appendx(&pl[0],&pl[0],rtodbl(xright));
      Appendy(&pl[0],&pl[1],rtodbl(yright));
    }
  }
  else /* non-recursive plot */
  {
    if (single_c)
      for (i=0; i<testpoints; i++)
      {
	p1 = lisexpr(ch);
	pl[0].d[i]=gtodouble(x);
	Appendy(&pl[0],&pl[1],gtodouble(p1));
	gaddz(x,dx,x); avma=av2;
      }
    else if (param)
    {
      long k;
      double z;

      for (i=0; i<testpoints; i++)
      {
	p1 = lisexpr(ch);
	for (j=0; j<nl; j=k)
	{
	  k=j+1; z=gtodouble((GEN)p1[k]);
	  Appendx(&pl[0], &pl[j],z);

	  j=k; k++; z=gtodouble((GEN)p1[k]);
	  Appendy(&pl[0], &pl[j],z);
	 }
	gaddz(x,dx,x); avma=av2;
      }
    }
    else /* plothmult */
      for (i=0; i<testpoints; i++)
      {
	p1 = lisexpr(ch);
	pl[0].d[i]=gtodouble(x);
	for (j=1; j<nl; j++) { Appendy(&pl[0],&pl[j],gtodouble((GEN)p1[j])); }
	gaddz(x,dx,x); avma=av2;
      }
  }
  pl[0].nb=nc; pop_val(ep); avma = av;
  return pl;
}

/*
 * Plot a dblPointList. Complete with axes, bounding box, etc.
 * We use two drawing rectangles: one for strings, another
 * for graphs.
 *
 * data is an array of structs. Its meaning depends on flags :
 *
 * + data[0] contains global extremas, the number of curves to plot
 *   (data[0].nb) and a list of doubles (first set of x-coordinates).
 *
 * + data[i].nb (i>0) contains the number of points in the list
 *   data[i].d (hopefully, data[2i].nb=data[2i+1].nb when i>0...)
 *
 * + If flags contain PLOT_PARAMETRIC, the array length should be
 *   even, and successive pairs (data[2i].d, data[2i+1].d) represent
 *   curves to plot.
 *
 * + If there is no such flag, the first element is an array with
 *   x-coordinates and the following ones contain y-coordinates.
 *
 * Additional flags: PLOT_NO_AXE_X, PLOT_NO_AXE_Y, PLOT_NO_FRAME.
 */

static GEN
rectplothrawin(long stringrect, long drawrect, dblPointList *data,
               long flags, PARI_plot *WW)
{
  PARI_plot W;
  GEN res;
  dblPointList y,x;
  double xsml,xbig,ysml,ybig,tmp;
  long ltype=0, ltop=avma;
  long is,js,i,lm,rm,tm,bm,nc,nbpoints, w[2], wx[2], wy[2];

  w[0]=stringrect; w[1]=drawrect;
  if (!data) return cgetg(1,t_VEC);
  x=data[0]; nc=x.nb; xsml=x.xsml; xbig=x.xbig; ysml=x.ysml; ybig=x.ybig;
  if (xbig-xsml < 1.e-9)
  {
    tmp=fabs(xsml)/10; if (!tmp) tmp=0.1;
    xbig+=tmp; xsml-=tmp;
  }
  if (ybig-ysml < 1.e-9)
  {
    tmp=fabs(ysml)/10; if (!tmp) tmp=0.1;
    ybig+=tmp; ysml-=tmp;
  }

  if (WW) /* no rectwindow has been supplied ==> ps or screen output */
  {
    W = *WW;
    lm = W.fwidth*10; /* left margin   */
    rm = W.hunit-1; /* right margin  */
    tm = W.vunit-1; /* top margin    */
    bm = W.vunit+W.fheight-1; /* bottom margin */

    is = W.width - (lm+rm); js = W.height - (tm+bm);
    wx[0]=wy[0]=0; wx[1]=lm; wy[1]=tm;
  /*
   * Window size (W.width x W.height) is given in pixels, and
   * correct pixels are 0..w_width-1.
   *
   * On the other hand, rect functions work with windows whose pixel
   * range is [0,width].
   */

    initrect(stringrect, W.width-1, W.height-1);
    if (drawrect != stringrect) initrect(drawrect, is-1, js-1);
  }

  if (!(flags & PLOT_NO_RESCALE))
    rectscale0(drawrect, xsml, xbig, ysml, ybig);

  if (!(flags & PLOT_NO_FRAME))
  {
    rectlinetype(drawrect, -2); 		/* Frame. */
    current_color[drawrect]=BLACK;
    rectmove0(drawrect,xsml,ysml,0);
    rectbox0(drawrect,xbig,ybig,0);
  }

  if (!(flags & PLOT_NO_AXE_Y) && (xsml<=0 && xbig >=0))
  {
    rectlinetype(drawrect, -1); 		/* Axes. */
    current_color[drawrect]=BLUE;
    rectmove0(drawrect,0.0,ysml,0);
    rectline0(drawrect,0.0,ybig,0);
  }

  if (!(flags & PLOT_NO_AXE_X) && (ysml<=0 && ybig >=0))
  {
    rectlinetype(drawrect, -1); 		/* Axes. */
    current_color[drawrect]=BLUE;
    rectmove0(drawrect,xsml,0.0,0);
    rectline0(drawrect,xbig,0.0,0);
  }

  if (flags & PLOT_PARAMETRIC) i=0; else i=1;
  current_color[drawrect]=RED;
  for (; ltype < nc; )
  {
    rectpointtype(drawrect,ltype); 	/* Graphs. */
    rectlinetype(drawrect,ltype++); 	/* Graphs. */
    if (nc>1)
    {
      if (ltype & 1) current_color[drawrect]=SIENNA;
      else current_color[drawrect]=RED;
    }
    if (flags & PLOT_PARAMETRIC) x=data[i++];

    y=data[i++]; nbpoints=y.nb;
    if (flags & PLOT_POINTS)
      rectpoints0(drawrect,x.d,y.d,nbpoints);
    else
      rectlines0(drawrect,x.d,y.d,nbpoints,0);
  }
  for (i--; i>=0; i--) free(data[i].d);
  free(data);

  if (WW)
  {
    char c1[16],c2[16],c3[16],c4[16];

    sprintf(c1,"%9.3f",ybig); sprintf(c2,"%9.3f",ysml);
    sprintf(c3,"%9.3f",xsml); sprintf(c4,"%9.3f",xbig);

    rectlinetype(stringrect,-2); /* Frame */
    current_color[stringrect]=BLACK;
    put_string(stringrect, 0, W.fheight - 1, c1);
    put_string(stringrect, 0, W.height - (bm+ 2 * W.vunit), c2);
    put_string(stringrect, lm-(W.fwidth*2), W.height-bm+W.fheight-1, c3);
    put_string(stringrect, W.width-(W.fwidth*10), W.height-bm+W.fheight-1,c4);

    if (flags & PLOT_POSTSCRIPT)
      postdraw0(w,wx,wy,2);
    else
      rectdraw0(w,wx,wy,2, 0);

    killrect(drawrect); if (stringrect != drawrect) killrect(stringrect);
  }

  avma=ltop;
  res = cgetg(5,t_VEC);
  res[1]=(long)dbltor(xsml); res[2]=(long)dbltor(xbig);
  res[3]=(long)dbltor(ysml); res[4]=(long)dbltor(ybig);
  return res;
}

/*************************************************************************/
/*                                                                       */
/*                          HI-RES FUNCTIONS                             */
/*                                                                       */
/*************************************************************************/

GEN
rectploth(long drawrect,entree *ep,GEN a,GEN b,char *ch,
          long prec,ulong flags,long testpoints)
{
  dblPointList *pl=rectplothin(ep, a,b, ch, prec, flags,testpoints);
  return rectplothrawin(0,drawrect, pl, flags,NULL);
}

GEN
rectplothraw(long drawrect, GEN data, long flags)
{
  dblPointList *pl=gtodblList(data,flags);
  return rectplothrawin(0,drawrect,pl,flags,NULL);
}

static PARI_plot*
init_output(long flags)
{
  if (flags & PLOT_POSTSCRIPT)
    { PARI_get_psplot(); return &pari_psplot; }
  else
    { PARI_get_plot(); return &pari_plot; }
}

static GEN
ploth0(long stringrect,long drawrect,entree *ep,GEN a,GEN b,char *ch,
             long prec,ulong flags,long testpoints)
{
  PARI_plot *output = init_output(flags);
  dblPointList *pl=rectplothin(ep, a,b, ch, prec, flags,testpoints);
  return rectplothrawin(stringrect,drawrect, pl, flags, output);
}

static GEN
plothraw0(long stringrect, long drawrect, GEN listx, GEN listy, long flags)
{
  PARI_plot *output = init_output(flags);
  long data[] = {evaltyp(t_VEC) | evallg(3), 0, 0};
  dblPointList *pl;

  data[1] = (long) listx;
  data[2] = (long) listy;
  pl=gtodblList(data,PLOT_PARAMETRIC);
  if (!pl) return cgetg(1,t_VEC);
  return rectplothrawin(stringrect,drawrect,pl,flags | PLOT_PARAMETRIC,output);
}

GEN
plothraw(GEN listx, GEN listy, long flags)
{
  flags=(flags)? 0: PLOT_POINTS;
  return plothraw0(STRINGRECT, DRAWRECT, listx, listy, flags);
}

GEN
ploth(entree *ep, GEN a, GEN b, char *ch, long prec,long flags,long numpoints)
{
  return ploth0(STRINGRECT, DRAWRECT, ep,a,b,ch,prec,flags,numpoints);
}

GEN
ploth2(entree *ep, GEN a, GEN b, char *ch, long prec)
{
  return ploth0(STRINGRECT, DRAWRECT, ep,a,b,ch,prec,PLOT_PARAMETRIC,0);
}

GEN
plothmult(entree *ep, GEN a, GEN b, char *ch, long prec)
{
  return ploth0(STRINGRECT, DRAWRECT, ep,a,b,ch,prec,0,0);
}

GEN
postplothraw(GEN listx, GEN listy, long flags)
{
  flags=(flags)? 0: PLOT_POINTS;
  return plothraw0(STRINGRECT, DRAWRECT, listx, listy, flags|PLOT_POSTSCRIPT);
}

GEN
postploth(entree *ep, GEN a, GEN b, char *ch, long prec,long flags,
           long numpoints)
{
  return ploth0(STRINGRECT,DRAWRECT,ep,a,b,ch,prec,flags|PLOT_POSTSCRIPT,
                numpoints);
}

GEN
postploth2(entree *ep, GEN a, GEN b, char *ch, long prec,
           long numpoints)
{
  return ploth0(STRINGRECT,DRAWRECT,ep,a,b,ch,prec,
		PLOT_PARAMETRIC|PLOT_POSTSCRIPT,numpoints);
}

GEN
plothsizes()
{
  GEN vect = cgetg(1+6,t_VEC);
  int i;

  PARI_get_plot();
  for (i=1; i<7; i++) vect[i]=lgeti(3);
  affsi(w_width,(GEN)vect[1]); affsi(w_height,(GEN)vect[2]);
  affsi(h_unit, (GEN)vect[3]); affsi(v_unit, (GEN)vect[4]);
  affsi(f_width,(GEN)vect[5]); affsi(f_height,(GEN)vect[6]);
  return vect;
}	

/*************************************************************************/
/*                                                                       */
/*                         POSTSCRIPT OUTPUT                             */
/*                                                                       */
/*************************************************************************/

typedef struct spoint {
  int x,y; } SPoint;
typedef struct ssegment {
  int x1,y1,x2,y2; } SSegment;
typedef struct srectangle {
  int x,y,width,height; } SRectangle;

static void ps_point(FILE *psfile, int x, int y);
static void ps_line(FILE *psfile, int x1, int y1, int x2, int y2);
static void ps_rect(FILE *psfile, int x1, int y1, int x2, int y2);
static void ps_string(FILE *psfile, int x, int y, char *c);

#undef ISCR
#undef JSCR
#define ISCR 1120 /* 1400 en haute resolution */
#define JSCR 800  /* 1120 en haute resolution */

static void
PARI_get_psplot()
{
  pari_psplot.height = JSCR - 60;
  pari_psplot.width  = ISCR - 40;
  pari_psplot.fheight = 15;
  pari_psplot.fwidth = 6;
  pari_psplot.hunit = 5;
  pari_psplot.vunit = 5;
}

static void
gendraw(GEN list,long ps)
{
  long i,n,ne,*w,*x,*y;
  GEN x0,y0,win;

  if (typ(list) != t_VEC) err(talker,"not a vector in rectdraw");
  n = lg(list)-1;
  if (n%3) err(talker,"incorrect number of components in rectdraw");
  n = n/3; if (!n) return;
  w = (long*)gpmalloc(n*sizeof(long));
  x = (long*)gpmalloc(n*sizeof(long));
  y = (long*)gpmalloc(n*sizeof(long));
  for (i=0; i<n; i++)
  {
    win=(GEN)list[3*i+1]; x0=(GEN)list[3*i+2]; y0=(GEN)list[3*i+3];
    if (typ(win)!=t_INT || typ(x0)!=t_INT || typ(y0)!= t_INT)
      err(talker, "not an integer type in rectdraw");
    ne=itos(win); check_rect(ne);
    x[i]=itos(x0); y[i]=itos(y0); w[i]=ne;
  }
  if (ps) postdraw0(w,x,y,n); else rectdraw0(w,x,y,n, 1);
  free(x); free(y); free(w);
}

void
postdraw(GEN list) { gendraw(list,1); }

void
rectdraw(GEN list) { gendraw(list,0); }

void
postdraw0(long *w, long *x, long *y, long lw)
{
  long *ptx,*pty,*numpoints,*numtexts,*xtexts,*ytexts;
  RectObj *p1;
  Rect *e;
  long i,j,x0,y0;
  long a,b,c,d,nd[ROt_MAX+1];
  char **texts;
  FILE *psfile;

  SPoint *points, **lines, *SLine;
  SSegment *seg;
  SRectangle *rect, SRec;

  psfile = fopen(current_psfile, "a");
  if (!psfile) 
    err(openfiler,"postscript",current_psfile);

  for (i=0; i<=ROt_MAX; i++) nd[i]=0;

  for (i=0; i<lw; i++)
  {
    e=rectgraph[w[i]]; p1=RHead(e);
    while (p1)
    {
      if (RoType(p1) != ROt_MP) nd[RoType(p1)]++;
      else nd[ROt_PT]+=RoMPcnt(p1);
      p1=RoNext(p1);
    }
  }
  points=(SPoint*) gpmalloc(nd[ROt_PT]*sizeof(SPoint));
  seg=(SSegment*) gpmalloc(nd[ROt_LN]*sizeof(SSegment));
  rect=(SRectangle*) gpmalloc(nd[ROt_BX]*sizeof(SRectangle));
  lines=(SPoint**) gpmalloc(nd[ROt_ML]*sizeof(SPoint*));
  numpoints=(long*) gpmalloc(nd[ROt_ML]*sizeof(long));
  texts=(char**) gpmalloc(nd[ROt_ST]*sizeof(char*));
  numtexts=(long*) gpmalloc(nd[ROt_ST]*sizeof(long));
  xtexts = (long*) gpmalloc(nd[ROt_ST]*sizeof(long));
  ytexts = (long*) gpmalloc(nd[ROt_ST]*sizeof(long));
  for (i=0; i<=ROt_MAX; i++) nd[i]=0;

  for (i=0; i<lw; i++)
  {
    e=rectgraph[w[i]]; p1=RHead(e); x0=x[i]; y0=y[i];
    while (p1)
    {
      switch(RoType(p1))
      {
	case ROt_PT:
	  points[nd[ROt_PT]].x=RoPTx(p1)+x0;
	  points[nd[ROt_PT]].y=RoPTy(p1)+y0;
	  nd[ROt_PT]++; break;
	case ROt_LN:
	  seg[nd[ROt_LN]].x1=RoLNx1(p1)+x0;
	  seg[nd[ROt_LN]].y1=RoLNy1(p1)+y0;
	  seg[nd[ROt_LN]].x2=RoLNx2(p1)+x0;
	  seg[nd[ROt_LN]].y2=RoLNy2(p1)+y0;
	  nd[ROt_LN]++; break;
	case ROt_BX:
	  a=rect[nd[ROt_BX]].x=RoBXx1(p1)+x0;
	  b=rect[nd[ROt_BX]].y=RoBXy1(p1)+y0;
	  rect[nd[ROt_BX]].width =RoBXx2(p1)+x0-a;
	  rect[nd[ROt_BX]].height=RoBXy2(p1)+y0-b;
	  nd[ROt_BX]++; break;
	case ROt_MP:
	  ptx=RoMPxs(p1); pty=RoMPys(p1);
	  for (j=0; j<RoMPcnt(p1); j++)
	  {
	    points[nd[ROt_PT]+j].x=ptx[j]+x0;
	    points[nd[ROt_PT]+j].y=pty[j]+y0;
	  }
	  nd[ROt_PT]+=RoMPcnt(p1); break;
	case ROt_ML:
	  ptx=RoMLxs(p1); pty=RoMLys(p1);
	  numpoints[nd[ROt_ML]]=RoMLcnt(p1);
	  lines[nd[ROt_ML]]=(SPoint*)gpmalloc(RoMLcnt(p1)*sizeof(SPoint));
	  for (j=0; j<RoMLcnt(p1); j++)
	  {
	    lines[nd[ROt_ML]][j].x=ptx[j]+x0;
	    lines[nd[ROt_ML]][j].y=pty[j]+y0;
	  }
	  nd[ROt_ML]++; break;
	case ROt_ST:
	  texts[nd[ROt_ST]]=RoSTs(p1);
	  numtexts[nd[ROt_ST]]=RoSTl(p1);
	  xtexts[nd[ROt_ST]]=RoSTx(p1)+x0;
	  ytexts[nd[ROt_ST]]=RoSTy(p1)+x0;
	  nd[ROt_ST]++; break;
	default: break;
      }
      p1=RoNext(p1);
    }
  }
  fprintf(psfile,"%%!\n50 50 translate\n/Times-Roman findfont 16 scalefont setfont\n0.65 0.65 scale\n");
  for (i=0; i<nd[ROt_PT]; i++)
    ps_point(psfile,points[i].x,points[i].y);
  for (i=0; i<nd[ROt_LN]; i++)
    ps_line(psfile,seg[i].x1,seg[i].y1,seg[i].x2,seg[i].y2);
  for (i=0; i<nd[ROt_BX]; i++)
  {
    SRec=rect[i]; a=SRec.x; b=SRec.y; c=a+SRec.width;
    d=b+SRec.height; ps_rect(psfile,a,b,c,d);
  }
  for (i=0; i<nd[ROt_ML]; i++)
  {
    SLine=lines[i];
    for (j=0; j<numpoints[i]; j++)
    {
      if (!j) fprintf(psfile,"%d %d moveto\n",SLine[0].y,SLine[0].x);
      else fprintf(psfile,"%d %d lineto\n",SLine[j].y,SLine[j].x);
    }
  }
  for (i=0; i<nd[ROt_ST]; i++)
    ps_string(psfile,xtexts[i],ytexts[i],texts[i]);
  fprintf(psfile,"stroke showpage\n"); fclose(psfile);
#define xfree(pointer) if (pointer) free(pointer)
  xfree(points); xfree(seg); xfree(rect); xfree(numpoints);
  for (i=0; i<nd[ROt_ML]; i++) xfree(lines[i]);
  xfree(lines); xfree(texts); xfree(numtexts); xfree(xtexts); xfree(ytexts);
#undef xfree
}

static void
ps_point(FILE *psfile, int x, int y)
{
  fprintf(psfile,"%d %d moveto\n0 2 rlineto 2 0 rlineto 0 -2 rlineto closepath fill\n",y,x);
}

static void
ps_line(FILE *psfile, int x1, int y1, int x2, int y2)
{
  fprintf(psfile,"%d %d moveto\n%d %d lineto\n",y1,x1,y2,x2);
}

static void
ps_rect(FILE *psfile, int x1, int y1, int x2, int y2)
{
  fprintf(psfile,"%d %d moveto\n%d %d lineto\n%d %d lineto\n%d %d lineto\nclosepath\n",y1,x1,y1,x2,y2,x2,y2,x1);
}

static void
ps_string(FILE *psfile, int x, int y, char *c)
{
  fprintf(psfile,"%d %d moveto 90 rotate\n(%s) show -90 rotate\n",y,x,c);
}
