#include <stdio.h>
#include <math.h>
#include <setjmp.h>
#include <Arthur.h>
#include "defs.h"

extern double atof(char *);

extern char *save(char *);
extern struct arrele *createarray(int);
extern struct arrele *concat(struct arrele *,struct arrele *,struct arrele *);
extern struct arrele *pullarray(int);
extern void dstrip (struct usere *);
extern void single (char *);
extern void docom(struct idlist *, int);

extern int strcmp(char *, char *);
extern int calloc(int, int);
extern int strlen(char *);

struct gstate gstack[32];
struct stelem mstack[500];
int gpo=0;
struct arrele ttq[6],ttq2[6];
int bw=0;
int globc;
struct userdict *dictstack[20];
int doffset=0;
struct idlist *idbase=NULL;
int used[230];
static struct etr *et[256];
struct etr *etbase;
int pths,etc;
double dgc=57.2957795;
double ox1,oy1,ox2,oy2;
double etx;
double mcx,mcy,mzx,mzy;
int clsd;
int ety;
int ymin, ymax;
int fflg=0;
int oldang;
int abc;
int exx=1;
int ab=0;
int on=0;
int to;
int escpress = 0;
jmp_buf env;
int flag = 0;
int ind = 0;
int cb = 0;
int rb = 0;
int esc = 0;
int offset = -1;
double cx = 0;
double cy = 0;
int cpath = 0;
int drti = 0;
double cline;
double freq,ang,prc;
double tim = 0.03125;
double tma,tmb,tmc,tmd,tmx,tmy;
int pjoin=0;
double t1[32],t2[32],t3[32];
double sintab[720],costab[720];

char acc[500];

struct gpath *baseg = NULL;
struct gpath *nextg = NULL;
struct gstate *currgs = NULL;

double x,y;

struct arrele *ctm;

struct stelem *current = NULL;

double pullnum(void);
void pushnum(double);
void dooproc(struct arrele *);
void seterror(char *);

char *systemdict[] ={
        "[","]",
        "abs","add","aload","anchorsearch","and","arc","arcn","arcto",
        "array","ashow","astore","atan","awidthshow","banddevice",
        "begin","bind","bitshift","bytesavailable","cachestatus",
        "ceiling","charpath","clear","cleartomark","clip","clippath",
        "closefile","closepath","concat","concatmatrix","copy",
        "copypage","cos","count","countdictstack","countexecstack",
        "counttomark","currentdash","currentdict","currentfile",
        "currentflat","currentfont","currentgray","currenthsbcolor",
        "currentlinecap","currentlinejoin","currentlinewidth",
        "currentmatrix","currentmiterlimit","currentpoint",
        "currentrgbcolor","currentscreen","currenttransfer",
        "curveto","cvi","cvlit","cvn","cvr","cvrs","cvs","cvx",
        "def","defaultmatrix","definefont","dict","dictstack",
        "div","dtransform","dup","echo","end","eoclip","eofill",
        "eq","erasepage","errordict","exch","exec","execstack",
        "executeonly","exit","exp","false","file","fill","findfont",
        "flattenpath","floor","flush","flushfile","fontdirectory",
        "for","forall","framedevice","ge","get","getinterval",
        "grestore","grestoreall","gsave","gt","identmatrix",
        "idiv","idtransform","if","ifelse","image","imagemask",
        "index","initclip","initgraphics","initmatrix",
        "invertmatrix","itransform","known","kshow","le","length",
        "lineto","ln","load","log","loop","lt","makefont","mark",
        "matrix","maxlength","mod","moveto","mul","ne","neg",
        "newpath","noaccess","not","null","nulldevice","or",
        "pathbbox","pathforall","pop","print","prompt","pstack",
        "put","putinterval","quit","rand","rcheck",
        "rcurveto","read","readhexstring","readline","readonly",
        "readstring","renderbands","repeat","resetfile","restore",
        "reversepath","rlineto","rmoveto","roll","rotate","round",
        "rrand","run","save","scale","scalefont","search",
        "setcachedevice","setcachelimit","setcharwidth","setdash",
        "setflat","setfont","setgray","sethsbcolor","setlinecap",
        "setlinejoin","setlinewidth","setmatrix","setmiterlimit",
        "setrgbcolor","setscreen","settransfer","show",
        "showpage","sin","sqrt","srand","stack",
        "standardencoding","start","status","stop","stopped",
        "store","string","stringwidth","stroke","strokepath",
        "sub","systemdict","token","transform","translate","true",
        "truncate","type","userdict","usertime","version",
        "vmstatus","wcheck","where","widthshow","write",
        "writehexstring","writestring","xcheck","xor","xx",(char *)0 };

double rad(degrees)
double degrees;
{
        return(degrees/dgc);
}

double deg(radians)
double radians;
{
        return(radians*dgc);
}

char *ftos(number)
double number;
{
        char name[30];
        sprintf((char *)name, "%g", number);
        return((char *)save((char *)name));
}        

void getxy()
{
        y = pullnum();
        x = pullnum();
}

void setgray(val)
double val;
{
        pushnum(val);
        dooproc(currgs->curt);
        val=pullnum();
        if (val<0 || val>1) seterror("undefined");
        if (bw) gcol(0, (int)(val*16-(val==1)));
        else {
                gcol(0, 21*(int)(val*4-(val==1)));
                tint(2, (int)(val*16-(val==1)) % 4); }
}

