/*****************************************************************************
 * Modul                : main.c                                             *
 * Zweck                : Simplexalgorithmus                                 *
 * Autor                : Stefan Förster                                     *
 *                                                                           *
 * Datum      | Version | Bemerkung                                          *
 * -----------|---------|--------------------------------------------------- *
 * 14.03.1989 | 1.0     |                                                    *
 * 15.03.1989 |         | Zeitmessung mittels CurrentTime()                  *
 * 17.03.1989 | 1.1     | SetTaskPri() jetzt korrekt                         *
 *            |         | min cTx zugelassen                                 *
 * 18.03.1989 | 1.2     | Optional Ausgabe an ein File möglich (-f)          *
 * 06.05.1989 | 1.3     | BUG in Phase I behoben (TryPivot())                *
 * 09.05.1989 |         | BUG in Phase II behoben (PhaseII())                *
 *            |         | Neue Funktion PrintTime() + Zeitmessung geändert   *
 * 20.05.1989 | 1.4     | Bessere Ausgabe bei VERBOSE ON                     *
 *            |         | Neues Kommando "INVERT"                            *
 *            |         | Kommando "VERBOSE" erweitert: VERBOSE [N]          *
 *            |         | Neues Kommando "PRICE"                             *
 *            |         | Neues Kommando "MINIMIZE"                          *
 *            |         | 'bsum' als globale Variable für PhaseI() definiert *
 * 21.07.1989 |         | mpsx.c: Nach NAME jetzt max. 33 Zeichen möglich.   *
 * 06.08.1989 | 1.5     | Überflüssige Parameter gestrichen                  *
 * 07.08.1989 |         | Ein-/Ausgabe in eigenem (NEW)CON:-window           *
 *            |         | DEFAULT-Option                                     *
 *****************************************************************************/



IMPORT USHORT         PhaseI(), PhaseII();
IMPORT BOOL           MPSX(), SearchExpr();
IMPORT VOID           GetInput();
IMPORT SHORT          GetExpr();
IMPORT VOID           GiveMemBack(), GetRidOfLists(), PrintError(), Cap();
IMPORT VOID           CurrentTime(), *OpenLibrary(), SetTaskPri(), fclose();
IMPORT struct Task    *FindTask();
IMPORT FILE           *freopen();

struct IntuitionBase  *IntuitionBase = NULL;
struct GfxBase        *GfxBase = NULL;

DOUBLE	INFINITE = 1.0/0.0;  /* IEEE: NAN (Not A Number) */


#ifdef PAL
TEXT    version[] = "NEWCON:0/11/640/245/ASimplex Version 1.5";
#else
TEXT    version[] = "NEWCON:0/11/640/189/ASimplex Version 1.5";
#endif

TEXT    comment[] = "THE Amiga Simplex Program\n(c) 06.02.-07.08.1989\n\
Stefan Förster";

TEXT    reverse[] = "\033[0;0H\033[41;32m\033[J";

FILE    *file[2] = { NULL }, *con = NULL;

TEXT    symbols[NUM_SYMBOLS][MAX_STRLEN+1];
BOOL    minimize = _FALSE; /* _TRUE, falls Zielfunktion minimiert wird */
BOOL    global_minimize = _FALSE;
LONG    invert_freq = INVERT_FREQUENCY, verbose_freq = 1L;
USHORT  method = MIXED;
DOUBLE  bsum; /* für PhaseI() */

ITEMPTR list[NUM_LISTS];
TEXT    args[BUFFER], line[BUFFER];

SHORT   m, n, *B = NULL, *Nq = NULL, *Nminus = NULL;
DOUBLE  c0, c0start;
DOUBLE  *A = NULL, *AB1 = NULL, *b = NULL, *b2q = NULL;
DOUBLE  *c = NULL, *c2 = NULL, *upper = NULL, *lower = NULL;
DOUBLE  *x = NULL, *cq = NULL, *pi = NULL, *dq = NULL, *help = NULL;


