/*******************************************************/
/* Linux  GNU     C++ Compiler                         */
/* Name : fastpictex.cc                                */
/* Autor: Harald Stauss                                */
/*******************************************************/

#include <iostream>
#include <fstream>
#include <float.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "fastpictex.h"

#ifdef DUMA
 #include <new>
 #include "../../duma/duma.h"
 #include "../../duma/dumapp.h"
#endif

/* ----------------------------- */
/* Implementation der Funktionen */
/* ----------------------------- */
FASTPICTEX::FASTPICTEX() {       /* Constructor */
 width=6.0; height=6.0;     // default 6x6 cm;
 nofseries=0;
 nofbar=0;
 linenoxmax=-1;
 xgrid=0; ygrid=0;
 noftline=0;
 series=(SERIES *)malloc(sizeof(SERIES));
 need_errorbarmacros=0;
 xlabel =(char*)malloc(2*sizeof(char)); strcpy(xlabel, "\0");
 ylabel =(char*)malloc(2*sizeof(char)); strcpy(ylabel, "\0");
 heading=(char*)malloc(2*sizeof(char)); strcpy(heading, "\0");
 nxticlabels=0;
 xticlabels=new char* [1];
 nofrawpictex=0;
 rawpictex=(char **)malloc(sizeof(char *));
 strcpy(plotsym[0], "$\\bullet$");
 strcpy(plotsym[1], "$\\circ$");
 strcpy(plotsym[2], "$\\diamond$");
 strcpy(plotsym[3], "$\\star$");
 strcpy(plotsym[4], "$\\ast$");
 strcpy(plotsym[5], "$\\oplus$");
 strcpy(plotsym[6], "$\\odot$");
 strcpy(plotsym[7], "$\\oslash$");
 strcpy(plotsym[8], "$\\otimes$");
 strcpy(plotsym[9], "$\\times$");
 strcpy(linesym[0], "\\setsolid");
 strcpy(linesym[1], "\\setdots");
 strcpy(linesym[2], "\\setdashes");
 strcpy(linesym[3], "\\setdashpattern <3mm,1mm,1mm,1mm>");
 strcpy(linesym[4], "\\setdashpattern <3mm,1mm,0.5mm,0.5mm,0.5mm,1mm>");
 strcpy(linesym[5], "\\setsolid");
 strcpy(linesym[6], "\\setdots");
 strcpy(linesym[7], "\\setdashes");
 strcpy(linesym[8], "\\setdashpattern <3mm,1mm,1mm,1mm>");
 strcpy(linesym[9], "\\setdashpattern <3mm,1mm,0.5mm,0.5mm,0.5mm,1mm>");
}

FASTPICTEX::~FASTPICTEX() {      /* Destructor */
 int           i;
 unsigned long j;
 for (i=0; i<nofseries; i++) {
   free(series[i].x);
   free(series[i].y);
   free(series[i].dx);
   free(series[i].dy);
   for (j=0; j<series[i].ny; j++)
     delete[]series[i].sig[j];
   free(series[i].sig);
   free(series[i].legend);
 }
 for (j=0; j<nxticlabels; j++)
     delete[]xticlabels[j];
 delete[]xticlabels;
 free(series);
 free(xlabel);
 free(ylabel);
 free(heading);
 for (i=0; i<nofrawpictex; i++) free(rawpictex[i]);
 free(rawpictex);
}