void setcol()
{
        int c;
        int t=0;
        int d=3;
        int f;
        if (currgs->red == currgs->green && currgs->red == currgs->blue)
                setgray(currgs->red);
        else {
                c=(int)((int)(currgs->red*4)-(int)(currgs->red==1));
                f=(int)(c+(((int)(currgs->green*4)-(currgs->green==1)) << 2) +
                        (((int)(currgs->blue*4)-(currgs->blue==1)) << 4));
                gcol(0, f);
                if (currgs->red==0) d--;
                if (currgs->green==0) d--;
                if (currgs->blue==0) d--;
                if (bw) gcol(0, ((int)(currgs->red*16-(currgs->red==1))+
                                 (int)(currgs->green*16-(currgs->green==1))+
                                 (int)(currgs->blue*16-(currgs->blue==1)))/d);
                else {
                        t=(((int)(currgs->red*16-(currgs->red==1)) & 3)+
                             ((int)(currgs->green*16-(currgs->green==1)) & 3)+
                             ((int)(currgs->blue*16-(currgs->blue==1)) & 3))/d;
                        tint(2, t); }
                }
}

void doclg()
{
        if (bw) gcol(0, 15);
        else {
                gcol(0, 63);
                tint(2,3); }
        rectanglefill(0,0,718,1023);
        setcol();
}

struct usere *searchdic(match)
char *match;
{
        struct idlist *t = idbase;
        while (t != NULL && strcmp(match, t->name) != 0) t=t->next;
        if (t == NULL) return(NULL);
        else {
                int found=0;
                struct usere *g;
                globc=doffset;
                while (globc>=0 && !found) {
                        g=t->invoc;
                        while (g != NULL && g->id != dictstack[globc])
                                g=g->next;
                        if (g != NULL) found=1;
                        else --globc;
                        }
                return(g);
                }
}

void seterror(match)
char *match;
{
        struct usere *tuser;
        tuser=searchdic(match);
        if (tuser != NULL) {
                dstrip(tuser);
                longjmp(env, 1); }
        else {
                printf("%s\n", match);
                longjmp(env, 1); }
}

void undef()
{
        seterror("Not implemented");
}

int hex(ch)
char ch;
{
        if (ch >= 'a' && ch <= 'f') ch=ch-32;
        if (ch >= '0' && ch <= '9') return(ch-48);
        else if (ch >= 'A' && ch <= 'F') return(ch-55);
             else {
               seterror("rangecheck"); return(0); }
}

int tohex(ch)
int ch;
{
        return(48+ch+39*(ch>9));
}

void readctm(temp)
struct arrele *temp;
{
        tma = temp[0].u.number;
        tmb = temp[1].u.number;
        tmc = temp[2].u.number;
        tmd = temp[3].u.number;
        tmx = temp[4].u.number;
        tmy = temp[5].u.number;
}

int testm(arry)
struct arrele *arry;
{
        if (arry != NULL) return(arry->length == 6);
        else return(0);
}

void pull()
{
        if (offset > 0) {
                --offset;
                current = &mstack[offset];
                }
        else if (offset) seterror("stackunderflow");
             else {
                        --offset;
                        current = NULL;
                  }
}

struct idlist *pullname()
{
        struct idlist *temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != NAME) seterror("typecheck");
        temp = current->u.name;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

int pullbool()
{
        int temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != BOOLEAN) seterror("typecheck");
        temp = current->u.bool;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

struct strdef *pullrs()
{
        struct strdef *temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != STRING) seterror("typecheck");
        temp = current->u.restr;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

struct userdict *pulldict()
{
        struct userdict *temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != DICTIONARY) seterror("typecheck");
        temp = current->u.dict;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

void dpush()
{
        struct userdict *t=pulldict();
        if (doffset == 19) seterror("dictstackoverflow");
        doffset++;
        dictstack[doffset] = t;
}

struct userdict *dcreate(amount)
int amount;
{
        struct userdict *temp;
        temp = (struct userdict *)calloc(1, sizeof(struct userdict));
        temp->maxsize = amount;
        temp->size = 0;
        return(temp);
}

void pushst(elem)
struct stelem *elem;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        *current = *elem;
}

void pushnum(val)
double val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = NUMBER;
        current->u.number = val;
}

void pushname(val)
struct idlist *val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = NAME;
        current->u.name = val;
}

void pushoper(val, count)
struct idlist *val;
int count;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = OPERATOR;
        current->u.oper.k = count;
        current->u.oper.called = val;
}

void pushrs(val)
struct strdef *val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = STRING;
        current->u.restr = val;
}

void pushms(str, l)
char *str;
int l;
{
        struct strdef *new;
        new = (struct strdef *)calloc(1, sizeof(struct strdef));
        new->string = str;
        new->len = l;
        pushrs(new);
}

void pushdict(val)
struct userdict *val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = DICTIONARY;
        current->u.dict = val;
}

void pushstr(val, storna)
char *val;
int storna;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = storna;
        current->u.string = val;
}

void pushbool(val)
int val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = BOOLEAN;
        current->u.bool = val;
}