STRPTR errors[] = {
  (STRPTR)"Invalid arguments for \"load\"",
  (STRPTR)"Identifier too long",
  (STRPTR)"Identifier declared twice",
  (STRPTR)"Unknown Identifier",
  (STRPTR)"Sections have to start in column 1",
  (STRPTR)"Section defined twice",
  (STRPTR)"Unknown section",
  (STRPTR)"Illegal order of sections",
  (STRPTR)"Missing NAME-section (1st section) or NAME-section empty",
  (STRPTR)"Missing ROWS-section (2nd section) or ROWS-section empty",
  (STRPTR)"Missing goal or goal not found",
  (STRPTR)"Missing COLUMNS-section (3rd section) or COLUMNS-section empty",
  (STRPTR)"Missing RHS-section (4th section)",
  (STRPTR)"Missing ENDATA-section (last section)",
  (STRPTR)"Constraint must be of type N, E, L or G",
  (STRPTR)"Bound must be of type UP or LO",
  (STRPTR)"Lower bound > upper bound",
  (STRPTR)"Expression in RANGES belongs to a \"E\"-constraint",
  (STRPTR)"Can\'t find necessary expression in this line",
  (STRPTR)"File-name too long",
  (STRPTR)"Can\'t open file for read access",
  (STRPTR)"Can\'t open file for write access",
  (STRPTR)"End-of-file reached",
  (STRPTR)"Not enough memory",
  (STRPTR)"FATAL ERROR (This error should not occur, reboot system!)"
};





