#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef __TURBOC__
#include <alloc.h>
#else
#include <malloc.h>
#endif
#include "fractint.h"
#include "prototyp.h"
#include "lsys.h"

struct lsys_cmd {
    void (*f)(struct lsys_turtlestatei *);
    long n;
    char ch;
};

static int _fastcall readLSystemFile(char *);
static void _fastcall free_rules_mem(void);
static int _fastcall save_rule(char *,char far **);
static void free_lcmds(void);
static struct lsys_cmd far * _fastcall findsize(struct lsys_cmd far *,struct lsys_turtlestatei *, struct lsys_cmd far **,int);
static struct lsys_cmd far * drawLSysI(struct lsys_cmd far *command,struct lsys_turtlestatei *ts, struct lsys_cmd far **rules,int depth);
static int lsysi_findscale(struct lsys_cmd far *command, struct lsys_turtlestatei *ts, struct lsys_cmd far **rules, int depth);
static struct lsys_cmd far *LSysISizeTransform(char far *s, struct lsys_turtlestatei *ts);
static struct lsys_cmd far *LSysIDrawTransform(char far *s, struct lsys_turtlestatei *ts);
static void _fastcall lsysi_dosincos(void);

static void lsysi_doslash(struct lsys_turtlestatei *cmd);
static void lsysi_dobslash(struct lsys_turtlestatei *cmd);
static void lsysi_doat(struct lsys_turtlestatei *cmd);
static void lsysi_dopipe(struct lsys_turtlestatei *cmd);
static void lsysi_dosizedm(struct lsys_turtlestatei *cmd);
static void lsysi_dosizegf(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawd(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawm(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawg(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawf(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawc(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawgt(struct lsys_turtlestatei *cmd);
static void lsysi_dodrawlt(struct lsys_turtlestatei *cmd);

/* Some notes to Adrian from PB, made when I integrated with v15:
     printfs changed to work with new user interface
     bug at end of readLSystemFile, the line which said rulind=0 fixed
       to say *rulind=0
     the calloc was not worthwhile, it was just for a 54 byte area, cheaper
       to keep it as a static;	but there was a static 201 char buffer I
       changed to not be static
     use of strdup was a nono, caused problems running out of space cause
       the memory allocated each time was never freed; I've changed to
       use far memory and to free when done
   */

#define sins ((long *)(boxy))
#define coss (((long *)(boxy)+50)) /* 50 after the start of sins */
static char far *ruleptrs[MAXRULES];
static struct lsys_cmd far *rules2[MAXRULES];
char maxangle;
static char loaded=0;


int _fastcall ispow2(int n)
{
  return (n == (n & -n));
}

LDBL _fastcall getnumber(char far **str)
{
   char numstr[30];
   LDBL ret;
   int i,root,inverse;

   root=0;
   inverse=0;
   strcpy(numstr,"");
   (*str)++;
   switch (**str)
   {
   case 'q':
      root=1;
      (*str)++;
      break;
   case 'i':
      inverse=1;
      (*str)++;
      break;
   }
   switch (**str)
   {
   case 'q':
      root=1;
      (*str)++;
      break;
   case 'i':
      inverse=1;
      (*str)++;
      break;
   }
   i=0;
   while ((**str<='9' && **str>='0') || **str=='.')
   {
      numstr[i++]= **str;
      (*str)++;
   }
   (*str)--;
   numstr[i]=0;
   ret=atof(numstr);
   if (ret <= 0.0) /* this is a sanity check, JCO 8/31/94 */
      return 0;
   if (root)
     ret=sqrtl(ret);
   if (inverse)
     ret = 1.0/ret;
   return ret;
}

static int _fastcall readLSystemFile(char *str)
{
   int c;
   char far **rulind;
   int err=0;
   int linenum,check=0;
   char inline[161],fixed[161],*word;
   FILE *infile;
   char msgbuf[481]; /* enough for 6 full lines */

   if (find_file_item(LFileName,str,&infile) < 0)
      return -1;
   while ((c = fgetc(infile)) != '{')
      if (c == EOF) return -1;
   maxangle=0;
   for(linenum=0;linenum<MAXRULES;++linenum) ruleptrs[linenum]=NULL;
   rulind= &ruleptrs[1];
   msgbuf[0]=(char)(linenum=0);

   while(file_gets(inline,160,infile) > -1)  /* Max line length 160 chars */
   {
      linenum++;
      if ((word = strchr(inline,';')) != NULL) /* strip comment */
	 *word = 0;
      strlwr(inline);

      if (strspn(inline," \t\n") < strlen(inline)) /* not a blank line */
      {
	 word=strtok(inline," =\t\n");
	 if (!strcmp(word,"axiom"))
	 {
	    if (save_rule(strtok(NULL," \t\n"),&ruleptrs[0])) {
	        strcat(msgbuf,"Error:  out of memory\n");
	        ++err;
		break;
            }
	    check=1;
	 }
	 else if (!strcmp(word,"angle"))
	 {
	    maxangle=(char)atoi(strtok(NULL," \t\n"));
	    check=1;
	 }
	 else if (!strcmp(word,"}"))
	    break;
	 else if (strcspn(word,"+-/\\@|!c<>][") == 0 && strlen(word) == 1)
	    {
	       sprintf(&msgbuf[strlen(msgbuf)],
	       "Syntax error line %d: Redefined reserved symbol %s\n",linenum,word);
	       ++err;
	       break;
	    }
	 else if (strlen(word)==1)
	 {
	    strcat(strcpy(fixed,word),strtok(NULL," \t\n"));
	    if (save_rule(fixed,rulind++)) {
		strcat(msgbuf, "Error:  out of memory\n");
		++err;
                break;
            }
	    check=1;
	 }
	 else
	    if (err<6)
	    {
	       sprintf(&msgbuf[strlen(msgbuf)],
		       "Syntax error line %d: %s\n",linenum,word);
	       ++err;
	    }
	 if (check)
	 {
	    check=0;
	    if((word=strtok(NULL," \t\n"))!=NULL)
	       if (err<6)
	       {
		  sprintf(&msgbuf[strlen(msgbuf)],
			 "Extra text after command line %d: %s\n",linenum,word);
		  ++err;
	       }
	 }
      }
   }
   fclose(infile);
   if (!ruleptrs[0] && err<6)
   {
      strcat(msgbuf,"Error:  no axiom\n");
      ++err;
   }
   if ((maxangle<3||maxangle>50) && err<6)
   {
      strcat(msgbuf,"Error:  illegal or missing angle\n");
      ++err;
   }
   if (err)
   {
      msgbuf[strlen(msgbuf)-1]=0; /* strip trailing \n */
      stopmsg(0,msgbuf);
      return -1;
   }
   *rulind=NULL;
   return 0;
}

int Lsystem(void)
{
   int order;
   char far **rulesc;
   struct lsys_cmd far **sc;
   int stackoflow = 0;

   if ( (!loaded) && LLoad())
     return -1;

   overflow = 0;		/* reset integer math overflow flag */

   order=(int)param[0];
   if (order<=0)
     order=0;
   if (usr_floatflag)
	overflow = 1;
   else {
	struct lsys_turtlestatei ts;

	ts.stackoflow = 0;
	ts.maxangle = maxangle;
        ts.dmaxangle = (char)(maxangle - 1);

	sc = rules2;
	for (rulesc = ruleptrs; *rulesc; rulesc++)
	    *sc++ = LSysISizeTransform(*rulesc, &ts);
	*sc = NULL;

	lsysi_dosincos();
	if (lsysi_findscale(rules2[0], &ts, &rules2[1], order)) {
	    ts.realangle = ts.angle = ts.reverse = 0;

	    free_lcmds();
	    sc = rules2;
	    for (rulesc = ruleptrs; *rulesc; rulesc++)
		*sc++ = LSysIDrawTransform(*rulesc, &ts);
	    *sc = NULL;

	    /* !! HOW ABOUT A BETTER WAY OF PICKING THE DEFAULT DRAWING COLOR */
	    if ((ts.curcolor=15) > colors)
	    	ts.curcolor=(char)(colors-1);
	    drawLSysI(rules2[0], &ts, &rules2[1], order);
	}
	stackoflow = ts.stackoflow;
   }

   if (stackoflow) {
      static char far msg[]={"insufficient memory, try a lower order"};
      stopmsg(0,msg);
   }
   else if (overflow) {
	struct lsys_turtlestatef ts;

	overflow = 0;

	ts.stackoflow = 0;
	ts.maxangle = maxangle;
        ts.dmaxangle = (char)(maxangle - 1);

	sc = rules2;
	for (rulesc = ruleptrs; *rulesc; rulesc++)
	    *sc++ = LSysFSizeTransform(*rulesc, &ts);
	*sc = NULL;

	lsysf_dosincos();
	if (lsysf_findscale(rules2[0], &ts, &rules2[1], order)) {
	    ts.realangle = ts.angle = ts.reverse = 0;

	    free_lcmds();
	    sc = rules2;
	    for (rulesc = ruleptrs; *rulesc; rulesc++)
		*sc++ = LSysFDrawTransform(*rulesc, &ts);
	    *sc = NULL;

	    /* !! HOW ABOUT A BETTER WAY OF PICKING THE DEFAULT DRAWING COLOR */
	    if ((ts.curcolor=15) > colors)
	    	ts.curcolor=(char)(colors-1);
	    lsys_prepfpu(&ts);
	    drawLSysF(rules2[0], &ts, &rules2[1], order);
	    lsys_donefpu(&ts);
	}
        overflow = 0;
   }
   free_rules_mem();
   free_lcmds();
   loaded=0;
   return 0;
}

int LLoad(void)
{
   if (readLSystemFile(LName)) { /* error occurred */
      free_rules_mem();
      loaded=0;
      return -1;
   }
   loaded=1;
   return 0;
}

static void _fastcall free_rules_mem(void)
{
   int i;
   for(i=0;i<MAXRULES;++i)
      if(ruleptrs[i]) farmemfree(ruleptrs[i]);
}

static int _fastcall save_rule(char *rule,char far **saveptr)
{
   int i;
   char far *tmpfar;
   i=strlen(rule)+1;
   if((tmpfar=farmemalloc((long)i))==NULL) {
       return -1;
   }
   *saveptr=tmpfar;
   while(--i>=0) *(tmpfar++)= *(rule++);
   return 0;
}

static void free_lcmds(void)
{
  struct lsys_cmd far **sc = rules2;

  while (*sc)
    farmemfree(*sc++);
}



#ifdef XFRACT
#define lsysi_doslash_386 lsysi_doslash
#define lsysi_dobslash_386 lsysi_dobslash
#define lsys_doat lsysi_doat
#define lsys_dosizegf lsysi_dosizegf
#define lsys_dodrawg lsysi_dodrawg
void lsys_prepfpu(struct lsys_turtlestatef *x) { }
void lsys_donefpu(struct lsys_turtlestatef *x) { }
#endif

/* integer specific routines */

#ifdef XFRACT
static void lsysi_doplus(struct lsys_turtlestatei *cmd)
{
    if (cmd->reverse) {
	if (++cmd->angle == cmd->maxangle)
	    cmd->angle = 0;
    }
    else {
	if (cmd->angle)
	    cmd->angle--;
	else
	    cmd->angle = cmd->dmaxangle;
    }
}
#else
extern void lsysi_doplus(struct lsys_turtlestatei *cmd);
#endif

#ifdef XFRACT
/* This is the same as lsys_doplus, except maxangle is a power of 2. */
static void lsysi_doplus_pow2(struct lsys_turtlestatei *cmd)
{
    if (cmd->reverse) {
	cmd->angle++;
	cmd->angle &= cmd->dmaxangle;
    }
    else {
	cmd->angle--;
	cmd->angle &= cmd->dmaxangle;
    }
}
#else
extern void lsysi_doplus_pow2(struct lsys_turtlestatei *cmd);
#endif

#ifdef XFRACT
static void lsysi_dominus(struct lsys_turtlestatei *cmd)
{
    if (cmd->reverse) {
	if (cmd->angle)
	    cmd->angle--;
	else
	    cmd->angle = cmd->dmaxangle;
    }
    else {
	if (++cmd->angle == cmd->maxangle)
	    cmd->angle = 0;
    }
}
#else
extern void lsysi_dominus(struct lsys_turtlestatei *cmd);
#endif

#ifdef XFRACT
static void lsysi_dominus_pow2(struct lsys_turtlestatei *cmd)
{
    if (cmd->reverse) {
	cmd->angle--;
	cmd->angle &= cmd->dmaxangle;
    }
    else {
	cmd->angle++;
	cmd->angle &= cmd->dmaxangle;
    }
}
#else
extern void lsysi_dominus_pow2(struct lsys_turtlestatei *cmd);
#endif

static void lsysi_doslash(struct lsys_turtlestatei *cmd)
{
    if (cmd->reverse)
	cmd->realangle -= cmd->num;
    else
	cmd->realangle += cmd->num;
}

#ifndef XFRACT
extern void lsysi_doslash_386(struct lsys_turtlestatei *cmd);
#endif

static void lsysi_dobslash(struct lsys_turtlestatei *cmd)
{
    if (cmd->reverse)
	cmd->realangle += cmd->num;
    else
	cmd->realangle -= cmd->num;
}

#ifndef XFRACT
extern void lsysi_dobslash_386(struct lsys_turtlestatei *cmd);
#endif

static void lsysi_doat(struct lsys_turtlestatei *cmd)
{
    cmd->size = multiply(cmd->size, cmd->num, 19);
}

static void lsysi_dopipe(struct lsys_turtlestatei *cmd)
{
    cmd->angle = (char)(cmd->angle + (char)(cmd->maxangle / 2));
    cmd->angle %= cmd->maxangle;
}

#ifdef XFRACT
static void lsysi_dopipe_pow2(struct lsys_turtlestatei *cmd)
{
    cmd->angle += cmd->maxangle >> 1;
    cmd->angle &= cmd->dmaxangle;
}
#else
extern void lsysi_dopipe_pow2(struct lsys_turtlestatei *cmd);
#endif

#ifdef XFRACT
static void lsysi_dobang(struct lsys_turtlestatei *cmd)
{
    cmd->reverse = ! cmd->reverse;
}
#else
extern void lsysi_dobang(struct lsys_turtlestatei *cmd);
#endif

static void lsysi_dosizedm(struct lsys_turtlestatei *cmd)
{
    double angle = (double) cmd->realangle * ANGLE2DOUBLE;
    double s, c;
    long fixedsin, fixedcos;

    FPUsincos(&angle, &s, &c);
    fixedsin = (long) (s * FIXEDLT1);
    fixedcos = (long) (c * FIXEDLT1);

    cmd->xpos = cmd->xpos + (multiply(multiply(cmd->size, cmd->aspect, 19), fixedcos, 29));
    cmd->ypos = cmd->ypos + (multiply(cmd->size, fixedsin, 29));

/* xpos+=size*aspect*cos(realangle*PI/180); */
/* ypos+=size*sin(realangle*PI/180); */
    if (cmd->xpos>cmd->xmax) cmd->xmax=cmd->xpos;
    if (cmd->ypos>cmd->ymax) cmd->ymax=cmd->ypos;
    if (cmd->xpos<cmd->xmin) cmd->xmin=cmd->xpos;
    if (cmd->ypos<cmd->ymin) cmd->ymin=cmd->ypos;
}

static void lsysi_dosizegf(struct lsys_turtlestatei *cmd)
{
  cmd->xpos = cmd->xpos + (multiply(cmd->size, coss[cmd->angle], 29));
  cmd->ypos = cmd->ypos + (multiply(cmd->size, sins[cmd->angle], 29));
/* xpos+=size*coss[angle]; */
/* ypos+=size*sins[angle]; */
  if (cmd->xpos>cmd->xmax) cmd->xmax=cmd->xpos;
  if (cmd->ypos>cmd->ymax) cmd->ymax=cmd->ypos;
  if (cmd->xpos<cmd->xmin) cmd->xmin=cmd->xpos;
  if (cmd->ypos<cmd->ymin) cmd->ymin=cmd->ypos;
}

static void lsysi_dodrawd(struct lsys_turtlestatei *cmd)
{
  double angle = (double) cmd->realangle * ANGLE2DOUBLE;
  double s, c;
  long fixedsin, fixedcos;
  int lastx, lasty;

  FPUsincos(&angle, &s, &c);
  fixedsin = (long) (s * FIXEDLT1);
  fixedcos = (long) (c * FIXEDLT1);

  lastx=(int) (cmd->xpos >> 19);
  lasty=(int) (cmd->ypos >> 19);
  cmd->xpos = cmd->xpos + (multiply(multiply(cmd->size, cmd->aspect, 19), fixedcos, 29));
  cmd->ypos = cmd->ypos + (multiply(cmd->size, fixedsin, 29));
/* xpos+=size*aspect*cos(realangle*PI/180); */
/* ypos+=size*sin(realangle*PI/180); */
  draw_line(lastx,lasty,(int)(cmd->xpos >> 19),(int)(cmd->ypos >> 19),cmd->curcolor);
}

static void lsysi_dodrawm(struct lsys_turtlestatei *cmd)
{
  double angle = (double) cmd->realangle * ANGLE2DOUBLE;
  double s, c;
  long fixedsin, fixedcos;

  FPUsincos(&angle, &s, &c);
  fixedsin = (long) (s * FIXEDLT1);
  fixedcos = (long) (c * FIXEDLT1);

/* xpos+=size*aspect*cos(realangle*PI/180); */
/* ypos+=size*sin(realangle*PI/180); */
  cmd->xpos = cmd->xpos + (multiply(multiply(cmd->size, cmd->aspect, 19), fixedcos, 29));
  cmd->ypos = cmd->ypos + (multiply(cmd->size, fixedsin, 29));
}

static void lsysi_dodrawg(struct lsys_turtlestatei *cmd)
{
  cmd->xpos = cmd->xpos + (multiply(cmd->size, coss[cmd->angle], 29));
  cmd->ypos = cmd->ypos + (multiply(cmd->size, sins[cmd->angle], 29));
/* xpos+=size*coss[angle]; */
/* ypos+=size*sins[angle]; */
}

static void lsysi_dodrawf(struct lsys_turtlestatei *cmd)
{
  int lastx = (int) (cmd->xpos >> 19);
  int lasty = (int) (cmd->ypos >> 19);
  cmd->xpos = cmd->xpos + (multiply(cmd->size, coss[cmd->angle], 29));
  cmd->ypos = cmd->ypos + (multiply(cmd->size, sins[cmd->angle], 29));
/* xpos+=size*coss[angle]; */
/* ypos+=size*sins[angle]; */
  draw_line(lastx,lasty,(int)(cmd->xpos >> 19),(int)(cmd->ypos >> 19),cmd->curcolor);
}

static void lsysi_dodrawc(struct lsys_turtlestatei *cmd)
{
  cmd->curcolor = (char)(((int) cmd->num) % colors);
}

static void lsysi_dodrawgt(struct lsys_turtlestatei *cmd)
{
  cmd->curcolor = (char)(cmd->curcolor - (char)cmd->num);
  if ((cmd->curcolor %= colors) == 0)
    cmd->curcolor = (char)(colors-1);
}

static void lsysi_dodrawlt(struct lsys_turtlestatei *cmd)
{
  cmd->curcolor = (char)(cmd->curcolor + (char)cmd->num);
  if ((cmd->curcolor %= colors) == 0)
    cmd->curcolor = 1;
}

static struct lsys_cmd far * _fastcall
findsize(struct lsys_cmd far *command, struct lsys_turtlestatei *ts, struct lsys_cmd far **rules, int depth)
{
   struct lsys_cmd far **rulind;
   int tran;

if (overflow)     /* integer math routines overflowed */
    return NULL;

#ifndef __TURBOC__
   if (stackavail() < 400) { /* leave some margin for calling subrtns */
      ts->stackoflow = 1;
      return NULL;
      }
#endif

   while (command->ch && command->ch !=']') {
      if (! (ts->counter++)) {
	 /* let user know we're not dead */
	 if (thinking(1,"L-System thinking (higher orders take longer)")) {
	    ts->counter--;
	    return NULL;
	 }
      }
      tran=0;
      if (depth) {
	 for(rulind=rules;*rulind;rulind++)
	    if ((*rulind)->ch==command->ch) {
	       tran=1;
	       if (findsize((*rulind)+1,ts,rules,depth-1) == NULL)
		  return(NULL);
	    }
      }
      if (!depth || !tran) {
	if (command->f) {
	  ts->num = command->n;
	  (*command->f)(ts);
	  }
	else if (command->ch == '[') {
	  char saveang,saverev;
	  long savesize,savex,savey;
	  unsigned long saverang;

	  saveang=ts->angle;
	  saverev=ts->reverse;
	  savesize=ts->size;
	  saverang=ts->realangle;
	  savex=ts->xpos;
	  savey=ts->ypos;
	  if ((command=findsize(command+1,ts,rules,depth)) == NULL)
	     return(NULL);
	  ts->angle=saveang;
	  ts->reverse=saverev;
	  ts->size=savesize;
	  ts->realangle=saverang;
	  ts->xpos=savex;
	  ts->ypos=savey;
	}
      }
      command++;
   }
   return command;
}

static int
lsysi_findscale(struct lsys_cmd far *command, struct lsys_turtlestatei *ts, struct lsys_cmd far **rules, int depth)
{
   float horiz,vert;
   double xmin, xmax, ymin, ymax;
   double locsize;
   double locaspect;
   struct lsys_cmd far *fsret;

   locaspect=screenaspect*xdots/ydots;
   ts->aspect = FIXEDPT(locaspect);
   ts->xpos =
   ts->ypos =
   ts->xmin =
   ts->xmax =
   ts->ymax =
   ts->ymin =
   ts->realangle =
   ts->angle =
   ts->reverse =
   ts->counter = 0;
   ts->size=FIXEDPT(1L);
   fsret = findsize(command,ts,rules,depth);
   thinking(0, NULL); /* erase thinking message if any */
   xmin = (double) ts->xmin / FIXEDMUL;
   xmax = (double) ts->xmax / FIXEDMUL;
   ymin = (double) ts->ymin / FIXEDMUL;
   ymax = (double) ts->ymax / FIXEDMUL;
   if (fsret == NULL)
      return 0;
   if (xmax == xmin)
      horiz = (float)1E37;
   else
      horiz = (float)((xdots-10)/(xmax-xmin));
   if (ymax == ymin)
      vert = (float)1E37;
   else
      vert = (float)((ydots-6) /(ymax-ymin));
   locsize = (vert<horiz) ? vert : horiz;

   if (horiz == 1E37)
      ts->xpos = FIXEDPT(xdots/2);
   else
/*    ts->xpos = FIXEDPT(-xmin*(locsize)+5+((xdots-10)-(locsize)*(xmax-xmin))/2); */
      ts->xpos = FIXEDPT((xdots-locsize*(xmax+xmin))/2);
   if (vert == 1E37)
      ts->ypos = FIXEDPT(ydots/2);
   else
/*    ts->ypos = FIXEDPT(-ymin*(locsize)+3+((ydots-6)-(locsize)*(ymax-ymin))/2); */
      ts->ypos = FIXEDPT((ydots-locsize*(ymax+ymin))/2);
   ts->size = FIXEDPT(locsize);

   return 1;
}

static struct lsys_cmd far * 
drawLSysI(struct lsys_cmd far *command,struct lsys_turtlestatei *ts, struct lsys_cmd far **rules,int depth)
{
   struct lsys_cmd far **rulind;
   int tran;

if (overflow)     /* integer math routines overflowed */
    return NULL;

#ifndef __TURBOC__
   if (stackavail() < 400) { /* leave some margin for calling subrtns */
      ts->stackoflow = 1;
      return NULL;
      }
#endif

   while (command->ch && command->ch !=']') {
      if (!(ts->counter++)) {
	 if (keypressed()) {
	    ts->counter--;
	    return NULL;
	 }
      }
      tran=0;
      if (depth) {
	 for(rulind=rules;*rulind;rulind++)
	    if ((*rulind)->ch == command->ch) {
	       tran=1;
	       if (drawLSysI((*rulind)+1,ts,rules,depth-1) == NULL)
		  return NULL;
	    }
      }
      if (!depth||!tran) {
	if (command->f) {
	  ts->num = command->n;
	  (*command->f)(ts);
	  }
	else if (command->ch == '[') {
	  char saveang,saverev,savecolor;
	  long savesize,savex,savey;
	  unsigned long saverang;

	  saveang=ts->angle;
	  saverev=ts->reverse;
	  savesize=ts->size;
	  saverang=ts->realangle;
	  savex=ts->xpos;
	  savey=ts->ypos;
	  savecolor=ts->curcolor;
	  if ((command=drawLSysI(command+1,ts,rules,depth)) == NULL)
	     return(NULL);
	  ts->angle=saveang;
	  ts->reverse=saverev;
	  ts->size=savesize;
	  ts->realangle=saverang;
	  ts->xpos=savex;
	  ts->ypos=savey;
	  ts->curcolor=savecolor;
	}
      }
      command++;
   }
   return command;
}

static struct lsys_cmd far *
LSysISizeTransform(char far *s, struct lsys_turtlestatei *ts)
{
  struct lsys_cmd far *ret;
  struct lsys_cmd far *doub;
  int maxval = 10;
  int n = 0;
  void (*f)();
  long num;

  void (*plus)() = (ispow2(ts->maxangle)) ? lsysi_doplus_pow2 : lsysi_doplus;
  void (*minus)() = (ispow2(ts->maxangle)) ? lsysi_dominus_pow2 : lsysi_dominus;
  void (*pipe)() = (ispow2(ts->maxangle)) ? lsysi_dopipe_pow2 : lsysi_dopipe;

  void (*slash)() =  (cpu >= 386) ? lsysi_doslash_386 : lsysi_doslash;
  void (*bslash)() = (cpu >= 386) ? lsysi_dobslash_386 : lsysi_dobslash;
  void (*at)() =     (cpu >= 386) ? lsysi_doat_386 : lsysi_doat;
  void (*dogf)() =   (cpu >= 386) ? lsysi_dosizegf_386 : lsysi_dosizegf;

  ret = (struct lsys_cmd far *) farmemalloc((long) maxval * sizeof(struct lsys_cmd));
  if (ret == NULL) {
       ts->stackoflow = 1;
       return NULL;
       }
  while (*s) {
    f = NULL;
    num = 0;
    ret[n].ch = *s;
    switch (*s) {
      case '+': f = plus;            break;
      case '-': f = minus;           break;
      case '/': f = slash;           num = (long) (getnumber(&s) * 11930465L);    break;
      case '\\': f = bslash;         num = (long) (getnumber(&s) * 11930465L);    break;
      case '@': f = at;              num = FIXEDPT(getnumber(&s));    break;
      case '|': f = pipe;            break;
      case '!': f = lsysi_dobang;     break;
      case 'd':
      case 'm': f = lsysi_dosizedm;   break;
      case 'g':
      case 'f': f = dogf;       break;
      case '[': num = 1;        break;
      case ']': num = 2;        break;
      default:
	num = 3;
	break;
    }
#ifdef XFRACT
    ret[n].f = (void (*)())f;
#else
    ret[n].f = (void (*)(struct lsys_turtlestatei *))f;
#endif
    ret[n].n = num;
    if (++n == maxval) {
      doub = (struct lsys_cmd far *) farmemalloc((long) maxval*2*sizeof(struct lsys_cmd));
      if (doub == NULL) {
         farmemfree(ret);
         ts->stackoflow = 1;
         return NULL;
         }
      far_memcpy(doub, ret, maxval*sizeof(struct lsys_cmd));
      farmemfree(ret);
      ret = doub;
      maxval <<= 1;
    }
    s++;
  }
  ret[n].ch = 0;
  ret[n].f = NULL;
  ret[n].n = 0;
  n++;

  doub = (struct lsys_cmd far *) farmemalloc((long) n*sizeof(struct lsys_cmd));
  if (doub == NULL) {
       farmemfree(ret);
       ts->stackoflow = 1;
       return NULL;
       }
  far_memcpy(doub, ret, n*sizeof(struct lsys_cmd));
  farmemfree(ret);
  return doub;
}

static struct lsys_cmd far *
LSysIDrawTransform(char far *s, struct lsys_turtlestatei *ts)
{
  struct lsys_cmd far *ret;
  struct lsys_cmd far *doub;
  int maxval = 10;
  int n = 0;
  void (*f)();
  long num;

  void (*plus)() = (ispow2(ts->maxangle)) ? lsysi_doplus_pow2 : lsysi_doplus;
  void (*minus)() = (ispow2(ts->maxangle)) ? lsysi_dominus_pow2 : lsysi_dominus;
  void (*pipe)() = (ispow2(ts->maxangle)) ? lsysi_dopipe_pow2 : lsysi_dopipe;

  void (*slash)() =  (cpu >= 386) ? lsysi_doslash_386 : lsysi_doslash;
  void (*bslash)() = (cpu >= 386) ? lsysi_dobslash_386 : lsysi_dobslash;
  void (*at)() =     (cpu >= 386) ? lsysi_doat_386 : lsysi_doat;
  void (*drawg)() =  (cpu >= 386) ? lsysi_dodrawg_386 : lsysi_dodrawg;

  ret = (struct lsys_cmd far *) farmemalloc((long) maxval * sizeof(struct lsys_cmd));
  if (ret == NULL) {
       ts->stackoflow = 1;
       return NULL;
       }
  while (*s) {
    f = NULL;
    num = 0;
    ret[n].ch = *s;
    switch (*s) {
      case '+': f = plus;            break;
      case '-': f = minus;           break;
      case '/': f = slash;           num = (long) (getnumber(&s) * 11930465L);    break;
      case '\\': f = bslash;         num = (long) (getnumber(&s) * 11930465L);    break;
      case '@': f = at;              num = FIXEDPT(getnumber(&s));    break;
      case '|': f = pipe;            break;
      case '!': f = lsysi_dobang;     break;
      case 'd': f = lsysi_dodrawd;    break;
      case 'm': f = lsysi_dodrawm;    break;
      case 'g': f = drawg;           break;
      case 'f': f = lsysi_dodrawf;    break;
      case 'c': f = lsysi_dodrawc;    num = (long) getnumber(&s);    break;
      case '<': f = lsysi_dodrawlt;   num = (long) getnumber(&s);    break;
      case '>': f = lsysi_dodrawgt;   num = (long) getnumber(&s);    break;
      case '[': num = 1;        break;
      case ']': num = 2;        break;
      default:
	num = 3;
	break;
    }
#ifdef XFRACT
    ret[n].f = (void (*)())f;
#else
    ret[n].f = (void (*)(struct lsys_turtlestatei *))f;
#endif
    ret[n].n = num;
    if (++n == maxval) {
      doub = (struct lsys_cmd far *) farmemalloc((long) maxval*2*sizeof(struct lsys_cmd));
      if (doub == NULL) {
           farmemfree(ret);
           ts->stackoflow = 1;
           return NULL;
           }
      far_memcpy(doub, ret, maxval*sizeof(struct lsys_cmd));
      farmemfree(ret);
      ret = doub;
      maxval <<= 1;
    }
    s++;
  }
  ret[n].ch = 0;
  ret[n].f = NULL;
  ret[n].n = 0;
  n++;

  doub = (struct lsys_cmd far *) farmemalloc((long) n*sizeof(struct lsys_cmd));
  if (doub == NULL) {
       farmemfree(ret);
       ts->stackoflow = 1;
       return NULL;
       }
  far_memcpy(doub, ret, n*sizeof(struct lsys_cmd));
  farmemfree(ret);
  return doub;
}

static void _fastcall lsysi_dosincos(void)
{
   double locaspect;
   double TWOPI = 2.0 * PI;
   double twopimax;
   double twopimaxi;
   double s, c;
   int i;

   locaspect=screenaspect*xdots/ydots;
   twopimax = TWOPI / maxangle;
   for(i=0;i<maxangle;i++) {
      twopimaxi = i * twopimax;
      FPUsincos(&twopimaxi, &s, &c);
      sins[i] = (long) (s * FIXEDLT1);
      coss[i] = (long) ((locaspect * c) * FIXEDLT1);
   }
}