void pushfile(val)
struct phile *val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = FILETYPE;
        current->u.filo = val;
}

void pushmark()
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = MARK;
}

void pushpmark()
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = PMARK;
        current->u.string="PROCEDURE MARK";
}

void pusharray(val, which)
struct arrele *val;
int which;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = which;
        current->u.array = val;
}

void dooproc(start)
struct arrele *start;
{
        int c=0,l;
        if (start != NULL) l=start->length;
        else l=0;
        exx=1;
        while (c<l && exx) {
                switch(start[c].sort) {
                case NUMBER: pushnum(start[c].u.number);
                             break;
                case NAME: pushname(start[c].u.name);
                           break;
                case STRING: pushrs(start[c].u.restr);
                             break;
                case MARK: pushmark();
                           break;
                case BOOLEAN: pushbool(start[c].u.bool);
                              break;
                case DICTIONARY: pushdict(start[c].u.dict);
                                 break;
                case ARRAY: pusharray(start[c].u.array, ARRAY);
                            break;
                case FILETYPE: pushfile(start[c].u.filo);
                               break;
                case PROCEDURE: pusharray(start[c].u.array, PROCEDURE);
                                break;
                case OPERATOR: docom(start[c].u.oper.called,start[c].u.oper.k);
                               break;
                default: pushstr(start[c].u.string, start[c].sort);
                         break; }
                c++; }
}

void pushgraph()
{
        if (gpo==31) seterror("limitcheck");
        currgs->path = baseg;
        currgs->epath = nextg;
        currgs->ctma = tma;
        currgs->ctmb = tmb;
        currgs->ctmc = tmc;
        currgs->ctmd = tmd;
        currgs->ctmx = tmx;
        currgs->ctmy = tmy;
        currgs->cline = cline;
        currgs->cx = cx;
        currgs->cy = cy;
        gpo++;
        gstack[gpo]=gstack[gpo-1];
        currgs=&gstack[gpo];
}

void setnum(a, val)
struct arrele *a;
double val;
{
        a->sort = NUMBER;
        a->u.number = val;
}

void doaoe(which)
int which;
{
        int ans=0;
        int b;
        int b2;
        switch(current->sort) {
        case BOOLEAN: b=pullbool();
                      b2=pullbool();
                      switch(which) {
                      case 1: if (b && b2) ans=1; break;
                      case 2: if (b || b2) ans=1; break;
                      case 3: if (b ^ b2) ans=1; break; }
                      pushbool(ans);
                      break;
        case NUMBER: b=(int)pullnum();
                     switch(which) {
                     case 1: ans=((int)pullnum()) & b; break;
                     case 2: ans=((int)pullnum()) | b; break;
                     case 3: ans=((int)pullnum()) ^ b; break; }
                     pushnum((double)ans);
                     break;
        default: seterror("typecheck"); break; }
}

void setmat(arry,a,b,c,d,e,f)
struct arrele *arry;
double a,b,c,d,e,f;
{
        struct arrele *temp;
        temp = arry;
        setnum(&arry[0], a);
        setnum(&arry[1], b);
        setnum(&arry[2], c);
        setnum(&arry[3], d);
        setnum(&arry[4], e);
        setnum(&arry[5], f);
}

struct arrele *inverse(mat, dest)
struct arrele *mat;
struct arrele *dest;
{
        double a,b,c,d,e,f,det;
        a = mat[0].u.number;
        b = mat[1].u.number;
        c = mat[2].u.number;
        d = mat[3].u.number;
        e = mat[4].u.number;
        f = mat[5].u.number;
        det = a*d-b*c;
        if (det == 0) seterror("undefinedresult");
        setmat(dest,d/det,-b/det,-c/det,a/det,(c*f-d*e)/det,-(a*f-b*e)/det);
        return(dest);
}

void doinvmat()
{
        struct arrele *mat1;
        struct arrele *mat2;
        mat2 = (struct arrele *)pullarray(ARRAY);
        mat1 = (struct arrele *)pullarray(ARRAY);
        if (testm(mat1) && testm(mat2)) pusharray(inverse(mat1, mat2),ARRAY);
        else seterror("typecheck");
}

void pdub(g,mat)
struct gpath *g;
struct arrele *mat;
{
        double tx,ty;
        tx=g->xc;
        ty=g->yc;
        readctm(mat);
        pushnum(tma*tx+tmc*ty+tmx);
        pushnum(tmb*tx+tmd*ty+tmy);
        readctm(ctm);
}

void sttod(from, to)
struct stelem *from;
struct usere *to;
{
        to->sort = from->sort;
        to->u = from->u;
}

void transa(from, to)
struct arrele *from;
struct arrele *to;
{
        to->sort = from->sort;
        to->u = from->u;
}

struct arrele *whicht(tr)
int tr;
{
        int an;
        struct arrele *temp=&ttq[0];
        switch (tr) {
        case ROTATE: an=(int)(2*pullnum());
                     while (an > 720) an-=720;
                     while (an < 0) an+=720;
                     setmat(temp,costab[an],sintab[an],
                            -sintab[an],costab[an],0.0,0.0);
                     break;
        case TRANSLATE: getxy();
                        setmat(temp,1.0,0.0,0.0,1.0,x,y);
                        break;
        case SCALE: getxy();
                    setmat(temp,x,0.0,0.0,y,0.0,0.0);
                    break;
        default: break; }
        readctm(inverse(temp, &ttq2[0]));
        cx=tma*cx+tmc*cy+tmx;
        cy=tmb*cx+tmd*cy+tmy;
        readctm(ctm);
        return(temp);
}