main()
{
  SHORT   start, stop, length, i;
  BOOL    end = _FALSE;
  USHORT  verbose = 0, result;
  TEXT    ch;
  STRPTR  sptr;
  ULONG   iter1, iter2, sec1, sec2, micro1, micro2;
  LONG    pri = 0L;
  SHORT   atoi();
  VOID    PrintSolution(), PrintTime(), Leave(), OpenAll();


  OpenAll();

  FOREVER {

    printf(">> ");

    GetInput(args);
    Cap(args,0,BUFFER);

    if(SearchExpr(args,&start,&stop)) {
      length = GetExpr(line,args,start,stop);

      if(strcmp(line,"HELP") == 0) {
        puts("   HELP");
        puts("   LOAD <file> [-cGOAL] [-bRHS] [-rRANGE] [-uBOUND] [-m] \
[-fFILE]");
        puts("   VERBOSE [N]");
        puts("   INVERT [N]");
        puts("   PRICE [MIXED|CYCL|STEEP]");
        puts("   MINIMIZE");
        puts("   PRIORITY [N]");
        puts("   DEFAULT");
        puts("   QUIT");
      }

      else if(strcmp(line,"QUIT") == 0) {
        do {
          printf("?? Really [Y,N] ? ");
          GetInput(args);
          Cap(args,0,BUFFER);
          if(!SearchExpr(args,&start,&stop)) start = 0;
        } while((ch = *(args+start))!='N' && ch!='Y');
        if(ch == 'Y') Leave("");
      }

      else if(strcmp(line,"VERBOSE") == 0) {
        sptr = args+stop+1;
        if(SearchExpr(sptr,&start,&stop)) {
          verbose = VERBOSE;
          length = GetExpr(line,sptr,start,stop);
          verbose_freq = (LONG)atoi(line);
          if(verbose_freq < 1L) verbose_freq = 1L;
        }
        else verbose = verbose ? 0 : VERBOSE;
        printf("   VERBOSE %ld O%s\n",verbose_freq, verbose ? "N" : "FF");
      }

      else if(strcmp(line,"INVERT") == 0) {
        sptr = args+stop+1;
        if(SearchExpr(sptr,&start,&stop)) {
          length = GetExpr(line,sptr,start,stop);
          invert_freq = (LONG)atoi(line);
          if(invert_freq <= 0L) invert_freq = 0L;
          printf("   Changed invert frequency to %ld.\n",invert_freq);
        }
        else printf("   Current invert frequency is %ld.\n",invert_freq);
      }

      else if(strcmp(line,"PRICE") == 0) {
        sptr = args+stop+1;
        if(SearchExpr(sptr,&start,&stop)) {
          length = GetExpr(line,sptr,start,stop);
          if(strcmp(line,"MIXED") == 0)      method = MIXED;
          else if(strcmp(line,"CYCL") == 0)  method = CYCL;
          else if(strcmp(line,"STEEP") == 0) method = STEEP;
          printf("   Changed price method to ");
        }
        else printf("   Current price method is ");
        switch(method) {
          case MIXED: puts("MIXED.");
                      break;
          case CYCL : puts("CYCL.");
                      break;
          case STEEP: puts("STEEP.");
                      break;
        }
      }

      else if(strcmp(line,"MINIMIZE") == 0) {
        global_minimize = !global_minimize;
        printf("   GLOBAL MINIMIZE O%s.\n", global_minimize ? "N" : "FF");
      }

      else if(strcmp(line,"PRIORITY") == 0) {
        sptr = args+stop+1;
        if(SearchExpr(sptr,&start,&stop)) {
          length = GetExpr(line,sptr,start,stop);
          pri = (LONG)atoi(line);
          if(pri<0L) pri = 0L;
          if(pri>20L) pri = 20L;
          SetTaskPri(FindTask((STRPTR)0),pri);
          printf("   Changed task priority to %ld.\n",pri);
        }
        else printf("   Current task priority is %ld.\n",pri);
      }

      else if(strcmp(line,"DEFAULT") == 0) {
        SetTaskPri(FindTask((STRPTR)0),0L);
        global_minimize = _FALSE;
        method = MIXED;
        invert_freq = INVERT_FREQUENCY;
        verbose = 0;
        verbose_freq = 1;
        puts("   Changed task priority to 0.");
        puts("   Changed price method to MIXED.");
        printf("   Changed invert frequency to %ld.\n",INVERT_FREQUENCY);
        puts("   GLOBAL MINIMIZE OFF.");
        puts("   VERBOSE 1 OFF.");
      }

      else if(strcmp(line,"LOAD") == 0) {
        if(MPSX(args+stop+1)) {
          CurrentTime(&sec1,&micro1);
          if(!verbose) puts("@@ Please wait - calculation.");
          iter1 = iter2 = 0L;
          result = PhaseI(&m,&n,c,c2,&c0,c0start,verbose,x,cq,pi,dq,
                          Nminus,help,&iter1);
          if(result == NOT_INVERTABLE) {
            puts("   Matrix AB is not invertable.");
            if(file[1]) fputs("   Matrix AB is not invertable.\n",file[1]);
          }

          else if(result == UNBOUNDED) {
            puts("   PHASE I IS UNBOUNDED. THIS SHOULD NOT OCCUR!!");
            if(file[1])
              fputs("   PHASE I IS UNBOUNDED. THIS SHOULD NOT OCCUR!!\n",file[1]);
          }

          else if(result == EMPTY) {
            puts("   This problem is not solveable.");
            if(file[1]) fputs("   This problem is not solveable.\n",file[1]);
          }

          else if(result == CLEAR_CUT) PrintSolution();

          else {
            result = PhaseII(m,n,c,&c0,c0start,verbose|PHASE_II,x,cq,pi,
                             dq,Nminus,help,&iter2);
            if(result == NOT_INVERTABLE) {
              puts("   Matrix AB is not invertable.");
              if(file[1]) fputs("   Matrix AB is not invertable.\n",file[1]);
            }
            else if(result == UNBOUNDED) {
              puts("   This problem is unbounded.");
              if(file[1]) fputs("   This problem is unbounded.\n",file[1]);
            }
            else PrintSolution();
          }

          printf("-> %ld iterations needed.\n",iter1+iter2);
          if(file[1]) fprintf(file[1],"-> %ld iterations needed.\n",iter1+iter2);
          GiveMemBack();
          GetRidOfLists();
          minimize = _FALSE;
          CurrentTime(&sec2,&micro2);
          PrintTime("-> Solution time = ",sec1,micro1,sec2,micro2,file[1]);
        }
        if(file[0]) {
          fclose(file[0]);
          file[0] = NULL;
        }
        if(file[1]) {
          fprintf(file[1],"\f");
          fclose(file[1]);
          file[1] = NULL;
        }
      }

      else printf("   Unknown command \"%s\".\n",line);

    }

  } /* FOREVER */

}



