#define	INF	1.e+37
#define	F	.25

/* following structure is arranged as two interleaved structures,
 * one for x and one for y to permit growing core point-by-point
 * It would be better to separate out the front matter from the
 * val substructure and so eliminate much of this funny business
*/

#define NT 8	/*sleazy way to tell size of struct to transpose*/
struct xy {
	int	xlbf,xubf,	ylbf,yubf;	int pada;
	int	xqf,pad1,	yqf,pad2;	int padb;
	float	xa,		ya;		int padc;
	float	xb,		yb;		int padd;
	float	xlb,		ylb;		int pade;
	float	xub,		yub;		int padf;
	float	xquant,		yquant;		int padg;
	float	xmult,		ymult;		int padh;
	struct {
		float xv,	yv;		int lbptr;
	} val[1];
} *xy;

/* labels are stored in a floating buffer at the
 * top of memory, slab=size, nlab=amt in use
*/
int slab 0;
int nlab 0;
#define B 64
#define BUMP (B*sizeof(xy->val[0]))

float xsize 1.;
float ysize 1.;
float xoffset 0.;
float yoffset 0.;
int tick 50;
int top 1940;
int bot -1860;
int xbot;
int ybot;
float absbot;
int cx,cy,ty;
int	n;
int	framef;
int	gridf;
int	connf;
int	symbf;
int	absf;
int	transf;
float	dx;
char	*plotsymb;

double	atof();
double	floor();
double	ceil();
char 	*sbrk();
int	fin;
#define BSIZ 80
char	labbuf[BSIZ];
char	titlebuf[BSIZ];

main(argc,argv)
char *argv[];
{
	extern int fout;

	fout = 1;
	space(-2048,-2048,2048,2048);
	xy = sbrk( sizeof(xy[0]) );
	setopt(argc,argv);
	if(framef)
		erase();
	readin();
	transpose();
	scale();
	axes();
	plot();
	move(-2047,-2047);
	closevt();
	return(0);
}

setopt(argc,argv)
char *argv[];
{
	int hflag;
	char *p1, *p2;

	hflag = 0;
	framef = 1;
	connf = 1;
	gridf = 2;
	symbf = 0;
	absf = 0;
	xy->xlbf = xy->xubf = xy->ylbf = xy->yubf = 0;
	xy->xqf = xy->yqf = 0;
	xy->xlb = xy->ylb = INF;
	xy->xub = xy->yub = -INF;
	xy->val[0].xv = xy->val[0].yv = 0;
	while(--argc > 0) {
		argv++;
again:		switch(argv[0][0]) {
		case '-':
			argv[0]++;
			goto again;
		case 'l': /* label for plot */
			p1 = titlebuf;
			if (argc>=2) {
				argv++;
				argc--;
				p2 = argv[0];
				while (*p1++ = *p2++);
			}
			break;


		case 'a': /*automatic abscissas*/
			absf = 1;
			dx = 1;
			if(!numb(&dx,&argc,&argv)) break;
			if(numb(&absbot,&argc,&argv))
				absf = 2;
			break;

		case 'd':/*disconnected, no lines between points*/
			connf = 0;
			break;

		case 's': /*save screen, overlay plot*/
			framef = 0;
			break;

		case 'g': /*grid style 0 none, 1 ticks, 2 full*/
			gridf = 0;
			if(argv[0][1])
				gridf = argv[0][1]-'0';
			break;

		case 'c': /*character(s) for plotting*/
			if(argc >= 2) {
				symbf = 1;
				plotsymb = argv[1];
				argv++;
				argc--;
			}
			break;

		case 't':	/*transpose*/
			transf = 1;
			break;
		case 'x':	/*x limits */
			limread(&xy->xlbf,&argc,&argv);
			break;
		case 'y':
			limread(&xy->ylbf,&argc,&argv);
			break;
		case 'h': /*set height of plot */
			numb(&ysize, &argc,&argv);
			break;
		case 'w': /*set width of plot */
			numb(&xsize, &argc, &argv);
			break;
		case 'r': /* set offset to right */
			numb(&xoffset, &argc, &argv);
			break;
		case 'u': /*set offset up the screen*/
			numb(&yoffset,&argc,&argv);
			break;
		default:
			badarg();
		}
	}
}