void changectm(tr)
int tr;
{
        struct arrele *temp;
        switch(current->sort) {
        case ARRAY:  temp=(struct arrele *)pullarray(ARRAY);
                     if (testm(temp) == 0) seterror("typecheck");
                     else pusharray(concat(whicht(tr),temp,temp),ARRAY);
                     break;
        case NUMBER: concat(whicht(tr), ctm, ctm);
                     break;
        default: seterror("typecheck"); break; }
        readctm(ctm);
}

int cotm()
{
        int of=offset;
        while (of >= 0 && mstack[of].sort != MARK) --of;
        if (of<0) seterror("unmatchedmark");
        return(offset-of);
}

void pushele(val)
struct arrele *val;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = val->sort;
        current->u = val->u;
}

char *save(value)
char *value;
{
        char *c;
        int k = -1;
        c=(char *)calloc(1+strlen(value), sizeof(char));
        do {
                k++;
                c[k]=value[k]; }
        while (value[k]!=0);
        return(c);
}


struct arrele *createarray(n)
int n;
{
        struct arrele *temp = NULL;
        if (n>0) {
                temp = (struct arrele *)calloc(n, sizeof(struct arrele));
                temp->length = n; }
        return(temp);
}

void deletearray(mat)
struct arrele *mat;
{
        free(mat);
}

void stacktoarray(a)
struct arrele *a;
{
        a->sort = current->sort;
        a->u = current->u;
        pull();
}

void bbcgra(x,y,z)
double x,y;
int z;
{
plot(z,(int)x,(int)y);
}

void linejoin()
{
        switch(currgs->ljoin) {
        case 0:
                break;
        case 1:
                circlefill((int)currgs->ax,(int)currgs->ay,(int)cline);
                break;
        case 2:
                break; }
}

void linecap(theta, sx, sy, tx, ty)
int theta;
double sx,sy,tx,ty;
{
        double xx,yy,xz,yz;
        if (currgs->lcap)
                switch (currgs->lcap) {
                case 1:
                        circlefill((int)sx,(int)sy,(int)cline);
                        circlefill((int)tx,(int)ty,(int)cline);
                        break;
                case 2:
                        xx=cline*costab[theta];
                        yy=cline*sintab[theta];
                        xz=cline*costab[180-theta];
                        yz=cline*sintab[180-theta];
                        if (sx>tx || (sx==tx && sy>ty)) {
                        xz = -xz;
                        yz = -yz; }
                        bbcgra(sx-xx, sy+yy,4);
                        bbcgra(sx+xx, sy-yy,4);
                        bbcgra(sx-xz-xx, sy-yz+yy,85);
                        bbcgra(sx-xz+xx, sy-yz-yy,85);
                        bbcgra(tx-xx, ty+yy,4);
                        bbcgra(tx+xx, ty-yy,4);
                        bbcgra(tx+xz-xx, ty+yz+yy,85);
                        bbcgra(tx+xz+xx, ty+yz-yy,85);
                        break; }
}

void tplot(a,bl,cl)
int a;
double bl,cl;
{
        int theta;
        double tx,ty,zx=currgs->ax,zy=currgs->ay;
        if (a==DRAW) {
                if (bl==zx) {
                        bbcgra(zx-cline, zy, 4);
                        bbcgra(zx+cline, zy, 4);
                        bbcgra(zx-cline, cl, 85);
                        bbcgra(zx+cline, cl, 85);
                        theta=0; }
                else {
                        theta=(int)(2*(90-deg(atan((cl-zy)/(bl-zx)))));
                        tx=cline*costab[theta];
                        ty=cline*sintab[theta];
                        bbcgra(zx+tx,zy-ty,4);
                        bbcgra(zx-tx,zy+ty,4);
                        bbcgra(bl+tx,cl-ty,85);
                        bbcgra(bl-tx,cl+ty,85); }
                if (pjoin) linejoin();
                pjoin=1; 
                linecap(theta,zx,zy,bl,cl); }
        else if (a==MOVE) pjoin=0;
        currgs->ax=bl;
        currgs->ay=cl;
}

void addvec(xx1, yy1, xx2, yy2)
double xx1;
int yy1;
double xx2;
int yy2;
{
        int ty, uu=1;
        double tx;
        struct etr *new;
        etx=xx2;
        ety=yy2;
        if (yy1 != yy2) {
                if (yy2 < yy1) {
                        tx=xx2;
                        ty=yy2;
                        xx2=xx1; yy2=yy1;
                        xx1=tx; yy1=ty;
                        uu = -1; }
                if (yy1<ymin) ymin=yy1;
                if (yy2>ymax) ymax=yy2;
                if (yy2>=0 && yy1<=255) {
                        if (yy1<0) {
                                xx1-=yy1*(xx2-xx1)/(yy2-yy1);
                                yy1=0; }
                        if (yy1 != yy2) {
                               new=&etbase[etc];
                               etc++;
                               new->next = et[yy1];
                               et[yy1]=new;
                               new->y2=yy2;
                               new->x1=xx1;
                               new->dx=(xx2-xx1)/(yy2-yy1);
                               new->up=uu; }
                        }
        }
}