/*****************************************************************************
 * VOID PrintTime(str,s1,m1,s2,m2,fptr)                                      *
 * berechnet die Zeitdifferenz zwischen s2/m2 und s1/m1 (von CurrentTime()   *
 * ermittelt, und gibt den String str und die Zeitdauer aus; falls fptr!=NULL*
 * auch in das file fptr.                                                    *
 *****************************************************************************/

VOID    PrintTime(str,s1,m1,s2,m2,fptr)
STRPTR  str;
ULONG   s1, m1, s2, m2;
FILE    *fptr;

{
  LONG  h, min, sec, sec100;

  sec100 = m2-m1;
  sec = s2-s1;
  if(sec100 < 0L) {
    sec100 += 1000000L;
    --sec;
  }
  sec100 /= 10000L;
  h = sec/3600L;
  min = (sec-3600L*h)/60;
  sec -= 3600L*h+60*min;
  printf("%s %3ld : %02ld : %02ld,%02ld\n",str,h,min,sec,sec100);
  if(file[1])
    fprintf(file[1],"%s %3ld : %02ld : %02ld,%02ld\n",str,h,min,sec,sec100);
}



/*****************************************************************************
 * VOID PrintSolution()                                                      *
 * Ausgabe der Lösung                                                        *
 *****************************************************************************/

VOID    PrintSolution()

{
  VOID PrintX();

  puts("@@ Solution:\n");
  printf("   %-9s = %14.10lg\n",symbols[GOAL],minimize ? -c0 : c0);
  PrintX(list[VAR_LIST],stdout);
  puts("");

  if(file[1]) {
    fputs("@@ Solution:\n\n",file[1]);
    fprintf(file[1],"   %-9s = %14.10lg\n",symbols[GOAL],minimize ? -c0 : c0);
    PrintX(list[VAR_LIST],file[1]);
    fputs("\n",file[1]);
  }
}



/*****************************************************************************
 * VOID PrintX()                                                             *
 * Gibt das Ergebnis der Berechnungen aus.                                   *
 *****************************************************************************/

VOID    PrintX(ptr,fptr)

ITEMPTR ptr;
FILE    *fptr;

{
  if(ptr) {
    PrintX(ptr->next,fptr);
    fprintf(fptr,"   %-9s = %14.10lg\n",ptr->string,x[ptr->nr-1]);
  }
}


/*****************************************************************************
 * VOID OpenAll()                                                            *
 * Öffnen der Bibliotheken und des (NEW)CON: - windows.                      *
 *****************************************************************************/

VOID OpenAll()
{
  VOID Leave();

  if(!(IntuitionBase = OpenLibrary("intuition.library",0L)))
    Leave("Can't find \"intuition.library\"!");
  if(!(GfxBase = OpenLibrary("graphics.library",0L)))
    Leave("Can't find \"graphics.library\"!");

  if(!(con = freopen(version,"w+",stdout)) &&
     !(con = freopen(version+3,"w+",stdout))  )
    Leave("Can't open (NEW)CON: - window!");
  printf("%s\033[1m%s\n%s\033[0m\033[41;32m\n\n",reverse,version+20,comment);
}



/*****************************************************************************
 * VOID Leave()                                                              *
 * Verlassen des Programms                                                   *
 *****************************************************************************/

VOID    Leave(str)

STRPTR  str;

{
  if(con)           fclose(con);
  if(GfxBase)       CloseLibrary(GfxBase);
  if(IntuitionBase) CloseLibrary(IntuitionBase);

  fprintf(stderr,"\n%s\n\n",str);
  exit(0);
}