limread(p, argcp, argvp)
register struct xy *p;
{
	if(!numb(&p->xlb,argcp,argvp))
		return;
	p->xlbf = 1;
	if(!numb(&p->xub,argcp,argvp))
		return;
	p->xubf = 1;
	if(!numb(&p->xquant,argcp,argvp))
		return;
	p->xqf = 1;
}

numb(np, argcp, argvp)
int *argcp;
float *np;
register char ***argvp;
{
	register char c;

	if(*argcp <= 1)
		return(0);
	while((c=(*argvp)[1][0]) == '+')
		(*argvp)[1]++;
	if(!(digit(c) || c=='-'&&(*argvp)[1][1]<'A' || c=='.'))
		return(0);
	*np = atof((*argvp)[1]);
	(*argcp)--;
	(*argvp)++;
	return(1);
}

readin()
{
	register i;
	register t;

	fin = dup(0);
	close(0);
	if(xy->xlbf && absf==1)
		absbot = xy->xlb;
	for(;;) {
		if(more()==0)
			return;
		for(i=0; i<B; i++) {
			if(absf)
				xy->val[n].xv = n*dx + absbot;
			else
				if(!getfloat(&xy->val[n].xv))
					return;
			if(!getfloat(&xy->val[n].yv))
				return;
			xy->val[n].lbptr = -1;
			t = getstring();
			if(t>0)
				xy->val[n].lbptr = copystring(t);
			n++;
			if(t<0)
				return;
		}
	}
}

transpose()
{
	register i;
	float f;
	if(!transf)
		return;
	for(i= -NT;i<n;i++) {
		f = xy->val[i].xv;
		xy->val[i].xv = xy->val[i].yv;
		xy->val[i].yv = f;
	}
}

more()
{
	register i;
	register char *p;

	if(sbrk(BUMP) == -1) {
		write(2,"points omitted\n",15);
		return(0);
	}
	p = sbrk(0);
	for(i=0;i<slab;i++) {
		--p;
		p[0] = p[-BUMP];
	}
	return(1);
}

copystring(k)
{
	register char *p;
	register i;
	int q;

	if(k+1+nlab>slab) {
		if(more()==0)
			return;
		slab =+ BUMP;
	}
	p = sbrk(0);
	q = nlab;
	for(i=0;i<=k;i++)
		*(p-slab+nlab++) = labbuf[i];
	return(q);
}

float
modceil(f,t)
float f,t;
{

	return(ceil(f/t)*t);
}

float
modfloor(f,t)
float f,t;
{
	return(floor(f/t)*t);
}

getlim(p)
register struct xy *p;
{
	register i;

	i = 0;
	do {
		if(!p->xlbf && p->xlb>(p->val[i].xv))
			p->xlb = p->val[i].xv;
		if(!p->xubf && p->xub<(p->val[i].xv))
			p->xub = p->val[i].xv;
		i++;
	} 
	while(i < n);
}