void dcur()
{
        double x1,y1,x2,y2,x3,y3,tt2,tt3,nx,ny,tx,ty;
        double p1,p2,p3,p4,p5,p6,zx=currgs->ax,zy=currgs->ay;
        int c=1;
        int theta;
        x3 = baseg->xc;
        y3 = baseg->yc;
        baseg=baseg->next;
        x2 = 3*baseg->xc;
        y2 = 3*baseg->yc;
        baseg=baseg->next;
        x1 = 3*baseg->xc;
        y1 = 3*baseg->yc;
        p1 = -x+x1-x2+x3;
        p2 = 3*x-2*x1+x2;
        p3 = -3*x+x1;
        p4 = -y+y1-y2+y3;
        p5 = 3*y-2*y1+y2;
        p6 = -3*y+y1;
        tt2=tim*tim;
        tt3=tt2*tim;
        nx = tt3*p1+tt2*p2+tim*p3+x;
        ny = tt3*p4+tt2*p5+tim*p6+y;
        if (nx==zx) {
                plot(4,(int)(zx-cline), (int)zy);
                plot(4,(int)(zx+cline), (int)zy); }
        else {
                theta=(90-(int)(dgc*atan((ny-zy)/(nx-zx)))) << 1;
                tx=cline*costab[theta];
                ty=cline*sintab[theta];
                plot(4,(int)(zx+tx), (int)(zy-ty));
                plot(4,(int)(zx-tx), (int)(zy+ty)); }
        for (; c < 32; c++) {
                nx = t3[c]*p1+t2[c]*p2+t1[c]*p3+x;
                ny = t3[c]*p4+t2[c]*p5+t1[c]*p6+y;
                if (fflg) addvec(etx, ety, nx/2, (int)ny >> 2);
                else if (nx==zx) {
                            plot(85,(int)(zx-cline), (int)ny);
                            plot(85,(int)(zx+cline), (int)ny); }
                     else {
                            theta=(90-(int)(dgc*atan((ny-zy)/(nx-zx)))) << 1;
                            tx=cline*costab[theta];
                            ty=cline*sintab[theta];
                            plot(85,(int)(nx+tx), (int)(ny-ty));
                            plot(85,(int)(nx-tx), (int)(ny+ty)); }
                zx=nx;
                zy=ny; }
        currgs->ax=zx;
        currgs->ay=zy;
        x=x3;
        y=y3;
}

void doelement()
{
        switch (baseg->code) {
        case MOVE: if (fflg && !clsd) {
                        clsd=1;
                        addvec(etx,ety,currgs->sx/2,(int)currgs->sy >> 2); }
                   x=baseg->xc;
                   y=baseg->yc;
                   currgs->sx=x;
                   currgs->sy=y;
                   drti=1;
                   if (fflg) {
                        etx=x/2;
                        ety=(int)y >> 2; }
                   else tplot(MOVE, x, y);
                   break;
        case DRAW: x=baseg->xc;
                   y=baseg->yc;
                   if (fflg) addvec(etx, ety, x/2, (int)y >> 2);
                   else tplot(DRAW, x, y);
                   clsd=0;
                   break;
        case CURVE: dcur();
                    clsd=0;
                    break;
        case CLOSE: if (drti) {
                        clsd=1;
                        x=currgs->sx;
                        y=currgs->sy;
                        if (fflg) addvec(etx, ety, x/2, (int)y >>2);
                        else tplot(DRAW, x, y);
                        break; }
                    break;
        }
}

void pathelem(comm, ex, wy)
int comm;
double ex, wy;
{
        if (baseg == NULL) {
                baseg = (struct gpath *)calloc(1, sizeof(struct gpath));
                nextg = baseg;
                cpath = 1;
        }
        else {
                nextg->next = (struct gpath *)calloc(1, sizeof(struct gpath));
                nextg = nextg->next; }
        if (comm == MOVE) {
                mzx=ex;
                mzy=wy;
                mcx=cx;
                mcy=cy; }
        if (comm == CLOSE) {
                cx=mcx;
                cy=mcy; }
        currgs->ax=ex;
        currgs->ay=wy;
        nextg->code = comm;
        nextg->xc = ex;
        nextg->yc = wy;
}

struct arrele *concat(mat1, mat2, mat3)
struct arrele *mat1;
struct arrele *mat2;
struct arrele *mat3;
{
        double a1,b1,c1,d1,tx1,ty1,a2,b2,c2,d2,tx2,ty2;
        a1 = mat1[0].u.number;
        b1 = mat1[1].u.number;
        c1 = mat1[2].u.number;
        d1 = mat1[3].u.number;
        tx1 = mat1[4].u.number;
        ty1 = mat1[5].u.number;
        a2 = mat2[0].u.number;
        b2 = mat2[1].u.number;
        c2 = mat2[2].u.number;
        d2 = mat2[3].u.number;
        tx2 = mat2[4].u.number;
        ty2 = mat2[5].u.number;
        mat3[5].u.number=tx1*b2+ty1*d2+ty2;
        mat3[4].u.number=tx1*a2+ty1*c2+tx2;
        mat3[3].u.number=c1*b2+d1*d2;
        mat3[2].u.number=c1*a2+d1*c2;
        mat3[1].u.number=a1*b2+b1*d2;
        mat3[0].u.number=a1*a2+b1*c2;
        return(mat3);
}