/* function to read input file */
int FASTPICTEX::Read(char *fname) {
 std::ifstream fptin;
 int           i, j, rc=0;
 int           nx, ny, ndx, ndy, ntype, nsize, nlegend;
 short         is_newcommand;
 char          *s, *fpt_command, *token, *hchar;
 unsigned long n, ndata, nallocdata=100;
 float         *data, wert;
 short         is_multiword;

 extern int  myatof(char *s, float *f);
 extern char *getsig(char *s);

 /* open input file */
 fptin.open(fname);
 if (!fptin) {
   rc=1;
   std::cout << "Cannot open input file! \n";
   return(rc);
 }

 /* allocate memory */
 s           = (char *)malloc(MAX_FIELD_LENGTH*sizeof(char));
 fpt_command = (char *)malloc(MAX_FIELD_LENGTH*sizeof(char));
 hchar       = (char *)malloc(MAX_FIELD_LENGTH*sizeof(char));
 data        = (float *)malloc(nallocdata*sizeof(float));
 nx=0; ny=0; ndx=0; ndy=0; ntype=0; nlegend=0;
 is_multiword=0;

 /* Hauptschleife */
 fptin.getline(hchar,MAX_FIELD_LENGTH);
 strcpy(s,hchar);
 while (fptin.fail() && !fptin.eof()) {
     fptin.clear();
     fptin.getline(hchar,MAX_FIELD_LENGTH);
     s=(char *)realloc(s, strlen(s)+strlen(hchar)+1);
     strcat(s, hchar);
 }
 while(!fptin.eof()) {
     /* new command or continue with old command */
     if (strchr(WHITE_SPACE, s[0])) {
         fpt_command=(char *)realloc(fpt_command, strlen(fpt_command)+strlen(s)+1);
         s=(char *)realloc(s, strlen(fpt_command)+strlen(s)+1);
         strcat(fpt_command, s);
         strcpy(s, fpt_command);
         is_newcommand=0;
     }
     else {
         is_newcommand=1;
     }
     /* get first token */
     token=strtok(s, WHITE_SPACE);
     strcpy(fpt_command, token);
     /* work on commands */
     /* comment */
     if (!strcmp(fpt_command, "%")) {
         /* do nothing */
     }
     /* size */
     else if (!strcmp(fpt_command, "size")) {
         token=fpt_command;
         if (is_newcommand) {
             nsize=0;
         }
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 if (nsize==0) {
                     if (myatof(token, &wert)) {
                         width=wert;
                         nsize++;
                     }
                 }
                 else if (nsize==1) {
                     if (myatof(token, &wert)) {
                         height=wert;
                         nsize++;
                     }
                 }
             } /* endif */
         } /* endwhile */
     }
     /* xlabel */
     else if (!strcmp(fpt_command, "xlabel")) {
         token=fpt_command;
         if (is_newcommand) strcpy(xlabel, "");
         else strcat(xlabel, " ");
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 xlabel=(char *)realloc(xlabel, strlen(xlabel)+strlen(token)+2);
                 strcat(xlabel, token); strcat(xlabel, " ");
             } /* endif */
         } /* endwhile */
         xlabel[strlen(xlabel)-1]='\0';
     }
     /* ylabel */
     else if (!strcmp(fpt_command, "ylabel")) {
         token=fpt_command;
         if (is_newcommand) strcpy(ylabel, "");
         else strcat(ylabel, " ");
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 ylabel=(char *)realloc(ylabel, strlen(ylabel)+strlen(token)+2);
                 strcat(ylabel, token); strcat(ylabel, " ");
             } /* endif */
         } /* endwhile */
         ylabel[strlen(ylabel)-1]='\0';
     }
     /* heading */
     else if (!strcmp(fpt_command, "heading")) {
         token=fpt_command;
         if (is_newcommand) strcpy(heading, "");
         else strcat(heading, " ");
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 heading=(char *)realloc(heading, strlen(heading)+strlen(token)+2);
                 strcat(heading, token); strcat(heading, " ");
             } /* endif */
         } /* endwhile */
         heading[strlen(heading)-1]='\0';
     }
     /* graph type */
     else if (!strcmp(fpt_command, "type")) {
         if (is_newcommand) {
             ntype++;
             if (ntype>nofseries) {
                 AddSeries();
             }
         }
         token=fpt_command;
         token=strtok(NULL, WHITE_SPACE);
         if (!token) {
             series[ntype-1].type=0;
         }
         if (!strcmp(token, "xy")) {
             series[ntype-1].type=1;
         }
         else if (!strcmp(token, "line")) {
             series[ntype-1].type=2;
         }
         else if (!strcmp(token, "bar")) {
             series[ntype-1].type=3;
             nofbar++;
         }
         else {
             series[ntype-1].type=0;
         }
     }
     /* trendline */
     else if (!strcmp(fpt_command, "tline")) {
         if (is_newcommand) {
             noftline++;
             if (noftline>nofseries) {
                 AddSeries();
             }
         }
         //series[noftline-1].tline=1;
         token=fpt_command;
         /* read one more token */
         token=strtok(NULL, WHITE_SPACE);
         if (token) series[noftline-1].tline=atoi(token);
         /* read remaining tokens to go to end of line */
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
         }
     }
     /* legend */
     else if (!strcmp(fpt_command, "legend")) {
         if (is_newcommand) {
             nlegend++;
             if (nlegend>nofseries) {
                 AddSeries();
             }
         }
         token=fpt_command;
         if (is_newcommand) {
             series[nlegend-1].legend=(char *)realloc(series[nlegend-1].legend, sizeof(char));
             strcpy(series[nlegend-1].legend, "");
         }
         else {
             series[nlegend-1].legend=(char *)realloc(series[nlegend-1].legend,
                                                      (strlen(series[nlegend-1].legend)+2)*sizeof(char));
             strcat(series[nlegend-1].legend, " ");
         }
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 series[nlegend-1].legend=(char *)realloc(series[nlegend-1].legend,
                                                          (strlen(series[nlegend-1].legend)+strlen(token)+2)*sizeof(char));
                 strcat(series[nlegend-1].legend, token);
                 strcat(series[nlegend-1].legend, " ");
             } /* endif */
         } /* endwhile */
         series[nlegend-1].legend[strlen(series[nlegend-1].legend)-1]='\0';
     }
     /* pictex commands */
     else if (!strcmp(fpt_command, "pictex")) {
         token=fpt_command;
         if (is_newcommand) {
             nofrawpictex++;
             rawpictex=(char **)realloc(rawpictex, nofrawpictex*sizeof(char *));
             rawpictex[nofrawpictex-1]=(char *)malloc(sizeof(char));
             strcpy(rawpictex[nofrawpictex-1], "");
         }
         else {
             rawpictex[nofrawpictex-1]=(char *)realloc(rawpictex[nofrawpictex-1],
                                                       (strlen(rawpictex[nofrawpictex-1])+2)*sizeof(char));
             strcat(rawpictex[nofrawpictex-1], " ");
         }
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 rawpictex[nofrawpictex-1]=(char *)realloc(rawpictex[nofrawpictex-1],
                                                           (strlen(rawpictex[nofrawpictex-1])+strlen(token)+2)*sizeof(char));
                 strcat(rawpictex[nofrawpictex-1], token);
                 strcat(rawpictex[nofrawpictex-1], " ");
             } /* endif */
         } /* endwhile */
         rawpictex[nofrawpictex-1][strlen(rawpictex[nofrawpictex-1])-1]='\0';
     }
     /* x coordinates */
     else if (!strcmp(fpt_command, "x")) {
         if (is_newcommand) {
             ndata=0;
             nx++;
             if (nx>nofseries) {
                 AddSeries();
             }
         }
         token=fpt_command;
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 if (ndata>=nallocdata) {
                     nallocdata+=100;
                     data=(float *)realloc(data, nallocdata*sizeof(float));
                 } /* endif */
                 data[ndata]=atof(token);
                 ndata++;
             } /* endif */
         } /* endwhile */
         series[nx-1].nx=ndata;
         series[nx-1].x=(float *)realloc(series[nx-1].x, ndata*sizeof(float));
         for (n=0; n<ndata; n++) {
             series[nx-1].x[n]=data[n];
         }
     }
     /* y coordinates */
     else if (!strcmp(fpt_command, "y")) {
         if (is_newcommand) {
             ndata=0;
             ny++;
             if (ny>nofseries) {
                 AddSeries();
             }
         }
         token=fpt_command;
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 if (ndata>=nallocdata) {
                     nallocdata+=100;
                     data=(float *)realloc(data, nallocdata*sizeof(float));
                 } /* endif */
                 series[ny-1].sig=(char **)realloc(series[ny-1].sig, (ndata+1)*sizeof(char *));
                 series[ny-1].sig[ndata]=getsig(token);
                 data[ndata]=atof(token);
                 ndata++;
             } /* endif */
         } /* endwhile */
         series[ny-1].ny=ndata;
         series[ny-1].y=(float *)realloc(series[ny-1].y, ndata*sizeof(float));
         for (n=0; n<ndata; n++) {
             series[ny-1].y[n]=data[n];
         }
     }
     /* dx coordinates */
     else if (!strcmp(fpt_command, "dx")) {
         if (is_newcommand) {
             need_errorbarmacros=1;
             ndata=0;
             ndx++;
             if (ndx>nofseries) {
                 AddSeries();
             }
         }
         token=fpt_command;
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 if (ndata>=nallocdata) {
                     nallocdata+=100;
                     data=(float *)realloc(data, nallocdata*sizeof(float));
                 } /* endif */
                 data[ndata]=atof(token);
                 ndata++;
             } /* endif */
         } /* endwhile */
         series[ndx-1].ndx=ndata;
         series[ndx-1].dx=(float *)realloc(series[ndx-1].dx, ndata*sizeof(float));
         for (n=0; n<ndata; n++) {
             series[ndx-1].dx[n]=data[n];
         }
     }
     /* dy coordinates */
     else if (!strcmp(fpt_command, "dy")) {
         if (is_newcommand) {
             need_errorbarmacros=1;
             ndata=0;
             ndy++;
             if (ndy>nofseries) {
                 AddSeries();
             }
         }
         token=fpt_command;
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 if (ndata>=nallocdata) {
                     nallocdata+=100;
                     data=(float *)realloc(data, nallocdata*sizeof(float));
                 } /* endif */
                 data[ndata]=atof(token);
                 ndata++;
             } /* endif */
         } /* endwhile */
         series[ndy-1].ndy=ndata;
         series[ndy-1].dy=(float *)realloc(series[ndy-1].dy, ndata*sizeof(float));
         for (n=0; n<ndata; n++) {
             series[ndy-1].dy[n]=data[n];
         }
     }
     /* xticlabels  */
     else if (!strcmp(fpt_command, "xticlabels")) {
         is_multiword=0; nxticlabels=0;
         token=fpt_command;
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
             if (token) {
                 if (is_multiword) {
                     xticlabels[nxticlabels-1]=(char *)realloc(xticlabels[nxticlabels-1],
                                                               strlen(xticlabels[nxticlabels-1])+1
                                                               +strlen(token)+1);
                     strcat(xticlabels[nxticlabels-1], " ");
                     strcat(xticlabels[nxticlabels-1], token);
                     if (token[strlen(token)-1]=='"') {
                         xticlabels[nxticlabels-1][strlen(xticlabels[nxticlabels-1])-1]='\0';
                         is_multiword=0;
                     }
                 }
                 else {
                     if (token[0]=='"') {
                         is_multiword=1;
                         token++;
                     }
                     nxticlabels++;
                     xticlabels=(char **)realloc(xticlabels, nxticlabels * sizeof(char *));
                     xticlabels[nxticlabels-1]=new char [strlen(token)+1];
                     strcpy(xticlabels[nxticlabels-1], token);
                     if (token[strlen(token)-1]=='"') {
                         xticlabels[nxticlabels-1][strlen(xticlabels[nxticlabels-1])-1]='\0';
                         is_multiword=0;
                     }
                 }
             } /* endif */
         } /* endwhile */
     }
     /* xgrid lines */
     else if (!strcmp(fpt_command, "xgrid")) {
         xgrid=1;
     }
     /* ygrid lines */
     else if (!strcmp(fpt_command, "ygrid")) {
         ygrid=1;
     }
     /* unknown command */
     else {
         token=fpt_command;
         while (token) {
             token=strtok(NULL, WHITE_SPACE);
         }
     }
     /* read new line */
     fptin.getline(hchar,MAX_FIELD_LENGTH);
     s=(char *)realloc(s, strlen(hchar)+1);
     strcpy(s,hchar);
     while (fptin.fail() && !fptin.eof()) {
         fptin.clear();
         fptin.getline(hchar,MAX_FIELD_LENGTH);
         s=(char *)realloc(s, strlen(s)+strlen(hchar)+1);
         strcat(s, hchar);
     }
 } /* endwhile */

 /* set x coordinates for bar charts if number of x coords different from number of y coords */
 for (i=0; i<nofseries; i++) {
   if (series[i].type==3) {
     if (series[i].ny>0 && (series[i].nx!=series[i].ny)) {
       series[i].nx=series[i].ny;
       series[i].x=(float *)realloc(series[i].x, series[i].nx*sizeof(float));
       for (n=0; n<series[i].nx; n++) {
         series[i].x[n]=n+1;
       }
     }
   }
 }
 /* set x coordinates for line and xy series without x-values */
 linenoxmax=-1; j=0;
 for (i=0; i<nofseries; i++) {
     if ((series[i].type==2 || series[i].type==1) && series[i].nx==0) {
         if (series[i].ny>0) {
             if (series[i].ny>j) {j=series[i].ny; linenoxmax=i;}
             series[i].nx=series[i].ny;
             series[i].x=(float *)realloc(series[i].x, series[i].nx*sizeof(float));
             for (n=0; n<series[i].nx; n++) {
                 series[i].x[n]=n+1;
             }
         }
     }
 }
 /* close input file */
 free(s); free(hchar); free(fpt_command);
 free(data);
 fptin.close();
 return(rc);
}