setlim(p)
register struct xy *p;
{
	float r,s,t,delta,sign;
	float lb,ub;
	int lbf,ubf;

	lb = p->xlb;
	ub = p->xub;
	delta = ub-lb;
	if(p->xqf) {
		if(delta*p->xquant <=0 )
			badarg();
		p->xmult = 1;
		return;
	}
	if(delta == 0) {
		if(ub > 0) {
			ub = 2*ub;
			lb = 0;
		} 
		else
			if(lb < 0) {
				lb = 2*lb;
				ub = 0;
			} 
			else {
				ub = 1;
				lb = -1;
			}
		delta = ub-lb;
	}
	sign = 1;
	lbf = p->xlbf;
	ubf = p->xubf;
	if(delta < 0) {
		sign = -1;
		t = lb;
		lb = ub;
		ub = t;
		t = lbf;
		lbf = ubf;
		ubf = t;
		delta = -delta;
	}
	r = s = 1;
	while(delta*s < 1)
		s =* 10;
	delta =* s;
	while(10*r < delta)
		r =* 10;
	lb =* s;
	ub =* s;
	if(r>=5*delta/6)
		r =/ 5;
	else if(r>=delta/2)
		r =/ 2;
	else if(r<delta/5)
		r =* 2;
	if(!ubf)
		ub = modceil(ub,r);
	if(!lbf)
		lb = modfloor(lb,r);
	if(!lbf && ub<=6*r && lb>0)
		lb = 0;
	else if(!ubf && lb>=-6*r && ub<0)
		ub = 0;
	if(sign > 0) {
		p->xlb = lb;
		p->xub = ub;
		p->xquant = r;
	} 
	else {
		p->xlb = ub;
		p->xub = lb;
		p->xquant = -r;
	}
	p->xmult = s;
}

scale()
{
	float edge;
	register struct xy *p;

	p = xy;
	getlim(&p->xlbf);
	setlim(&p->xlbf);
	getlim(&p->ylbf);
	setlim(&p->ylbf);
	edge = top-bot;
	p->xa = xsize*edge/(xy->xub-xy->xlb);
	xbot = bot + edge*xoffset;
	p->xb = xbot - xy->xlb*xy->xa;
	p->ya = ysize*edge/(p->yub-p->ylb);
	ybot = bot + edge*yoffset;
	p->yb = ybot - p->ylb*p->ya;
}

axes()
{
	int ix,ix0,ix1,iy,iy0,iy1;
	float h,v;
	extern float quant();

	ix0 = xbot;
	iy0 = ybot;
	ix1 = xbot + (top-bot)*xsize;
	iy1 = ybot + (top-bot)*ysize;
	if(gridf==2) {
		for(h=quant(&xy->xlbf); lim(h,&xy->xlbf); h=+xy->xquant) {
			ix = h*xy->xa+xy->xb;
			line(ix,iy0,ix,iy1);
		}
		for(v=quant(&xy->ylbf); lim(v,&xy->ylbf); v=+xy->yquant) {
			iy = v*xy->ya+xy->yb;
			line(ix0,iy,ix1,iy);
		}
	}
	if(gridf==1) {
		h = 0;
		if((xy->xlb < 0 && xy->xub <= 0) ||
		   (xy->xlb >= 0 && xy->xub >= 0))
			h = xy->xlb;
		ix = h*xy->xa+xy->xb;
		line(ix,iy0,ix,iy1);
		for(v=quant(&xy->ylbf); lim(v,&xy->ylbf); v=+xy->yquant) {
			iy = v*xy->ya+xy->yb;
			line(ix-tick,iy,ix+tick,iy);
		}
		v = 0;
		if((xy->ylb < 0 && xy->yub <= 0) || (xy->ylb >= 0 && xy->yub >= 0))
			v = xy->ylb;
		iy = v*xy->ya+xy->yb;
		line(ix0,iy,ix1,iy);
		for(h=quant(&xy->xlbf); lim(h,&xy->xlbf); h=+xy->xquant) {
			ix = h*xy->xa+xy->xb;
			line(ix,iy-tick,ix,iy+tick);
		}
	}
	if(gridf)
		title();
}


float quant(p)
register struct xy *p;
{
	if(p->xquant>0)
		return(modceil(p->xlb,p->xquant));
	else
		return(modfloor(p->xlb,p->xquant));
}

lim(l,p)
float l;
struct xy *p;
{

	l =- p->xub + p->xquant/2;
	if(p->xquant > 0)
		return(l<0);
	return(l>0);
}