void graph(comm, x1, y1)
int comm;
double x1,y1;
{
        if (currgs->nopath)
                if (comm & 3) seterror("nocurrentpoint");
                else currgs->nopath=0;
        cx=cx+x1;
        cy=cy+y1;
        pathelem(comm, tma*cx+tmc*cy+tmx, tmb*cx+tmd*cy+tmy);
}

void addtodict(value, which, token)
struct usere *value;
struct userdict *which;
struct idlist *token;
{
        struct usere *t=token->invoc;
        while (t != NULL && t->id != which) t=t->next;
        if (t != NULL) {
                t->sort = value->sort;
                t->u = value->u; }
        else {
                if (which->size == which->maxsize) seterror("dictfull");
                t = (struct usere *)calloc(1, sizeof(struct usere));
                *t = *value;
                value = token->invoc;
                token->invoc = t;
                t->next = value;
                t->id = which;
                which->size++;
                }
}

void dstrip(value)
struct usere *value;
{
        switch (value->sort) {
        case NUMBER: pushnum(value->u.number); break;
        case NAME: pushname(value->u.name); break;
        case STRING: pushrs(value->u.restr); break;
        case MARK: pushmark(); break;
        case ARRAY: pusharray(value->u.array,ARRAY); break;
        case DICTIONARY: pushdict(value->u.dict); break;
        case PROCEDURE: dooproc(value->u.array); break;
        case BOOLEAN: pushbool(value->u.bool); break;
        case OPERATOR: pushoper(value->u.oper.called, value->u.oper.k); break;
        case FILETYPE: pushfile(value->u.filo); break;
        default: pushstr(value->u.string, value->sort); break;
        }
}

void pdict(value)
struct usere *value;
{
        if (offset == MAXSTACK) seterror("stackoverflow");
        offset++;
        current = &mstack[offset];
        current->sort = value->sort;
        current->u = value->u;
}

void ptsn(val)
struct strdef *val;
{
        int c=0;
        printf("(");
        while (c < val->len) {
                if (val->string[c] > 31) printf("%c", val->string[c]);
                else printf(".");
                c++; }
        printf(")");
}

void pse2(this)
struct arrele *this;
{
        int c=0,l;
        if (this != NULL) l=this->length;
        else l=0;
        while (c<l) {
                switch (this[c].sort) {
                case NUMBER: printf("%s",ftos(this[c].u.number));
                             break;
                case BOOLEAN: if (this[c].u.bool) printf("true");
                              else printf("false"); break;
                case DICTIONARY: printf("DICT"); break;
                case MARK: printf("["); break;
                case ARRAY: printf("ARRAY"); break;
                case STRING: ptsn(this[c].u.restr); break;
                case NAME: printf("/%s", this[c].u.name->name); break;
                case OPERATOR: printf("%s",this[c].u.name->name); break;
                case PROCEDURE: printf("{");
                                pse2(this[c].u.array);
                                printf("}"); break;
                case FILETYPE: printf("FILE"); break;
                default: printf("%s",this[c].u.string); break; }
        printf(" ");
        c++;
        }
}

void pse(this)
struct stelem *this;
{
        switch (this->sort) {
        case NUMBER: printf("%s",ftos(this->u.number));
                     break;
        case BOOLEAN: if (this->u.bool) printf("true");
                      else printf("false"); break;
        case DICTIONARY: printf("DICT %d", this->u.dict->size); break;
        case MARK: printf("["); break;
        case ARRAY: printf("ARRAY"); break;
        case STRING: ptsn(this->u.restr); break;
        case NAME: printf("/%s", this->u.name->name); break;
        case OPERATOR: printf("%s", this->u.name->name); break;
        case PROCEDURE: printf("{");
                        pse2(this->u.array);
                        printf("}"); break;
        case FILETYPE: printf("FILE"); break;
        default: printf("%s",this->u.string); break;
        }
}

void pstack()
{
        int of=offset;
        while (of >= 0) {
                pse(&mstack[of]);
                printf("\n");
                --of; }
}

double pullnum()
{
        double temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != NUMBER) seterror("typecheck");
        temp = current->u.number;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

double pullpnum()
{
        double temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort == NUMBER && current->u.number >= 0) {
                temp = current->u.number;
                --offset;
                if (offset < 0) current=NULL;
                else current = &mstack[offset]; }
        else seterror("typecheck");
        return(temp);
}

struct phile *pullfile()
{
        struct phile *temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != FILETYPE) seterror("typecheck");
        temp = current->u.filo;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

struct arrele *pullarray(which)
int which;
{
        struct arrele *temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != which) seterror("typecheck");
        temp = current->u.array;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}

int lenarray(a)
struct arrele *a;
{
        if (a != NULL) return(a->length);
        else return(0);
}