/* function to write PicTeX file */
int FASTPICTEX::Write(char *fname) {
 std::ofstream pictex;
 int           rc=0, i, j, is_fault;
 short         is_valid, is_first;
 short         nxy=0, nline=0, nbar=0;
 unsigned long n;
 float         xmin=0, xmax=10, xstep=1, ymin=0, ymax=10, ystep=1;
 float         xlmin, xlmax;
 float         valmin=0, valmax=10, step=1;
 float         xunit, yunit;
 float         ypos;
 unsigned long nreg;
 float         sx, sy, sx2, sy2, sxy, qx, qy, qxy;
 float         ax, bx, ay, by, r, s2xy, s2yx, pwert;
 float         rxmin, rxmax;

 extern float prho(int n, float is, int *ifault);
 extern int scale(float fmn, float fmx, int n, float *valmin, float *step, float *valmax);

 /* open output file */
 pictex.open(fname);
 if (!pictex) {
   rc=1;
   std::cout << "Cannot open output file! \n";
   return(rc);
 }

 /* write header of pictex file */
 pictex << "% ......   start of pictex file generated by FastPicTeX   ...... \n";
 pictex << "\\beginpicture \n";
 /* need errorbar macros ? */
 if (need_errorbarmacros) {
   pictex << "% ......   macros for errorbars   ...... \n";
   pictex << "\\newcommand{\\putxerrorbar}[3]{% \n";
   pictex << "\\dimen0=\\Xdistance{#1} \\dimen1=\\Ydistance{#2} \\dimen2=\\Xdistance{#3} \n";
   pictex << "\\unitlength=1pt \\setdimensionmode \n";
   pictex << "\\dimen3=\\dimen0 \\advance \\dimen3 by -\\dimen2 \n";
   pictex << "\\dimen4=\\dimen0 \\advance \\dimen4 by \\dimen2 \n";
   pictex << "\\dimen5=\\dimen1 \\advance \\dimen5 by -1mm \n";
   pictex << "\\dimen6=\\dimen1 \\advance \\dimen6 by 1mm \n";
   pictex << "\\putrule from {\\dimen3} {\\dimen1} to {\\dimen4} {\\dimen1} \n";
   pictex << "\\putrule from {\\dimen3} {\\dimen5} to {\\dimen3} {\\dimen6} \n";
   pictex << "\\putrule from {\\dimen4} {\\dimen5} to {\\dimen4} {\\dimen6} \n";
   pictex << "\\setcoordinatemode } \n";
   pictex << "\\newcommand{\\putyerrorbar}[3]{% \n";
   pictex << "\\dimen0=\\Xdistance{#1} \\dimen1=\\Ydistance{#2} \\dimen2=\\Ydistance{#3} \n";
   pictex << "\\unitlength=1pt \\setdimensionmode \n";
   pictex << "\\dimen3=\\dimen1 \\advance \\dimen3 by -\\dimen2 \n";
   pictex << "\\dimen4=\\dimen1 \\advance \\dimen4 by \\dimen2 \n";
   pictex << "\\dimen5=\\dimen0 \\advance \\dimen5 by -1mm \n";
   pictex << "\\dimen6=\\dimen0 \\advance \\dimen6 by 1mm \n";
   pictex << "\\putrule from {\\dimen0} {\\dimen3} to {\\dimen0} {\\dimen4} \n";
   pictex << "\\putrule from {\\dimen5} {\\dimen3} to {\\dimen6} {\\dimen3} \n";
   pictex << "\\putrule from {\\dimen5} {\\dimen4} to {\\dimen6} {\\dimen4} \n";
   pictex << "\\setcoordinatemode } \n";
 }
 /* get extrema */
 GetExtrema(0,0.0,0.0,&xmin, &xmax, &ymin, &ymax);
 /* write coordinate-system and axis */
 /* number of x-tics depend on xticlabels first */
 xlmin=FLT_MIN; xlmax=FLT_MAX;
 if (nxticlabels>0) {
     xlmin=1.0; xlmax=(float)nxticlabels;
     GetExtrema(1, xlmin, xlmax, &xmin, &xmax, &ymin, &ymax);
     xmin=1.0; xmax=(float)nxticlabels;
     scale(xmin-1, xmax+1, nxticlabels+2, &valmin, &step, &valmax);
 }
 /* number of x-tics depend on bar-graphs second */
 else if (nofbar>0) {
     j=0;
     for (i=0; i<nofseries; i++) {
         if (series[i].type==3)
             if (series[i].nx>j) j=series[i].nx;
     }
     scale(xmin-1, xmax+1, j+2, &valmin, &step, &valmax);
 }
 /* number of x-tics depend on line- and xy-series without x-values third */
 else if (linenoxmax>=0)
   scale(xmin-1, xmax+1, series[linenoxmax].nx+2, &valmin, &step, &valmax);
 /* finally default */
 else
   scale(xmin, xmax, N_XTICKS, &valmin, &step, &valmax);
 xstep=floor(step*10000.0+0.5)/10000.0;
 if (xmin>valmin) xmin=floor(valmin*10000.0+0.5)/10000.0;
 else xmin=floor((valmin-xstep)*10000.0+0.5)/10000.0;
 if (xmax<valmax) xmax=floor(valmax*10000.0+0.5)/10000.0;
 else xmax=floor((valmax+xstep)*10000.0+0.5)/10000.0;
 scale(ymin, ymax, N_YTICKS, &valmin, &step, &valmax);
 ystep=floor(step*10000.0+0.5)/10000.0;
 if (ymin>valmin) ymin=floor(valmin*10000.0+0.5)/10000.0;
 else ymin=floor((valmin-ystep)*10000.0+0.5)/10000.0;
 if (ymax<valmax) ymax=floor(valmax*10000.0+0.5)/10000.0;
 else ymax=floor((valmax+ystep)*10000.0+0.5)/10000.0;
 xunit=width/(xmax-xmin);
 yunit=height/(ymax-ymin);
 pictex << "\\setcoordinatesystem units <" << xunit << "cm,"
        << yunit << "cm> point at 0 0 \n";
 pictex << "\\setplotarea x from " << xmin << " to " << xmax
        << ", y from " << ymin << " to " << ymax << " \n";
 /* plot axis */
 pictex << "% .......... axis ............ \n";
 pictex << "\\axis bottom ";
 if (xlabel) pictex << "label {" << xlabel << "} ";
 pictex << "ticks ";
 if (xgrid) pictex << "andacross ";
 if (nxticlabels==0) {
   pictex << "numbered from "
          << xmin << " to " << xmax << " by " << xstep << " / \n";
 }
 else {
   pictex << "withvalues {} ";
   for (n=0; n<nxticlabels; n++) pictex << "{" << xticlabels[n] << "}" << " ";
   pictex << "{} / \n";
   pictex << "quantity " << nxticlabels+2 << " / \n";
 }
 pictex << "\\axis left ";
 if (ylabel) pictex << "label {" << ylabel << "} ";
 pictex << "ticks ";
 if (ygrid) pictex << "andacross ";
 pictex << "numbered from "
        << ymin << " to " << ymax << " by " << ystep << " / \n";
 /* plot heading */
 pictex << "% .......... heading ............ \n";
 if (heading) pictex << "\\plotheading {" << heading << "} \n";
 /* plot series */
 pictex.setf(std::ios::fixed);
 pictex << "% .......... series ............. \n";
 for (i=0; i<nofseries; i++) {
   switch (series[i].type) {
   case 1:    // xy plot;
       /* write dots and calculate regression line */
       sx2=0; sy2=0; sx=0; sy=0; sxy=0; nreg=0;
       for (n=0; n<series[i].nx; n++) {
           if (series[i].x[n]>=xlmin && series[i].x[n]<=xlmax) {
               pictex << "\\put {" << plotsym[(int)fmod(nxy,10)] << "} at "
                      << series[i].x[n] << " " << series[i].y[n] << " \n";
               sx2+=(series[i].x[n]*series[i].x[n]); sy2+=(series[i].y[n]*series[i].y[n]);
               sxy+=(series[i].x[n]*series[i].y[n]);
               sx+=series[i].x[n]; sy+=series[i].y[n];
               nreg++;
               if (series[i].ndx>n) {
                   pictex << "\\putxerrorbar{" << series[i].x[n] << "}{"
                          << series[i].y[n] << "}{" << series[i].dx[n] << "} \n";
               }
               if (series[i].ndy>n) {
                   pictex << "\\putyerrorbar{" << series[i].x[n] << "}{"
                          << series[i].y[n] << "}{" << series[i].dy[n] << "} \n";
               }
               /* plot significance signs */
               if (series[i].sig[n]) {
                   ypos=series[i].y[n];
                   if (series[i].ndy>n) ypos+=series[i].dy[n];
                   pictex << "\\put {" << series[i].sig[n] << "} [b] <0mm,0.5\\baselineskip> at "
                          << series[i].x[n] << " " << ypos << "\n";
               }
           }
       }
       /* calculate regression line */
       if (nreg>2) {
           qx=sx2-(sx*sx/nreg); qy=sy2-(sy*sy/nreg); qxy=sxy-(sx*sy/nreg);
           r=qxy/sqrt(qx*qy);
           by=qxy/qx; bx=qxy/qy;
           ay=(sy-by*sx)/nreg; ax=(sx-bx*sy)/nreg;
           s2yx=(qy-(qxy*qxy/qx))/(nreg-2);
           s2xy=(qx-(qxy*qxy/qy))/(nreg-2);
           pwert=2.0*(1.0-prho(nreg, (nreg*nreg*nreg-nreg)*(1.0-fabs(r))/6.0, &is_fault));
           series[i].m1=by; series[i].m2=bx; series[i].b=ay; series[i].nreg=nreg;
           if (pwert>1.0) pwert=1.0;
           if (pwert<0.0) pwert=0.0;
           /* plot regression line */
           rxmin=xmin; rxmax=xmax;
           if (series[i].tline>0) {
               if (ymin>xmin*by+ay) rxmin=(ymin-ay)/by;
               if (ymax<xmin*by+ay) rxmin=(ymax-ay)/by;
               if (ymin>xmax*by+ay) rxmax=(ymin-ay)/by;
               if (ymax<xmax*by+ay) rxmax=(ymax-ay)/by;
               pictex << "\\setlinear \n";
               pictex << linesym[(int)fmod(nxy,10)] << " \n";
               /* set formatting */
               pictex << "\\plot " << rxmin << " " << rxmin*by+ay << " "
                      << rxmax << " " << rxmax*by+ay << " /\n";
           }
       } /* endif nreg>2 */
       else series[i].tline=0;
       /* clean up */
       nxy++;
       break;
   case 2:     // line graph;
     /* write lines */
     pictex << "\\setlinear \n";
     pictex << linesym[(int)fmod(nline,10)] << " \n";
     is_first=1;
     for (n=0; n<series[i].nx; n++) {
         if (series[i].x[n]>=xlmin && series[i].x[n]<=xlmax) {
             if (is_first) {pictex << "\\plot "; is_first=0;}
             pictex << series[i].x[n] << " " << series[i].y[n] << " ";
         }
     }
     pictex << "/ \n";
     pictex << "\\setsolid \n";
     for (n=0; n<series[i].nx; n++) {
         if (series[i].x[n]>=xlmin && series[i].x[n]<=xlmax) {
             if (series[i].ndx>n) {
                 pictex << "\\putxerrorbar{" << series[i].x[n] << "}{"
                        << series[i].y[n] << "}{" << series[i].dx[n] << "} \n";
             }
             if (series[i].ndy>n) {
                 pictex << "\\putyerrorbar{" << series[i].x[n] << "}{"
                        << series[i].y[n] << "}{" << series[i].dy[n] << "} \n";
             }
             /* plot significance signs */
             if (series[i].sig[n]) {
                 ypos=series[i].y[n];
                 if (series[i].ndy>n) ypos+=series[i].dy[n];
                 pictex << "\\put {" << series[i].sig[n] << "} [b] <0mm,0.5\\baselineskip> at "
                        << series[i].x[n] << " " << ypos << "\n";
             }
         }
     }
     nline++;
     break;
   case 3:     // bar graph;
     /* write bars */
     if (nbar==0) {
       pictex << "\\shaderectanglesoff \n";
     }
     if (nbar>0 && nbar<nofbar-1) {
       pictex << "\\shaderectangleson \n";
       pictex << "\\setshadegrid span <" << 1.0-(float)(nbar-1)/(float)(nofbar-2) << "mm> \n";
     }
     if (nbar!=0 && nbar==nofbar-1) {
       pictex << "\\shaderectanglesoff%\n";
       pictex << "\\dimen0=\\linethickness%\n";
       pictex << "%look here\n";
       pictex << "\\setlength{\\linethickness}{" << (BARWIDTH*xunit)/(float)nofbar << "cm}%\n";
       //pictex << "\\setlength{\\linethickness}{\\Xdistance{" << BARWIDTH/(float)nofbar << "}}%\n";
     }
     if (nbar==0 || nbar<nofbar-1) {
       for (n=0; n<series[i].nx; n++) {
         if (series[i].x[n]>=xlmin && series[i].x[n]<=xlmax) {
             pictex << "\\putrectangle corners at ";
             pictex << (series[i].x[n]-BARWIDTH/2.0)+(BARWIDTH*(float)nbar/(float)nofbar) << " " << ymin << " and ";
             pictex << (series[i].x[n]-BARWIDTH/2.0)+(BARWIDTH*(float)(nbar+1)/(float)nofbar) << " "
                    << series[i].y[n] << " \n";
         }
       }
     }
     if (nbar!=0 && nbar==nofbar-1) {
       for (n=0; n<series[i].nx; n++) {
         if (series[i].x[n]>=xlmin && series[i].x[n]<=xlmax) {
             pictex << "\\putrule from "
                    << (series[i].x[n]-BARWIDTH/2)+BARWIDTH/(float)(2*nofbar)+(BARWIDTH*(float)(nbar)/(float)nofbar) << " "
                    << ymin << " to "
                    << (series[i].x[n]-BARWIDTH/2)+BARWIDTH/(float)(2*nofbar)+(BARWIDTH*(float)(nbar)/(float)nofbar) << " "
                    << series[i].y[n] << " \n";
         }
       }
       pictex << "\\setlength{\\linethickness}{\\dimen0}%\n";
     }
     for (n=0; n<series[i].nx; n++) {
         if (series[i].x[n]>=xlmin && series[i].x[n]<=xlmax) {
             /* plot errorbars */
             if (series[i].ndy>n) {
                 pictex << "\\putyerrorbar{"
                        << (series[i].x[n]-BARWIDTH/2)+BARWIDTH/(float)(2*nofbar)+(BARWIDTH*(float)(nbar)/(float)nofbar)
                        << "}{"
                        << series[i].y[n] << "}{" << series[i].dy[n] << "} \n";
             }
             /* plot significance signs */
             if (series[i].sig[n]) {
                 ypos=series[i].y[n];
                 if (series[i].ndy>n) ypos+=series[i].dy[n];
                 pictex << "\\put {" << series[i].sig[n] << "} [b] <0mm,0.5\\baselineskip> at "
                        << (series[i].x[n]-BARWIDTH/2)+BARWIDTH/(float)(2*nofbar)+(BARWIDTH*(float)(nbar)/(float)nofbar)
                        << " " << ypos << "\n";
             }
         }
     }
     nbar++;
     break;
   } /* endswitch */
 } /* endfor */
 /* legend */
 is_valid=0;
 for (i=0; i<nofseries; i++) {
     if ((strlen(series[i].legend)>0) ||
         (series[i].type==1 && series[i].tline>0 && series[i].nreg>2))
         is_valid=1;
 }
 if (is_valid) {
     pictex << "% .......... legends ............. \n";
     j=0; nxy=0; nbar=0, nline=0;
     for (i=0; i<nofseries; i++) {
         if ((strlen(series[i].legend)>0) ||
             (series[i].type==1 && series[i].tline>0 && series[i].nreg>2)) {
             /* first symbols */
             if (series[i].type==1) { /* xy */
                 if (series[i].tline>0 && series[i].nreg>2) {
                     pictex << "\\put {" << plotsym[(int)fmod(nxy,10)] << "} [cc] <0.2cm,-" << 2.5*j+1 << "ex> at " << xmax << " " << ymax << "\n";
                     pictex << "\\setlinear \n";
                     pictex << linesym[(int)fmod(nxy,10)] << " \n";
                     pictex << "\\put {\\frame{\\hspace*{8mm}}} [lc] <0.4cm,-" << 2.5*j+1 << "ex> at " << xmax << " " << ymax << "\n";
                 }
                 else
                     pictex << "\\put {" << plotsym[(int)fmod(nxy,10)] << "} [cc] <0.8cm,-" << 2.5*j+1 << "ex> at " << xmax << " " << ymax << "\n";
             }
             if (series[i].type==2) { /* line */
                 pictex << linesym[(int)fmod(nline,10)] << " \n";
                 pictex << "\\putrule <0.4cm,-" << 2.5*j+1 << "ex> from " <<
                     xmax << " " << ymax << " to " << xmax+0.8/xunit << " " << ymax << "\n";
             }
             if (series[i].type==3) { /* bar */
                 if (nbar==0) {
                     pictex << "\\shaderectanglesoff \n";
                     pictex << "\\putrectangle <5mm,-" << 2.5*j << "ex> corners at " <<
                         xmax << " " << ymax << " and " << xmax+0.6/xunit << " " << ymax-0.3/yunit << "\n";
                 }
                 if (nbar>0 && nbar<nofbar-1) {
                     pictex << "\\shaderectangleson \n";
                     pictex << "\\setshadegrid span <" << 1.0-(float)(nbar-1)/(float)(nofbar-2) << "mm> \n";
                     pictex << "\\putrectangle <5mm,-" << 2.5*j << "ex> corners at " <<
                         xmax << " " << ymax << " and " << xmax+0.6/xunit << " " << ymax-0.3/yunit << "\n";
                 }
                 if (nbar!=0 && nbar==nofbar-1) {
                     pictex << "\\shaderectanglesoff%\n";
                     pictex << "\\dimen0=\\linethickness%\n";
                     pictex << "\\setlength{\\linethickness}{6mm}%\n";
                     pictex << "\\putrule <8mm,-" << 2.5*j << "ex> from " <<
                         xmax << " " << ymax << " to " << xmax << " " << ymax-0.3/yunit << "\n";
                     pictex << "\\setlength{\\linethickness}{\\dimen0}%\n";
                 }
             }
             /* then labels */
             pictex << "\\put {" << series[i].legend;
             if (series[i].type==1 && series[i].tline>0 && series[i].nreg>2) {
                 if (strlen(series[i].legend)>0) pictex << ", n=" << series[i].nreg;
                 else pictex << "n=" << series[i].nreg;
             }
             pictex << "} [lc] <1.4cm,-" << 2.5*j+1 << "ex> at " << xmax << " " << ymax << "\n";
             j++;
             if (series[i].type==1 && series[i].tline==2 && series[i].nreg>2) {
                 pictex << "\\put ";
                 by=series[i].m1; ay=series[i].b; bx=series[i].m2;
                 pictex.precision(3);
                 if (ay>0)
                     pictex << "{\\footnotesize $(y=" << by << "x+" << ay << ", r^2=" << by*bx << ")$}";
                 else pictex << "{\\footnotesize $(y=" << by << "x-" << (-1)*ay << ", r^2=" << by*bx << ")$}";
                 pictex.precision(6);
                 pictex << " [lc] <1.4cm,-" << 2.5*j+1 << "ex> at " << xmax << " " << ymax << "\n";
                 j++;
             }
         }
         if (series[i].type==1) nxy++;
         if (series[i].type==2) nline++;
         if (series[i].type==3) nbar++;
     } /* endfor i */
 } /* endif is_valid */
 /* pictex commands */
 if (nofrawpictex>0) pictex << "% .......... rawpictex ............. \n";
 for (i=0; i<nofrawpictex; i++) pictex << rawpictex[i] << " \n";

 /* close pictex file */
 pictex << "\\endpicture \n";
 pictex << "% ......   end of pictex file generated by FastPicTeX   ...... \n";
 pictex.close();
 return(rc);
}