plot()
{
	int ix,iy,lx,ly;
	int i;
	int first;

	first = 0;
	for(i=0; i<n; i++) {
		if(!inlim(xy->val[i].xv,&xy->xlbf)||
		   !inlim(xy->val[i].yv,&xy->ylbf)) {
			first = 0;
			continue;
		}
		ix = xy->xa*xy->val[i].xv*xy->xmult+xy->xb;
		iy = xy->ya*xy->val[i].yv*xy->ymult+xy->yb;
		if(connf) {
			if(first > 0)
				line(ix,iy,lx,ly);
			lx = ix;
			ly = iy;
			first = 1;
		}
		symbol(ix,iy,xy->val[i].lbptr);
	}
}

inlim(xv,p)
float xv;
register struct xy *p;
{
	if(!p->xlbf&&!p->xubf)
		return(1);
	if(p->xquant >0)
		return(xv>=p->xlb&xv<=p->xub);
	else
		return(xv>=p->xub&xv<=p->xlb);
}

char savchar ' ';
getfloat(p)
float *p;
{
	register i;

	while(blank(savchar)) {
		if((savchar=getchar())==0)
			return(0);
	}
	for(i=0; i<BSIZ-1; ) {
		labbuf[i] = savchar;
		if((savchar=getchar())==0)
			return(0);
		i++;
		if(digit(savchar))
			continue;
		switch(savchar) {
		case '.':
		case '+':
		case '-':
		case 'E':
		case 'e':
			continue;
		}
		break;
	}
	labbuf[i] = ' ';
	*p = atof(labbuf);
	return(1);
}

getstring()
{
	register i;

	i = 0;
	labbuf[0] = 0;
	while(blank(savchar)) {
		if((savchar=getchar())==0)
			return(-1);
	}
	if(digit(savchar))
		return(0);
	switch(savchar) {
	case '.':
	case '+':
	case '-':
		return(0);
	}
	if(savchar=='"') {
		while(i<BSIZ-1) {
			switch(savchar=getchar()){
			case 0:
				return(-1);
			case '"':
				savchar = ' ';
			case '\n':
				break;
			default:
				labbuf[i++] = savchar;
				continue;
			}
			break;
		}
	} else {
		for(;;) {
			labbuf[i++] = savchar;
			if((savchar=getchar())==0)
				return(-1);
			if(blank(savchar))
				break;
		}
	}
	labbuf[i] = 0;
	return(i);
}

digit(c)
{
	return('0'<=c&c<='9');
}

blank(c)
{
	switch(c) {
	case ' ':
	case '\t':
	case '\n':
		return(1);
	}
	return(0);
}

symbol(ix,iy,k)
{

	if(symbf==0&&k<0) {
		if(connf==0)
			point(ix,iy);
	} 
	else {
		move(ix,iy);
		label(k>=0?sbrk(0)-slab+k:plotsymb);
	}
}

title()
{
	move(xbot,ybot-60);
	bound(xy->xlb,&xy->xlbf);
	label(" -x- ");
	bound(xy->xub,&xy->xlbf);
	label("      ");
	bound(xy->ylb,&xy->ylbf);
	label(" -y- ");
	bound(xy->yub,&xy->ylbf);
	if (titlebuf[0]) {
		label("       ");
		label(titlebuf);
	}
}

char	putbuf[30];
int	putpos;

bound(f,p)
float f;
struct xy *p;
{

	fconv(putbuf,f/p->xmult);
	label(putbuf);
}

fconv(cp,f)
	double f;
	char *cp;
	{
	char *cdig;
	int decpt, sign, i;
	cdig = fcvt(f, 6, &decpt, &sign);
	if (sign)
		*cp++ = '-';
	if(decpt<0) 
		*cp++ = '.';
	for(i=0;i>decpt;i--)
		*cp++ = '0';
	for (i=0; i < 6; i++)
		{
		if (i== decpt)
			*cp++ = '.';
		*cp++ = *cdig++;
		}
	*cp = '\0';
	}

badarg()
{
	write(2,"bad argument\n",13);
	exit(1);
}