char *pullstr(which)
int which;
{
        char *temp;
        if (offset < 0) seterror("stackunderflow");
        if (current->sort != which) seterror("typecheck");
        temp = current->u.string;
        --offset;
        if (offset < 0) current=NULL;
        else current = &mstack[offset];
        return(temp);
}


int pullgraph()
{
        if (!gpo) return(0);
        --gpo;
        currgs=&gstack[gpo];
        baseg = currgs->path;
        if (baseg == NULL) cpath = 0;
        else cpath = 1;
        nextg = currgs->epath;
        cx = currgs->cx;
        cy = currgs->cy;
        ctm[0].u.number = currgs->ctma;
        ctm[1].u.number = currgs->ctmb;
        ctm[2].u.number = currgs->ctmc;
        ctm[3].u.number = currgs->ctmd;
        ctm[4].u.number = currgs->ctmx;
        ctm[5].u.number = currgs->ctmy;
        readctm(ctm);
        tplot(4, currgs->ax, currgs->ay);
        setcol();
        cline=currgs->cline;
        return(1);
}

struct etr *sort(aet)
struct etr *aet;
{
        struct etr *temp=NULL,*old=aet,*atemp,*otemp;
        if (aet != NULL) temp=aet->next;
        while(temp != NULL)
                if (temp->x1 < old->x1) {
                        otemp=NULL;
                        atemp=aet;
                        while (atemp->x1 < temp->x1) {
                                otemp=atemp;
                                atemp=atemp->next; }
                        old->next=temp->next;
                        temp->next=atemp;
                        if (otemp==NULL) aet=temp;
                        else otemp->next=temp;
                        temp=old->next; }
                else {
                        old=temp;
                        temp=temp->next; }
        return(aet);
}

void startfill(nzw)
int nzw;
{
        int c,res;
        struct etr *aet=NULL;
        struct etr *temp,*temp2,*old;
        struct gpath *tbg;
        fflg=1;
        pjoin=0;
        clsd=1;
        ymin = 256;
        ymax = -1;
        tbg = baseg;
        pths=0;
        etc=0;
        while (tbg != NULL) {
                if (tbg->code == CURVE) pths+=32;
                else pths++;
                tbg=tbg->next; }
        if (pths) etbase=(struct etr *)calloc(pths, sizeof(struct etr));
        while (baseg != NULL) {
                doelement();
                baseg = baseg->next; }
        if (drti && !clsd) addvec(etx, ety, currgs->sx/2, (int)currgs->sy >>2);
        fflg=0;
        currgs->nopath = 1;
        if (ymax>255) ymax=255;
        if (ymin<0) ymin=0;
        for (c=ymin; c<=ymax; c++) {
                temp=aet;
                old=NULL;
                while (temp != NULL)
                        if (temp->y2 == c) {
                                if (temp==aet) aet=temp->next;
                                else old->next=temp->next;
                                temp=temp->next; }
                        else {
                                old=temp;
                                temp->x1+=temp->dx;
                                temp=temp->next; }
                if (aet != NULL && old != NULL) old->next = et[c];
                else aet=et[c];
                aet=sort(aet);
                temp=aet;
                if (nzw) {
                        if (temp != NULL) {
                                res=0;
                                plot(4, (int)(temp->x1) << 1,c << 2);
                                temp2=temp->next;
                                while(temp2 != NULL) {
                                        res+=temp2->up;
                                        temp2=temp2->next; }
                                while (temp->next != NULL) {
                                        temp=temp->next;
                                        plot(5-(res==0), (int)(temp->x1) << 1,
                                                c << 2);
                                        res-=temp->up; }
                                }
                        }
                else
                        while (temp != NULL) {
                                plot(4, (int)(temp->x1) << 1, c << 2);
                                temp=temp->next;
                                plot(5, (int)(temp->x1) << 1, c << 2);
                                temp=temp->next; }
                et[c]=NULL;
                }
        if (pths) free(etbase);
}

char *left(string, amount)
char *string;
int amount;
{
        char *temp;
        temp = save(string);
        temp[amount] = 0;
        return(temp);
}

char *right(string, amount)
char *string;
int amount;
{
        char *temp;
        temp = save(string);
        temp = (char *)(temp+strlen(string)-amount);
        return(temp);
}

int insys(match)
char *match;
{
        int n;
        int usf = 1;
        int stp = 10;
        unsigned half = 56;
        int count = 112;
        do {
                n=strcmp(match,systemdict[count]);
                if (n==0) usf=0;
                else {
                        if (n<0) count-=half;
                        else count+=half;
                        half=half >> 1;
                        if (half == 0) half++;
                        stp--; }
        }
        while (usf && stp);
        if (stp) return(count);
        else return(-1);
}

struct idlist *findop(match)
char *match;
{
        struct idlist *find=idbase;
        while (find != NULL && strcmp(match, find->name) != 0) find=find->next;
        if (find == NULL) {
                find = (struct idlist *)calloc(1, sizeof(struct idlist));
                find->name = save(match);
                find->invoc = NULL;
                find->next = idbase;
                idbase = find;
                }
        return(find);
}