/* function to add a new series */
int FASTPICTEX::AddSeries() {
 nofseries++;
 series=(SERIES *)realloc(series, nofseries*sizeof(SERIES));
 series[nofseries-1].type=0;   /* graph type not defined (0) */
 series[nofseries-1].ndata=0;
 series[nofseries-1].nx=0; series[nofseries-1].ny=0;
 series[nofseries-1].ndx=0; series[nofseries-1].ndy=0;
 series[nofseries-1].tline=0; /* no trendline */
 series[nofseries-1].x=(float *)malloc(sizeof(float));
 series[nofseries-1].y=(float *)malloc(sizeof(float));
 series[nofseries-1].dx=(float *)malloc(sizeof(float));
 series[nofseries-1].dy=(float *)malloc(sizeof(float));
 series[nofseries-1].sig=(char **)malloc(sizeof(char *));
 series[nofseries-1].legend=(char *)malloc(sizeof(char));
 strcpy(series[nofseries-1].legend, "\0");
}


/* Get Extrema */
int FASTPICTEX::GetExtrema(short is_limit, float x1, float x2, float *xmin, float *xmax, float *ymin, float *ymax) {
 int           rc=0, i;
 unsigned long n;
 float         wert1, wert2;
 float         mxmin, mxmax, mymin, mymax;

 if (nofseries>0) {
     /* start values */
     mxmin=FLT_MAX; mymin=FLT_MAX;
     mxmax=FLT_MIN; mymax=FLT_MIN;
     for (i=0; i<nofseries; i++) {
         for (n=0; n<series[i].nx; n++) {
             if (!is_limit || (is_limit && series[i].x[n]<=x2 && series[i].x[n]>=x1)) {
                 if (series[i].ndx>n) {
                     wert1=series[i].x[n]-series[i].dx[n];
                     wert2=series[i].x[n]+series[i].dx[n];
                 }
                 else {
                     wert1=series[i].x[n];
                     wert2=series[i].x[n];
                 }
                 if (mxmin>wert1) mxmin=wert1;
                 if (mxmax<wert2) mxmax=wert2;
                 if (series[i].ndy>n) {
                     wert1=series[i].y[n]-series[i].dy[n];
                     wert2=series[i].y[n]+series[i].dy[n];
                 }
                 else {
                     wert1=series[i].y[n];
                     wert2=series[i].y[n];
                 }
                 if (mymin>wert1) mymin=wert1;
                 if (mymax<wert2) mymax=wert2;
             } /* endif limit */
         } /* endfor */
     } /* endfor */
     *xmin=mxmin; *xmax=mxmax;
     *ymin=mymin; *ymax=mymax;
 }
 else rc=-1;
 return(rc);
}