void dinp(match)
char *match;
{
        struct arrele *new = NULL;
        int l=0,toff,t;
        if (strlen(match))
                switch (match[0]) {
                case '{': pushpmark(); break;
                case '}': toff=offset;
                          while (mstack[toff].sort != PMARK) --toff;
                          l=offset-toff;
                          new=createarray(l);
                          for (; l>0; --l) stacktoarray(&new[l-1]);
                          --offset;
                          if (offset < 0) current=NULL;
                          else current = &mstack[offset];
                          pusharray(new, PROCEDURE);
                          break;
                case '/': match++;
                          t=insys(match);
                          if (t >= 0) used[t]=0;
                          pushname(findop(match));
                          break;
                case '[': pushmark();
                          break;
                case ']': if (cb) pushoper(findop("]"), RB);
                          else docom(findop("]"), RB);
                          break;
                case '(': match++;
                          match[strlen(match)-1] = 0;
                          pushms(save(match), strlen(match));
                          break;
                default: if (match[0] >= 'A' && match[0] <= 'z') {
                                if (cb) pushoper(findop(match),
                                                 insys(match));
                                else docom(findop(match), insys(match));
                         }
                         else pushnum(atof(match));
                         break; }
        acc[0]=0;
}

void single(line)
char *line;
{
int fr;
char *hy;
char temp[2];
temp[1] = 0;
while (line[0] != 0) {
        temp[0] = line[0];
        line++;
        if (rb>0)
                if (esc) {
                        switch (temp[0]) {
                        case 'n': temp[0] = '\n'; break;
                        case 'r': temp[0] = '\r'; break;
                        case 't': temp[0] = '\t'; break;
                        case 'b': temp[0] = '\b'; break;
                        case 'f': temp[0] = '\f'; break;
                        case '\n': temp[0] = 0; break;
                        }
                        strcat(&acc,temp);
                        esc=0; }
                else {
                        switch (temp[0]) {
                        case ')': if (rb == 0) seterror("syntaxerror");
                                  else {
                                        --rb;
                                        if (rb == 0) {
                                                strcat(&acc,")");
                                                dinp(acc); }
                                        else strcat(&acc,")");
                                  }
                                  break;
                        case '(': rb++;
                                  strcat(&acc,"(");
                                  break;
                        case '\\': esc = 1;
                                   break;
                        default: strcat(&acc, temp);
                                 break; }
                }
        else if (ab)
                switch (temp[0]) {
                case ' ': break;
                case '\n': break;
                case '>' : if (on) {
                                on=0;
                                acc[abc] = 16*to;
                                abc++; }
                           hy=(char *)calloc(abc, sizeof(char));
                           fr=0;
                           while (fr < abc) {
                                hy[fr] = acc[fr];
                                fr++; }
                           pushms(hy, abc);
                           ab=0;
                           break;
                default: if (temp[0]>='a' && temp[0]<='f')
                                temp[0]=temp[0]-32;
                         if (temp[0]>='0' && temp[0]<='9') fr=temp[0]-48;
                         else if (temp[0]>='A' && temp[0]<='F')
                                fr=temp[0]-55;
                              else seterror("syntaxerror");
                         if (on) {
                                to=16*to+fr;
                                acc[abc] = to;
                                abc++; }
                         else to=fr;
                         on = 1-on;
                         break; }
             else
                switch (temp[0]) {
                case '{': dinp(acc);
                          dinp("{");
                          cb++;
                          break;
                case '}': if (cb == 0) seterror("syntaxerror");
                          else {
                                dinp(acc);
                                --cb;
                                dinp("}");
                                }
                          break;
                case '(': dinp(acc);
                          rb=1;
                          strcat(&acc,"(");
                          break;
                case ')': seterror("syntaxerror");
                          break;
                case '<': dinp(acc);
                          ab=1;
                          abc=0;
                          break;
                case '\n': dinp(acc);
                           break;
                case ' ': dinp(acc);
                          break;
                case '[': dinp(acc);
                          dinp("[");
                          break;
                case ']': dinp(acc);
                          dinp("]");
                          break;
                case '%': line[0] = 0;
                          dinp(acc);
                          break;
                default: strcat(&acc,temp); break; }
        }
}

void startup()
{
        int c=0;
        double angle=0.0;
        double t;
        if (bw) {
                mode(12);
                colour(15);
                for (; c<16; c++) palette(c,16,c*16,c*16,c*16); }
        else mode(15);
        vdu(28); vdu(45); vdu(31); vdu(79); vdu(0);
        gwindow(0,0,718,1023);
        ctm = createarray(6);
        currgs = &gstack[0];
        dictstack[0] = dcreate(MAXDICT);
        printf("Computing tables");
        for (c=0; c<720; c++) {
                sintab[c] = sin(rad(angle));
                costab[c] = cos(rad(angle));
                angle=angle+0.5;
                if (!(c%50)) printf("."); }
        sintab[0] = 0; costab[0] = 1;
        sintab[180] = 1; costab[180] = 0;
        sintab[360] = 0; costab[360] = -1;
        sintab[540] = -1; costab[540] = 0;
        c=0;
        for (t=tim; t<=1; t=t+tim) {
                t1[c]=t;
                t2[c]=t*t;
                t3[c]=t*t*t;
                c++; }
        for (c=0; c<230; c++) used[c]=1;
        cls();
}
