/* ------------------------------------------------------------------------ */
/*                                                                          */
/*  DISPATCH.C                                                              */
/*  LARRY CONRAD                                                            */
/*  EE SPECIAL PROJECT COURSE                                               */
/*  FALL/WINTER 1990                                                        */
/*                                                                          */
/* ------------------------------------------------------------------------ */

#include <conio.h>
#include <string.h>
#include <stdio.h>
#include <dos.h>
#include <math.h>
#include <graph.h>
#include <malloc.h>

#include "realtype.h"
#include "gj.h"
#include "mulreg.h"
#include "curvefit.h"
#include "worlddr.h"
#include "segraph.h"
/* #include "screen.h"   for screen dumps */
/* #include "async2.h"   for plotter */
#include "edit.h"
#include "getkey.h"
#include "userio.h"
#include "dispatch.h"


/* --------------------------- MAIN ROUTINE ------------------------------- */
/*                                                                          */
/*  MAIN HANDLES THE USER INTERFACE                                         */
/*                                                                          */
/* ------------------------------------------------------------------------ */

main()
{
    int  i;
    int  c;
    char flag;

    strcpy( mp,
	 "WELCOME!  Most I/O will be on this line in green.  Select a lesson.");

/*    print_all = 'Y';      */
    /* GET THE CONFIGURATION DATA AS NEEDED                                */
    c = get_config();

    /* SET UP INITIAL VIDEO MODE                                           */
    InitSEGraphics(16, font_path, 0);
    SetScatMarkRelSize( scatter_mark_size );

    /* Save original foreground, background, and text position. */
    oldfgd = _settextcolor( _WHITE );
    oldbgd = _setbkcolor( _RED );
    oldpos = _gettextposition();


    _clearscreen( _GCLEARSCREEN );
    if( show_message("INTRO") == 'F') return ( 'F' );
    c = any_key( 25, -1, prompt_color );

    START_MENU:

    _settextcolor( _WHITE );
    _setbkcolor( _RED );
    _displaycursor( _GCURSOROFF );
    _clearscreen( _GCLEARSCREEN );

    /* PRINT MENU */
    center_message( 1, "DISPATCH.C - By Larry Conrad" );
    center_message( 3, "Use a L-etter key to make your choice" );
    center_message( 5, "------------- LESSON MENU -------------" );
    _settextposition(7,20);
    _outtext( "T-he Cost of Generation" );
    _settextposition(8,20);
    _outtext( "G-eneral Requirements for Finding Minimum Cost" );
    _settextposition(9,20);
    _outtext( "F-itting the Cost to an Equation" );
    _settextposition(10,20);
    _outtext( "M-inimum Cost Concept with two Generators" );
    _settextposition(11,20);
    _outtext( "D-ispatching Several Generators" );
    _settextposition(12,20);
    _outtext( "A-ccounting for System Losses" );
    _settextposition(13,20);
    _outtext( "I-terating to a Solution" );
    _settextposition(14,20);
    _outtext( "S-imple Solution with Text Report" );
    _settextposition(15,20);
    sprintf(buffer, "R-ead Data from a File.        [%s]",genfile_buff);
    _outtext( buffer );
    _settextposition(16,20);
    _outtext( "V-alid listing of files" );
    _settextposition(17,20);
    _outtext( "E-dit or Create New Data" );
    _settextposition(18,20);
    _outtext( "W-rite Data to a File" );
    _settextposition(19,20);
    _outtext( "e-X-it This Program" );
    _settextposition(20,20);
    sprintf(buffer, "L-earn messages displayed?     %s",
				teach_mode == 'T' ? "[YES]" : "[NO]");
    _outtext( buffer );
    _settextposition(21,20);
    sprintf(buffer, "Z show intermediate steps      %s",
		  print_all == 'Y' ? "[YES]" : "[NO]");
    _outtext( buffer );

    /* PRINT CURRENT MESSAGE ON INPUT LINE & ASK USER TO SELECT A FUNCTION */
    c = prompt( INROW, -1, prompt_color, mp );

    switch (toupper(c))
		{

		/* THE COST OF GENERATION */
		case 'T':
			if( show_message( "THECOSTOF1" ) == 'F') return ( 'F' );
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "THECOSTOF2" ) == 'F') return ( 'F' );
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "THECOSTOF3" ) == 'F') return ( 'F' );
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			strcpy( mp,
			  "The Cost of Generation Finished normally.");
			goto START_MENU;

		/* FITTING THE COST TO AN EQUATION */
		case 'F':
			if( show_message( "GENPOLY1" ) == 'F') goto START_MENU;
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( input_gen_poly( 0 ) == 'T' )
				strcpy( mp, "Cost Fitting Finished Normally");

			goto START_MENU;

		/* MINIMUM COST CONCEPT */
		case 'M':
			c = teach_equal_lambda ();
			strcpy( mp, "All finished with learning about minimum cost");
			goto START_MENU;

		/* GENERAL REQUIREMENTS  */
		case 'G':

			if( show_message( "GENERAL1" ) == 'F') goto START_MENU;
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "GENERAL2" ) == 'F') goto START_MENU;
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "GENERAL3" ) == 'F') goto START_MENU;
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );

			strcpy( mp,
			  "\aGeneral Requirements is temporarily out of service");
			goto START_MENU;

		/* ACCOUNTING FOR SYSTEM LOSSES */
		case 'A':
			if( show_message( "LOSSES1" ) == 'F') goto START_MENU;
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( valid_data == 'T' )
				{
				show_files( "*.LOS" );
				get_loss_data();
				}
			if( (line_data=='N') || (bus_data=='N') || (a_data=='N') )
				{
				goto START_MENU;
				}
			c = update_loss_penalty();
			if( c == 'T')
				{
				strcpy( mp, "New loss factors have been calculated");
				up_to_date = 'F';
				goto START_MENU;
				}
			goto START_MENU;

		/* DISPATCHING SEVERAL GENERATORS */
		case 'D':

			/* LOAD UP A DATA SET IF NO VALID DATA IS LOADED */
			_clearscreen( _GCLEARSCREEN );
			if( show_message( "MAKEAGUESS" ) == 'F') goto START_MENU;
			c = prompt( 25, -1, prompt_color,
					"Would you like me to load suggested data Y/N?" );

			if( toupper( c ) == 'Y' )
				{
				strcpy( genfile_buff, "TEN.GEN");
				c = get_gen_data();
				}
			_clearscreen( _GCLEARSCREEN );
			if (valid_data != 'T')
				{
				strcpy( mp, "\aNo Valid Data Loaded at This Time");
				goto START_MENU;
				}
			c = prompt( 25, -1, prompt_color,
					"Press any key to set up your first guess" );
			c = guess_a_lambda();
			strcpy( mp,
			  "Dispatching Several Generators Finished Normally");
			goto START_MENU;

		/* ITERATING TO A SOLUTION */
		case 'I':
			if (valid_data != 'T')
				{
				strcpy( mp, "\aNo Valid Data Loaded at This Time");
				goto START_MENU;
				}

			/* DISPLAY MESSAGE & QUIT IF MESSAGE NOT FOUND */
			_setbkcolor( _RED );
			_settextcolor( _WHITE );
			_clearscreen( _GCLEARSCREEN );
			if( show_message( "BINARY1" ) == 'F')
				{
				goto START_MENU;
				}
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "BINARY2" ) == 'F')
				{
				goto START_MENU;
				}
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "BINARY3" ) == 'F')
				{
				goto START_MENU;
				}
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );
			if( show_message( "BINARY4" ) == 'F')
				{
				goto START_MENU;
				}
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );

			/* MAKE SURE WE ARE UP TO DATE */
			calc_dfdp_constants();
			flag = solve_a_case();
			if( binary_solution() == 'T')
				{
				plot_iterations( lam_guess, num_guesses );
				strcpy( mp, "Binary solution finished normally.");
				}

		    /* NOW SHOW A SOLUTION USING THE SLOPE OF LAMBDA & THE ERROR */
			_setbkcolor( _RED );
			_settextcolor( _WHITE );
			_clearscreen( _GCLEARSCREEN );
			if( show_message( "BINARY5" ) == 'F')
				{
				goto START_MENU;
				}
			if( teach_mode == 'T') any_key( 25, -1, prompt_color );

			c = slope_solution( .6 );
				{
				plot_iterations( lam_guess, num_guesses );
				strcpy( mp, "Slope solution finished normally.");
				}
			goto START_MENU;

		case 'E':

			valid_data = input_data();
			up_to_date = 'F';
			if( valid_data == 'T' ) strcpy( mp, "Input Data Complete");
			if( valid_data == 'F' ) strcpy( mp, "Input Data Has Errors");
			goto START_MENU;

		/* READ DATA FROM A FILE */
		case 'R':

			/* REQUEST USER TO INPUT GENERATOR FILE NAME  */
			_clearscreen( _GCLEARSCREEN );
			show_files( "*.GEN" );;
			_settextposition(24, 10);
			fflush( stdin );
			sprintf( buffer, "Enter of generator data file name: " );
			_outtext( buffer );
			_displaycursor( _GCURSORON );
			gets( genfile_buff );
			_displaycursor( _GCURSOROFF );

/* consider passing the file name to this someday                          */
			c = get_gen_data();
			if( c == 'T')
				{
				valid_data = 'T';
				up_to_date = 'T';
				file_open = 'T';
				strcpy( mp, "File loaded");
				}
			if( c == 'F')
				{
				valid_data = 'F';
				/* get_gen_data SETS THE ERROR MESSAGE */
				}
			goto START_MENU;

		/* TOGGLE THE LEARNING SCREENS */
		case 'L':
			if ( teach_mode == 'F')
				{
				teach_mode = 'T';
				strcpy( mp, "Learn message screens WILL be shown.");
				}
			else
				{
				teach_mode = 'F';
				strcpy( mp, "Learn message screens have been DISABLED.");
				}
			goto START_MENU;

		/* SOLVE A CASE */
		case 'S':
			if (valid_data == 'T')
				{

				/* ALLOW MULTIPLE CASES TO BE RUN 'esc' KEY EXITS */

				SOLVE_ANOTHER:

				_setbkcolor( _RED );
				_settextcolor( _WHITE );
				flag = solve_a_case();
				if( flag == 'T') flag = binary_solution();
				if( flag == 'T') print_solution();
				if( flag == 'F') goto START_MENU;
				if( flag == 'N') goto START_MENU;

				/* WAIT FOR KEY HIT TO DO ANOTHER CASE */

				_settextposition(INROW,INCOL);
				center_message( INROW, "Would you like to see a graph of"
					" this solution (Y/N)?");
				if( toupper(getch()) == 'Y')
					{
					_setbkcolor( _BLACK );
					_clearscreen( _GCLEARSCREEN );
					display_unit_incrementals( lambda, 'N' );
					}
				_settextposition(INROW,INCOL);
				center_message(INROW,"Press any key for another solution"
							 " - ESC key exits." );
				if( getch() == KEY_ESCAPE )
					{
					strcpy( mp, "Solution routine ended normally");
					goto START_MENU;
					}
				goto SOLVE_ANOTHER;
				}
			else
				{
				strcpy( mp, "\aNo Valid Data Loaded at This Time");
				}
			goto START_MENU;

		/* UPDATE LOSS FACTORS */
		case 'U':
			if( (line_data=='N') || (bus_data=='N') || (a_data=='N') )
				{
				strcpy( mp, "\aNot enough data to update loss factors");
				goto START_MENU;
				}
			c = update_loss_penalty();
			if( c == 'T')
				{
				strcpy( mp, "New loss factors have been calculated");
				up_to_date = 'F';
				goto START_MENU;
				}
			goto START_MENU;

		case 'V':
			show_files( "*.GEN" );;
			c = any_key( 25, -1, prompt_color );
			goto START_MENU;

		/* DISPLAY FILE LIST AND REQUEST WRITE DATA FILE NAME */
		case 'W':

			show_files( "*.GEN" );;
			_settextposition( 24, 10 );
			c = save_data();
			if( c == 'S')
				{
				strcpy( mp, "Save to file was successful");
				up_to_date = 'T';
				file_open = 'F';
				}
			 else
				{
				strcpy( mp, "Save to file FAILED - Try a different name");
				up_to_date = 'F';
				}
			goto START_MENU;

		/* EXIT TO DOS */
		case 'X':

			/* MAKE SURE DATA FILE IS UP TO DATE BEFORE QUITTING */
			if( up_to_date == 'T' )
				{
				break;
				}

			/* DATA IS NOT UP TO DATE - WHAT DO I DO? */
			else
				{
				 _clearscreen( _GCLEARSCREEN );
				 _settextposition( INROW, INCOL);
				 printf("\aCurrent Data is not saved on disk - ");
				 printf("Quit anyway Y?:");
				 c = getch();
				 if( (c == 'y') || (c == 'Y') )
					{
					break;
					}
				strcpy( mp, "\aActive Data is not saved on disk.");
				goto START_MENU;
				}


		/* TOGGLE SEND INTERMEDIATE OUTPUT TO SCREEN */
		case 'Z':
			if( print_all == 'N')
				{
				print_all = 'Y';
				strcpy( mp,
				  "Hit CTRL-PRTSCR to capture intermediate steps");
				}
			else
				{
				print_all = 'N';
				strcpy( mp, "Intermediate steps will be suppressed");
				}
			goto START_MENU;


		/* DEFAULT */
		default:
			strcpy( mp, "\aInvalid Key");
			goto START_MENU;

		}

    /* Restore original foreground and background. */
    _settextcolor( oldfgd );
    _setvideomode( _DEFAULTMODE );
    _setbkcolor( oldbgd );
    _clearscreen( _GCLEARSCREEN );
    _settextposition( oldpos.row, oldpos.col );
    _displaycursor( _GCURSORON );
    exit(0);
}


/* -------------------------- GET GEN DATA  ------------------------------ */
/*  ALLOW USER TO REQUEST DATA FROM A FORMATTED FILE                       */
/*  CHECK FOR VALID FILE AND VALID DATA - CALCULATE Y MATRIX               */
/*                                                                         */
/*  FOUR DATA BLOCKS ARE READ.  1) GENERATOR COST AND LIMITS               */
/*                              2) LINE IMPEDANCE AND CONNECTIVITY         */
/*                              3) BUS VOLTAGES                            */
/*                              4) LOSS COEFFECIENTS FOR EACH BUS          */
/*                                                                         */
/*  FILE STRUCTURE ASSUMES ALL GENERATORS ARE AVAILABLE FOR DISPATCH       */
/*                                                                         */
/*  RETURNS: T-rue                                                         */
/*           F-alse                                                        */
/*   ALSO SETS VALID DATA FLAG ACCORDINGLY                                 */
/* ----------------------------------------------------------------------- */

char get_gen_data(void)
{

    int   j, k, i, n;              /* Y(j,k)  LINE(i)      GEN-n           */
    valid_data = 'T';              /* ASSUME DATA IS GOOD-WE'LL FIND ERRORS */

    if( (genfile = fopen( genfile_buff, "rt" )) == NULL )
	   {

	    /* File open failed */
	   strcpy( mp, "\aUnable to open data file!");
	   return( 'F' );
	   }

    /* File opened successfully - read data */

    fscanf( genfile, "%i\n", &num_gen);

    if( print_all == 'Y' )
	 {
	 printf( "\n%i\n", num_gen);
	 }


    if( num_gen > MAXGEN)
	   {
	   strcpy( mp,"\aToo many Generators.  The limit is MAXGEN");
	   return( 'F' );
	   }

    for( i = 0; i < num_gen; i++)
	  {
	  fscanf( genfile, "%f%f%f%f%f%f%f%f%f\n",
		&gen_name[i],
		&min_out[i],
		&max_out[i],
		&fuel_cost[i],
		&h_3[i],
		&h_2[i],
		&h_1[i],
		&h_0[i],
		&loss_dpdl[i]);


	  /* OPTIONAL DUMP OF DATA BACK OUT */

	  if( print_all == 'Y' )
		{
		printf( "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n",
			gen_name[i],
			min_out[i],
			max_out[i],
			fuel_cost[i],
			h_3[i],
			h_2[i],
			h_1[i],
			h_0[i],
			loss_dpdl[i]);
		}

	  /* BASIC DATA ERROR CHECKING HERE */

	  if( check_gen_data( i ) == 'F' ) return( 'F' );

	  /* SET REGULATING FLAGS TO 'R' ------------------------------------ */

	  gen_status[i] = 'R';

	  }
    return( 'T' );
}




/* -------------------------- INPUT DATA ---------------------------------- */
/*  ALLOW USER TO LOAD DATA FROM SCRATCH OR TO EDIT A FILE                  */
/*  VALIDATE DATA AS IT IS BEING LOADED                                     */
/*  RETURNS T-rue  - This function worked                                   */
/*          F-alse - This function failed                                   */
/*  ALSO SETS EXTERNAL VALID DATA FLAG                                      */
/* ------------------------------------------------------------------------ */

char input_data(void)
{
	char  c;                           /* CHARACTER                       */
	char  line[81];                    /* LINE OF TEXT                    */
	int   i;                           /* COUNTER                         */
	int   count;                       /* GRAPHICS POSITION COUNTER       */
	int   start_pos;                   /* STARTING POSITION OF EDIT       */
	int   end_pos;                     /* POSITION WHERE EDIT ENDED       */
	unsigned  ret_key;                 /* KEY RETURNED FROM EDITLINE      */
	char  flag;                        /* T-rue F-alse FLAG               */

	/* SET THE MAXIMUM TO MAXGEN IF WE DON'T HAVE VALID DATA */
	if( valid_data == 'F' ) num_gen = MAXGEN;

	_clearscreen( _GCLEARSCREEN );

	flag = 'F';

	/* LOOP THROUGH THIS GENERATOR UNTIL WE GET GOOD DATA                */

	_settextposition( 1, 1);
	_outtext( "Input data until complete.  No more than 10 generators"
			" allowed");
	_settextposition( 2, 1);
	_outtext( "Enter '999' in Gen Name to signal end of data");
	_settextposition( 4, 1);
	_outtext( " NUM  MINOUT MAXOUT  FUEL_C      P^3      P^2      P"
			"   NL COST   DP/DL");

	/* PRINT OUT THE DATA POINTS FOR EDITING                              */
	for( i = 0; i < MAXGEN; i++)
		{
		gen_status[i] = 'R';

		/* SET NUMGEN TO 999 */
		if( i == num_gen ) gen_name[i] = 999.;
		_settextposition( 6 + i, 0 );
		sprintf( line, "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n",
			gen_name[i],
			min_out[i],
			max_out[i],
			fuel_cost[i],
			h_3[i],
			h_2[i],
			h_1[i],
			h_0[i],
			loss_dpdl[i]);
		_outtext( line );
		}

	/* LOOP THROUGH ADDING DATA POINTS UNTIL FINISHED                     */
	i = 0;
	ret_key = 0;
	start_pos = 0;

	while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) )
		{
		_settextposition( 6 + i, 0 );
		sprintf( line, "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n",
			gen_name[i],
			min_out[i],
			max_out[i],
			fuel_cost[i],
			h_3[i],
			h_2[i],
			h_1[i],
			h_0[i],
			loss_dpdl[i]);
		_displaycursor( _GCURSORON );
		ret_key = editline( line, start_pos, &end_pos );
		_displaycursor( _GCURSOROFF );
		start_pos = end_pos;
		sscanf( line, "%f%f%f%f%f%f%f%f%f",
			&gen_name[i],
			&min_out[i],
			&max_out[i],
			&fuel_cost[i],
			&h_3[i],
			&h_2[i],
			&h_1[i],
			&h_0[i],
		    &loss_dpdl[i]);

		/* SEE IF WE ARE DONE */
		if( gen_name[i] == 999 )
			{
			num_gen = i;
			return( flag );
			}

		/* PRINT THE CURRENT UNIT NUMBER THAT WAS CHECKED */
		sprintf( buffer, "Checking Generator%5.1f", gen_name[i] );
		center_message( 24, buffer );

		/* CHECK FOR ERRORS -PRINT MESSAGE FOR ERRORS */
		if( check_gen_data( i ) == 'T' )
			{
			center_message( 25, "Unit Data is OK" );
			flag = 'T';

			/* MOVE AROUND DEPENDING ON KEY PRESS */
			switch( ret_key )
				{
				case KEY_UP:
					if( i > 0 ) i--;
					break;

				case KEY_DOWN:
					if( i < MAXGEN - 1 ) i++;
					break;

				default:
					break;
				}
			}
		else
			{
			/* ISSUE BAD DATA MESSAGE AND STAY ON THAT LINE */
			center_message( 25, mp );
			flag = 'F';
			}

		}
	return( flag );
}


/* -------------------------- SAVE DATA ---------------------------------- */
/*  ALLOW USER TO SAVE DATA TO A FILE                                      */
/*                                                                         */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char save_data(void)
{
	FILE *genfile;

	int   i;                       /* COUNTER                              */
	char flag = 'F';               /* FLAG FOR SUCCESSUL SAVE              */

	/* REQUEST USER TO INPUT GENERATOR FILE NAME  */

	fflush( stdin );
	sprintf( buffer, "Save generator data to file named: " );
	_outtext( buffer );
	_displaycursor( _GCURSORON );
	gets( genfile_buff );
	_displaycursor( _GCURSOROFF );
	if( (genfile = fopen( genfile_buff, "wt" )) == NULL )
		{
		/* FILE OPEN FAILED - EXIT */
		fclose( genfile );
		return( 'F');
		}

	/* File opened successfully - save data */
	fprintf(genfile, "%4i\n", num_gen);
	for( i = 0; i < num_gen; i++)
	  {
	  fprintf( genfile, "%5.1f%7.1f%7.1f%8.4f%11.6f%8.4f%8.3f%8.2f%8.4f\n",
		gen_name[i],
		min_out[i],
		max_out[i],
		fuel_cost[i],
		h_3[i],
		h_2[i],
		h_1[i],
		h_0[i],
		loss_dpdl[i]);
	   }
	fclose( genfile );
	return( 'S' );

}

/* ---------------------------- SHOW_DATA  ------------------------------- */
/*  GIVEN VALID DATA, ECHO THE GENERATOR CAPABILITIES AND COST FACTORS     */
/*  BACK TO THE USER.  CALCULATE MINIMUM AND MAXIMUM FEASIBLE LOADS.       */
/*  REQUIRES KNOWLEDGE OF useloss VALUE TO USE LOSSES OR NOT               */
/* ----------------------------------------------------------------------- */

void show_data(void)
{
    char  c;
    char  flag;                    /* ERROR FLAG                           */
    int   i, n;                    /* COUNTER                              */

	/* RESET THE FEASABILITY COUNTERS EACH TIME WE ENTER THE SOLUTION      */
	/* OTHERWISE WE WILL MISCALCULATE FEASABLE LIMITS                      */

	maximum_feasable = 0;
	minimum_feasable = 0;

	/* ECHO THE DATA BACK OUT AND ASK FOR LOAD TO MEET */

	_clearscreen( _GCLEARSCREEN );
	_settextposition(OUTROW, OUTCOL);
	sprintf(buffer,"%i Generators, %i busses, %i lines  in Data file: %s\n",
		num_gen,
		num_bus,
		num_line,
		genfile_buff);
	_outtext( buffer );
	sprintf( buffer, "Losses included ? %s\n",
				 useloss == 'Y' ? "[YES]" : "[NO]");
	_settextposition( OUTROW + 1, OUTCOL );
	_outtext( buffer );
	_settextposition( 4, 1);
	_outtext( " NUM  MINOUT MAXOUT  FUEL_C      P^3      P^2      P"
			"   NL COST   DP/DL");

	/* PRINT OUT THE DATA POINTS FOR EDITING                              */
	for( i = 0; i < num_gen; i++)
		{
		/* CALCULATE FEASABLE LOADS WHILE ECHOING DATA */

		/* Dispatchable units have the full range of output capability */
		if( (gen_status[i] == 'R') ||
			(gen_status[i] == 'H') ||
			(gen_status[i] == 'L')    )
			{
			maximum_feasable = maximum_feasable + max_out[i];
			minimum_feasable = minimum_feasable + min_out[i];

			/* RESET FLAGS TO 'R' SINCE WE ARE STARTING A NEW CASE */
			/* NOTE: THIS ONLY RESETS DISPATCHABLE GENERATOR FLAGS */

			gen_status[i] = 'R';

			sprintf( buffer, "%5.1f%7.1f%7.1f%8.4f%11.6f"
						  "%8.4f%8.3f%8.2f%8.4f\n",
				gen_name[i],
				min_out[i],
				max_out[i],
				fuel_cost[i],
				h_3[i],
				h_2[i],
				h_1[i],
				h_0[i],
				loss_dpdl[i]);
			}

		/* Fixed output units have only one level of output */
		if( gen_status[i] == 'F' )
			{
			maximum_feasable = maximum_feasable + gen_load[i];
			minimum_feasable = minimum_feasable + gen_load[i];
			sprintf( buffer, "%5.1f%7.1f%7.1f This unit output is held!",
				gen_name[i],
				gen_load[i],
				gen_load[i]);
			}

		/* Check Units that are off are off so say so */
		if( gen_status[i] == 'O' )
			{
			sprintf( buffer, "%5.1f              "
						  "This unit is scheduled off!",
				gen_name[i]);
			}
		_settextposition( 6 + i, 0 );
		_outtext( buffer );
		}

    /* PRINT OUT FEASABLE LIMITS FOR USER AND REQUEST INPUT DATA           */
    sprintf( buffer, "Minimum feasable = %8.1f MW   "
				 "Maximum feasable = %8.1f MW",
		minimum_feasable,
		maximum_feasable);
    _settextposition(INROW - 3, INCOL);
    _outtext( buffer );

}

/* ---------------------------- SOLVE_A_CASE ----------------------------- */
/*                                                                         */
/*  GIVEN VALID DATA, ECHO THE GENERATOR CAPABILITIES AND COST FACTORS     */
/*  BACK TO THE USER AND REQUEST A LOAD LEVEL TO DISPATCH.  CHECK          */
/*  FEASABILITY.  RETURNS 'T' IF FEASIBLE                                  */
/*                                                                         */
/*  REQUIRES KNOWLEDGE OF useloss VALUE TO USE LOSSES OR NOT               */
/* ----------------------------------------------------------------------- */

char solve_a_case(void)

{
	int   i, n;                        /* COUNTER                         */
	char  c;
	char  flag;                        /* ERROR FLAG                      */
	char  line[8];                     /* INPUT LINE STRING               */
	unsigned  ret_key;                 /* KEY RETURNED FROM EDITLINE      */
	int   start_pos;                   /* STARTING POSITION OF EDIT       */
	int   end_pos;                     /* POSITION WHERE EDIT ENDED       */

	/* UPDATE DISPATCH CONSTANTS */
	calc_dfdp_constants();

	/* ECHO THE DATA BACK OUT AND ASK FOR LOAD TO MEET */
	show_data();

	_settextposition( INROW, INCOL );
	_outtext( "Enter a value for the system load: " );

	/* SET UP THE INPUT LINE AND EDIT THE LINE */
	while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) )
		{
		_settextposition( INROW, INCOL + 34 );
		strcpy( line, "   000.0 " );
		ret_key = 0;
		_displaycursor( _GCURSORON );
		ret_key = editline( line, 2, &end_pos );
		_displaycursor( _GCURSOROFF );
		}

	/* LEAVE IF THE USER TRIED TO ESCAPE */
	if( ret_key == KEY_ESCAPE )
		{
		strcpy( mp, "Solution terminated by user.");
		return( 'N' );
		}

	/* READ THE INPUT LINE  */
	sscanf( line, "%f", &system_load);

	/* FEASABILITY CHECK - STOP PROCESSING IF NOT FEASABLE */
	if((system_load > maximum_feasable) || (system_load < minimum_feasable))
		{
		 sprintf( mp,
				"NOT FEASABLE - %7.1f MW Load is outside system limits.",
				system_load);
		return( 'N' );
		}
	/* PREVENT PROBLEMS FROM ZERO LOAD */
	if( system_load == 0 )
		{
		strcpy( mp, "\aZERO LOAD -- BLACKOUT IS AT HAND!");
		return( 'N' );
		}

	/* DATA IS OK.  CALL THE DISPATCH */
	return('T');
}

/* ------------------------ CHECK_GEN_DATA ------------------------------- */
/*                                                                         */
/*  CHECK TO SEE THAT THE DATA FOR THIS GENERATOR IS GOOD                  */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char check_gen_data( int unitno )
{
	float     dlam_dp_min;        /* SLOPE OF LAMBDA AT Pmin              */
	float     dlam_dp_max;        /* SLOPE OF LAMBDA AT Pmax              */
	float     dlam_zero;           /* POINT WHERE dLAMBDA IS AERO          */

	/* MAKE SURE MAXIMUM OUTPUT IS GREATER THAN THE MINIMUM & MIN > ZERO  */
	if( (max_out[unitno] <= 0)
		|| (min_out[unitno] <= 0)
		|| (min_out[unitno] > max_out[unitno]))
		{
		strcpy( mp, "\aInvalid generator output limits");
		return( 'F' );
		}

	/* MAKE SURE THE FUEL COST IS POSITIVE                                */
	if( fuel_cost[unitno] < 0 )
		{
		strcpy( mp, "\aFuel cost must be greater than zero");
		return( 'F');
		}

	/* CALCULATE THE SLOPE OF THE INCREMENTAL COST AT MIN & MAX OUTPUT    */
	dlam_dp_min = 6 * h_3[unitno] * min_out[unitno] + 2 * h_2[unitno];
	dlam_dp_max = 6 * h_3[unitno] * max_out[unitno] + 2 * h_2[unitno];

	/* OPTIONAL PRINTOUT */
	if( print_all == 'Y' )
		printf( "unit number = %3i slope at min=%9.5f at max=%9.5f\n",
			unitno,
			dlam_dp_min,
			dlam_dp_max);

	/* CHECK THE COEFFECIENTS FOR POSITIVE SLOPE AT THE MIN & MAX LIMITS  */
	if( (dlam_dp_min <= 0) || (dlam_dp_max <= 0) )
		{
		strcpy( mp,
			"\aNegative or zero lambda at the generator output limits.");
		return( 'F' );
		}

	/* FIND WHERE SLOPE OF LAMBDA = 0; MUST BE OUTSIDE Pmin Pmax          */

	if( h_3[unitno] != 0 )
		{
		dlam_zero = - h_2[unitno] / 3 / h_3[unitno];

		/* OPTIONAL PRINTOUT */
		if( print_all == 'Y' )
			printf(" zero lambda at %9.2f MW\n", dlam_zero);

		if( (dlam_zero > min_out[unitno]) && (dlam_zero < max_out[unitno]))
			{
			strcpy( mp,
				"\aLambda is Zero within the generator output limits");
			return( 'F' );
			}
		}

	/* WE MADE IT */
	return( 'T' );
}





/* ------------------------CALC DF/DP CONSTANTS---------------------------- */
/*                                                                          */
/* TAKE THE FIRST DERIVATIVE OF THE COST FUNCTION AND CALCULATE THE         */
/* RESULTING QUADRATIC CONSTANTS TO DEVELOP AN EQUATION FOR GENEROTOR       */
/* OUTPUT AS A FUNCTION OF LAMBDA, THE INCREMENTAL COST.                    */
/*                                                                          */
/* ALSO CALCULATES MINIMUM AND MAXIMUM LAMBDAS OF THE GENERATORS ON LINE    */
/*                                                                          */
/* ------------------------------------------------------------------------ */

void calc_dfdp_constants (void)

{
    int   i;                  /* COUNTER                                    */
    float loss, min, max;

    /* CALCULATE THE FIRST GENERATOR SO WE HAVE SOME INITIAL CONDITIONS    */
    /* FOR MINIMUM & MAXIMUM VALUES OF LAMBDA BEFORE CALCULATING THE REST  */
   if(useloss == 'N') loss = 0;

    /* CALCULATE THE CONSTANT AND Pi VALUES FOR THE  GENERATORS            */
    for( i = 0; i < num_gen; i++)
	  {
	  loss = loss_dpdl[i];

	  /* CALCULATION DEPENDS ON IF WE WANT TO USE LOSSES OR NOT           */
	  if(useloss == 'N') loss = 0;
	  dfdp_0[i] = h_1[i] * fuel_cost[i] * (1/(1-loss));
	  dfdp_1[i] = 2 * h_2[i] * fuel_cost[i] * (1/(1-loss));
	  dfdp_2[i] = 3 * h_3[i] * fuel_cost[i] * (1/(1-loss));

	  /* CALCULATE MAX AND MIN POSSIBLE LAMBDA                            */
	  gen_min_lambda[i] = dfdp_2[i] * min_out[i] * min_out[i] +
					  dfdp_1[i] * min_out[i] +
					  dfdp_0[i];
	  gen_max_lambda[i] = dfdp_2[i] * max_out[i] * max_out[i] +
					  dfdp_1[i] * max_out[i] +
					  dfdp_0[i];

	  /* optional printout */

	  if( print_all == 'Y')
		{
		printf( "\nMax=%6.2f Min=%6.2f d0=%10.5f d1=%10.5f d2=%10.5f",
			gen_max_lambda[i],
			gen_min_lambda[i],
			dfdp_0[i],
			dfdp_1[i],
			dfdp_2[i]);
		}

	  }

    /* FIND THE MINIMUM AND MAXIMUMS FOR LAMBDA                         */
    min_lambda = gen_min_lambda[0];
    max_lambda = gen_max_lambda[0];
    for( i = 1; i < num_gen; i++)
	  {
	  if( gen_max_lambda[i] > max_lambda ) max_lambda = gen_max_lambda[i];
	  if( gen_min_lambda[i] < min_lambda ) min_lambda = gen_min_lambda[i];
	  }
}

/* ------------------------- CALC_OUTPUT --------------------------------- */
/*                                                                         */
/* CALCULATE THE GENERATOR OUTPUT GIVEN AN ESTIMATE FOR THE SYSTEM         */
/* LAMBDA.  THE RESPONSIBILITY OF THIS ROUTINE IS TO CALCULATE:            */
/*          gen_load[i]                                                    */
/*                                                                         */
/* IT COULD ALSO CALCULATE THE GENERATOR LAMBDA gen_lambda[] but THAT      */
/* WOULD SLOW THE ROUTINE DOWN TOO MUCH.                                   */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char calc_output (void)
{
	int       i;                  /* COUNTERS                             */
	int       n;

	/* SET NET OUTPUT TO ZERO                                             */
	net_out = 0;

	/* LOOP THROUGH ALL OF THE THE GENERATORS                             */
	for( i = 0; i < num_gen; i++ )
		{
		while( (gen_status[i] != 'O') && (gen_status[i] != 'F') )
			{

			/* SET ALL DISPATCHABLE GENERATORS TO REGULATING            */
			gen_status[i] = 'R';

			/* LOCK GENERATOR LOW IF LAMBDA IS < MINIMUM                */
			if( lambda < gen_min_lambda[i] )
				{
				gen_load[i] = min_out[i];
				gen_status[i] = 'L';
				break;
				}

			/* LOCK GENERATOR HIGH IF LAMBDA IS > MAXIMUM               */
			if( lambda > gen_max_lambda[i] )
				{
				gen_load[i] = max_out[i];
				gen_status[i] = 'H';
				break;
				}


			/* CALCULATE THE LOAD - USE A DIFFERENT EQUATION DEPENDING  */
			/* ON THE STRUCTURE OF THE COST FUNCTION                    */
			if( (dfdp_2[i] != 0) && (gen_status[i] == 'R'))
				{

				/* DF/DP IS QUADRATIC & WE KNOW WHICH ROOT IS CORRECT  */
				gen_load[i] = ( - dfdp_1[i]
							 + sqrt( dfdp_1[i] * dfdp_1[i]
								   - 4
								   * dfdp_2[i]
								   * ( dfdp_0[i] - lambda )
								   )
							)
							/ 2
							/ dfdp_2[i];
				break;
				}
			else
				{

				/* DF/DP IS LINEAR & dfdp_1[] CAN'T BE ZERO            */
				if( gen_status[i] == 'R')
					gen_load[i] = ( lambda - dfdp_0[i] ) / dfdp_1[i];
				break;
				}

			}

		/* ADD UP NET OUTPUT FOR GENERATORS ON LINE                      */
		if( gen_status[i] != 'O' )
			net_out = net_out + gen_load[i];
	}

	/* optional printout */
	if( print_all == 'Y')
	   {
	   printf( "\nIL=%6.3f%5.0f", lambda, net_out);
	   for( n = 0; n < num_gen; n++)
		 {
		 printf( "%5.0f%c", gen_load[n], gen_status[n]);
		 }
	   }
}


/* ------------------------- BINARY SOLUTION ----------------------------- */
/* THIS METHOD STARTS MIDWAY BETWEEN THE POSSIBLE RANGE OF LAMBDAS FOR     */
/* THE CASE.  EACH ITERATION MOVES HALF THE DISTANCE OF THE LAST ONE.      */
/* ----------------------------------------------------------------------- */

char binary_solution(void)
{
	char      solved;             /* SOLUTION FLAG                        */
	int       i;                  /* COUNTER                              */
	float     del_lambda;         /* LAMBDA INCREMENT                     */
	float     tolerance;          /* SOLUTION TOLERANCE                   */
	float     mw_error;           /* MW ERROR                             */

	/* START WITH LAMBDA AT THE MIDDLE  */
	lambda = (max_lambda + min_lambda) / 2;

	/* START THE INCREMENT AT 1/4 THE RANGE OF LAMBDAS                    */
	del_lambda = (max_lambda - min_lambda) / 4;

	/* SET UP ITERATION VARIABLES */
	solved = 'F';
	tolerance = TOL;    /* TOL is defined by a compiler directive */
	i = 0;
	while( (i < MAXITERATIONS) && (solved == 'F') )
		{
		/* CALCULATE THE OUTPUT FOR THIS GUESS */
		calc_output();

		/* LOG THE QUANTITIES FOR PLOTTING */
		mw_error = net_out - system_load;
		lam_guess[i] = lambda;
		num_guesses = i + 1;

		if( print_all == 'Y' ) printf( "\n MW Error = %12.6f", mw_error);

		/* SEE IF WE MET THE SYSTEM LOAD */
		if( fabs(mw_error) < tolerance)
			{

			/* WE FOUND A SOLUTION */
			solved = 'T';
			}
		else
			{

			/* NO GOOD - TRY AGAIN */
			if( mw_error < 0 )
				{
				/* RATCHET UP ONE INCREMENT IF WE ARE SHORT */
				lambda = lambda + del_lambda;
				}
			if( mw_error > 0 )
				{
				/* RATCHET DOWN ONE INCREMENT IF WE HAVE EXCESS */
				lambda = lambda - del_lambda;
				}

			/* TRUE BINARY - HALVE THE INCREMENT FOR THE NEXT ITERATION */
			del_lambda = del_lambda / 2;
			i++;
			}
		}

	return( solved );
}


/* -------------------------- SLOPE_SOLUTION ----------------------------- */
/*                                                                         */
/* THIS METHOD STARTS AT THE MIDDLE AND WORKS TILL IT MAKES IT             */
/* IT IS BASICALLY BARE TO SHOW FAST SOLUTIONS Returns T-rue F-ailed       */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char slope_solution( float acceleration ) /* Iteration Acceleration Factor */
{
	char      c;                  /* CHARACTER                            */
	char      solved;             /* SOLUTION FLAG                        */
	int       i;                  /* COUNTER                              */
	float     lambda_slope;       /* SLOPE OF LAMBDA IN LAMBDA PER MW     */
	float     prev_lambda;        /* PREVIOUS VALUE OF LAMBDA             */
	float     prev_load;          /* PREVIOUS LOAD LEVEL                  */
	float     tolerance;          /* SOLUTION TOLERANCE                   */
	float     mw_error;           /* MW ERROR                             */

	/* CALCULATE THE SLOPE OF LAMBDA PER MW                   */
	lambda_slope = (max_lambda - min_lambda) /
				(maximum_feasable - minimum_feasable);

	/* ASSUME THE FIRST GUESS WAS THE MINIMUM LAMBDA  */
	mw_error = system_load - minimum_feasable;
	prev_load = minimum_feasable;
	prev_lambda = min_lambda;

	/* SET UP ITERATION VARIABLES */
	solved = 'F';
	tolerance = TOL;
	i = 0;
	while( (i < MAXITERATIONS) && (solved == 'F') )
		{

		/* MAKE A GUESS FOR LAMBDA AND CALCULATE THE OUTPUT */
		lambda = lambda + (mw_error * acceleration * lambda_slope);
		calc_output();

		/* LOG THE QUANTITIES FOR PLOTTING (Opposite of Binary ) */
		mw_error =  system_load - net_out;
		lam_guess[i] = lambda;
		num_guesses = i + 1;

		if( print_all == 'Y' ) printf( "\n MW Error = %12.6f", mw_error);

		/* SEE IF WE MET THE SYSTEM LOAD */
		if( fabs(mw_error) < tolerance)
			{

			/* WE FOUND A SOLUTION */
			solved = 'T';
			}
		else
			{

			/* NO GOOD - TRY AGAIN */
			if( net_out == prev_load )
				/* Get out before it blows up */
				return( 'F' );
			lambda_slope = fabs( lambda - prev_lambda)/
						fabs( net_out - prev_load );
			prev_lambda = lambda;
			prev_load = net_out;

			/* CALCULATE A NEW SLOPE FOR LAMBDA */

			i++;
			}
		}

	return( solved );
}

/* ------------------------- CALC ENERGY COST ----------------------------- */
/*                                                                          */
/*  CALCULATE UNIT OUTPUT COST AND ENERGY COST EXCLUDING TRANSMISSION LOSS  */
/*                                                                          */
/*  ADD PENALTY FACTOR FOR TOTAL LOADED COST                                */
/*                                                                          */
/* ------------------------------------------------------------------------ */

void calc_energy_cost (void)
{
    int   i;                       /* COUNTER                               */

    for( i = 0; i < num_gen; i++)
	  {
	  gen_output_cost[i] = fuel_cost[i] *
					   (
					   h_0[i] +
					   h_1[i] * gen_load[i] +
					   h_2[i] * gen_load[i] * gen_load[i] +
					   h_3[i] * gen_load[i] * gen_load[i] * gen_load[i]
					   );

	  if( gen_status[i] == 'O') gen_output_cost[i] = 0;
	  if( useloss == 'Y')
		{
		gen_output_cost[i] = gen_output_cost[i] * 1/(1-loss_dpdl[i]);
		}

	  /* Avoid dividing by zero when gen_load is zero */
	  if( gen_load[i] > 0 )
		{
		gen_energy_cost[i] = gen_output_cost[i] / gen_load[i];
		}
	  else
		{
		gen_energy_cost[i] = 0;
		}
	  }
}

/* ---------------------- PRINT SOLUTION --------------------------------- */
/*                                                                         */
/*  WE ALREADY KNOW THIS IS A GOOD CASE - THIS JUST PRINTS IT.             */
/*                                                                         */
/*  RETURNS: NOTHING                                                       */
/*                                                                         */
/* ----------------------------------------------------------------------- */

void print_solution(void)
{
    int   i;
    int   largest_unit_num = 0;    /* LARGEST UNIT NUMBER                  */
    int   largest_load_num = 0;    /* LARGEST LOADED UNIT NUMBER           */
    float largest_unit = 0;        /* LARGEST UNIT                         */
    float largest_load = 0;        /* LARGEST LOADED UNIT                  */
    float reserves;                /* SPINNING RESERVES                    */
    float system_hourly_cost = 0;  /* TOTAL ENERGY COST OF SYSTEM IN $     */
    float net_output = 0;          /* SYSTEM OUTPUT IN MW                  */
    float system_energy_cost = 0;  /* ENERGY COST IN $ / MWh               */

    /* CLEAR SCREEN AND PRINT HEADER                                       */

    _clearscreen( _GCLEARSCREEN );
    _settextposition(OUTROW,OUTCOL);
    printf( "%s      System Lambda = %8.4f",
		genfile_buff, lambda);
    printf("  %s",
		useloss == 'Y' ? "OUTPUT COSTS ARE PENALIZED" : "No losses case.");
    calc_energy_cost();
    printf( "\n\n GEN STATUS  LOAD  OUTPUT COST ENERGY COST\n" );
    printf( " --- ------  ----- ----------- -----------\n" );

    /* LOOP THROUGH EACH GENERATOR PRINTING DATA                           */

    for( i = 0; i < num_gen; i++)
	  {
	  system_hourly_cost = system_hourly_cost + gen_output_cost[i];
	  net_output = net_output + gen_load[i];
	  printf( "%3.0f       %c %6.1f     %7.1f     %7.3f\n",
		  gen_name[i],
		  gen_status[i],
		  gen_load[i],
		  gen_output_cost[i],
		  gen_energy_cost[i]);

	  /* CHECK FOR LARGEST UNIT AND UNIT WITH THE MOST LOAD               */
	  if( largest_load < gen_load[i] )
		{
		largest_load = gen_load[i];
		largest_load_num = i;
		}
	  if( largest_unit < max_out[i] )
		{
		largest_unit = max_out[i];
		largest_unit_num = i;
		}
	  }

    /* CALCULATE SYSTEM ENERGY COST AND PRINT TOTALS                       */

    system_energy_cost = system_hourly_cost / net_output;
    reserves = maximum_feasable - system_load;

    printf( "           -------    --------     -------" );
    printf( "\nTOTALS %11.1f%12.1f%12.3f",
		  net_output,
		  system_hourly_cost,
		  system_energy_cost);
    _settextposition( INROW - 4, INCOL );
    printf( "%7.1f MW Total Spinning Reserves", reserves);
    printf( "\n%7.1f MW Reserves after losing the largest unit. (%3.0f)",
		reserves - max_out[largest_unit_num],
		gen_name[largest_unit_num]);
    printf("\n%7.1f MW Reserves after loss of largest loaded unit. (%3.0f)",
		reserves - max_out[largest_load_num],
		gen_name[largest_load_num]);
}


/* ----------------------- UPDATE LOSS PENALTY --------------------------- */
/*                                                                         */
/*  UPDATES dPl/dPn FROM LOAD FLOW DATA                                    */
/*                                                                         */
/*  INPUT: REQUIRES GLOBAL VARIABLE loss_dpdl[n] FOR EACH GENERATOR        */
/*                                                                         */
/*  RETURNS: T-rue       ALSO REQUIRES Y BUS DATA, VOLTAGE DATA & A[j][n]  */
/*           F-alse                                                        */
/* ----------------------------------------------------------------------- */

char update_loss_penalty(void)
{
	int       n, j, k;            /* STANDARD POINTERS FOR GEN & BUSSES   */
	char c;                       /* CHARACTER                            */

	/* Calculate dPloss / dTHETA                                          */
	for( j = 0; j < num_bus; j++)
		{
		dpl_dtheta[j] = 0;
		for( k = 0; k < num_bus; k++)
			{
			dpl_dtheta[j] = dpl_dtheta[j]
						+ v[j]
						* v[k]
						* y[j][k].x
						* sin((theta[k] - theta[j])
						/180
						*3.14159);
			}
		}

	/* Calculate dPloss / dPgen load  = loss_dpdl                         */

	for( n = 0; n < num_gen; n++)
		{
		loss_dpdl[n] = 0;
		for( j = 0; j < num_bus; j++)
			{
			loss_dpdl[n] = loss_dpdl[n] + dpl_dtheta[j] * a[j][n];
			}
		/* JUST MAKE SURE loss_dpdl is less than 1                       */
		if( loss_dpdl[n] >= 1 )
			{
			strcpy( mp,
			  "\aMore than one MW of Loss per MW increase on generator.");
			return( 'F' );
			}
		}

	/* WE DID IT!  LETS GET OUT OF HERE                                   */

	return( 'T' );
}


/* ------------------------ plot_gen_poly--------------------------------- */
/*                                                                         */
/*  DEVELOPS THE POLYNOMIAL VALUES FOR AN INDIVIDUAL GENERATOR             */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char plot_gen_poly( float min_load,     /* MINIMUM LOAD FOR THIS UNIT      */
				float max_load,     /* MAXIMUM LOAD FOR THIS UNIT      */
				float *mw_out,      /* HEAT RATE DATA - MW OUTPUT      */
				float *mbtu_in,     /* HEAT RATE DATA - MBTU INPUT     */
				int numobs,         /* NUMBER OF HEAT RATE DATA POINTS */
				float *coef)        /* COEFFECIENTS OF POLYNOMIAL      */
{
	int   order;                       /* THIRD ORDER POLYNOMIAL          */
	int   i;                           /* COUNTER                         */
	float yest[10];                    /* ESTIMATED Y VALUE FROM POLYN    */
	float resid[10];                   /* RESIDUAL VALUE FROM POLYNOMIAL  */
	float see;                         /* STANDARD ERROR OF THE ESTIMATE  */
	float coefsig[4];                  /* STANDARD ERROR OF COEFFECIENT   */
	float rsq;                         /* R SQUARED VALUE                 */
	float r;                           /* CORRELATION COEFFECIENT         */
	char  cferror = 0;                 /* ERROR = 1 IF SINGULAR           */
	char  c;                           /* CHARACTER                       */

	float x[11];                       /* OUTPUT X DATA FOR PLOTS         */
	float y[11];                       /* OUTPUT Y DATA FOR PLOTS         */
	float min_x;                       /* MINIMUM X FOR PLOTTING          */
	float max_x;                       /* MAXIMUM X FOR PLOTTING          */

	/* MAKE SURE WE HAVE AT LEAST TWO DATA POINTS                         */
	if( numobs < 2 )
		{
		strcpy( mp, "\aNot enough data points to compute polynomial!");
		return( 'F' );
		}

	/* NO MORE THAN 8 POINTS ARE ALLOWED AT THIS TIME                     */
	if( numobs > 8 )
		{
		strcpy( mp, "\aToo many data points!");
		return( 'F' );
		}

	/* require the order to be 3 */
	order = 3;

	/* SORT DATA IN ASCENDING ORDER JUST TO BE SURE IT IS OK              */
	SortDataX( mw_out, mbtu_in, numobs, 1);


	/* CHOOSE BETWEEN MW_OUT AND MIN/MAX_LOAD FOR MIN/MAX X               */
	min_x = mw_out[0];
	if( min_load < min_x ) min_x = min_load;
	max_x = mw_out[numobs -1];
	if( max_load > max_x ) max_x = max_load;

	/* CALCULATE THE POLYNOMIAL FOR THIS UNIT                             */
	PolyCurveFit(mw_out, mbtu_in, numobs, order, coef, yest, resid,
			   &see, coefsig, &rsq, &r, &cferror);

	/* CHECK FOR ERRORS FROM POLYCURVEFIT                                 */
	if( cferror == 1 )
		{
		strcpy( mp, "\aSingular Matrix");
		return( 'F' );
		}

	/* CALCULATE ACTUAL NUMBERS FROM POLYNOMIAL                           */
	for( i = 0; i < 11; i++)
		{
		x[i] = min_x + ( i * ( max_x - min_x ) / 10 );
		y[i] = coef[0] +
			  coef[1] * x[i] +
			  coef[2] * x[i] * x[i] +
			  coef[3] * x[i] * x[i] * x[i];
		}

	/* SET UP GRAPHICS                                                    */
	_setbkcolor( _BLACK );
	_clearscreen( _GCLEARSCREEN );

	/* WINDOW 1 IS TOP LEFT OF SCREEN                                     */
	SetPercentWindow(0,0,.5,.8,1);
	SetWin2PlotRatio(1, 0.2,0.14,0.05,0.14 );

	/* WINDOW 2 IS TOP RIGHT OF SCREEN                                    */
	SetPercentWindow(.5,0,1,.8,2);
	SetWin2PlotRatio(2, 0.2,0.14,0.05,0.14 );

	/* DRAW WINDOW 1 - CURVE AND FITTED DATA                              */
	SetCurrentWindow(1);
	setlinestyleXX(0,0,1);
	BorderCurrentWindow( border_color );
	SelectColor( border_color );
	TitleWindow("PLOT OF CURVE AND DATA");
	SelectColor( axis_color );
	TitleXAxis("MW OUTPUT", 0);
	TitleYAxis("MBTU per HOUR", 0);
	SetAxesType(0,0);
	AutoAxes(x, y, 11, 1);

	/* PLOT OUT THE CALCULATED VS INPUT POINTS                            */
	LinePlotData(x, y, 11, 2,0);

	/* DRAW IN THE ORIGINAL DATA POINTS                                   */
	ScatterPlotData(mw_out, mbtu_in, numobs, 3, 1);
	setlinestyleXX(1,0,1);
	DrawGrid(10);

	/* MOVE TO WINDOW 2 - THE ERROR BAR WINDOW                            */
	SetCurrentWindow(2);
	setlinestyleXX(0,0,1);
	BorderCurrentWindow( border_color );
	SelectColor( border_color );
	TitleWindow("CURVE FIT ERROR ANALYSIS");
	SelectColor( axis_color );
	TitleXAxis("MW OUTPUT", 0);
	TitleYAxis("MBTU ERROR", 0);
	SetAxesType(0,0);
	AutoAxes(mw_out,resid,numobs,0);
	DrawGrid(10);

	/* DRAW THE ERROR BARS                                                */
	BargraphData(mw_out, resid, numobs, (max_x - min_x)/60 , 3, 1, 1, 1);
	setlinestyleXX(1,0,1);            /* ^ width of bar ^ */

	/* PLACE SUMMARY DATA ON THE BOTTOM OF THE SCREEN                     */
	_settextcolor( _RED );
	strcpy( buffer, "THIS IS THE CUBIC POLYNOMIAL HEAT RATE EQUATION");
	center_message( 21, buffer);

	/* PRINT OUT THE INDIVIDUAL POINTS AND THE ERRORS                     */
	sprintf( buffer, "H(p) = (%8.6f)*p^3 +"
					   " (%7.4f)*p^2 +"
					   " (%6.3f)*p +"
					   " (%6.2f)",
					   coef[3],
					   coef[2],
					   coef[1],
					   coef[0]);
	center_message( 23, buffer);

	/* ALL DONE.                                                          */
	c = prompt( 25, -1, prompt_color, "All Finished - Press any key." );
	strcpy( mp, "Normal Return of Polynomial Plot");
	return( 'T' );
}


/* ------------------------ input_gen_poly ------------------------------- */
/*                                                                         */
/*  INPUT THE DATA FOR AN INDIVIDUAL GENERATOR                             */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char input_gen_poly( int unitno )
{
	char  c;                           /* CHARACTER                       */
	char  line[81];                    /* LINE OF TEXT                    */
	int   suggest = 0;                 /* DID USER WANT TO USE SUGGESTED  */
	int   numobs;                      /* NUMBER OF POINTS IN HEAT DATA   */
	int   order;                       /* THIRD ORDER POLYNOMIAL          */
	int   i;                           /* COUNTER                         */
	int   count;                       /* GRAPHICS POSITION COUNTER       */
	int   start_pos;                   /* STARTING POSITION OF EDIT       */
	int   end_pos;                     /* POSITION WHERE EDIT ENDED       */
	float mw_out[10],                  /* GENERATOR OUTPUT IN MEGAWATTS   */
		 mbtu_in[10];                 /* BTU INPUT                       */
	float min_out;                     /* MINIMUM OUTPUT                  */
	float max_out;                     /* MAXIMUM OUTPUT                  */
	float coef[4];                     /* POLYNOMIAL COEFFECIENTS         */
	unsigned  ret_key;                 /* KEY RETURNED FROM EDITLINE      */

	/* SET UP VIDEO COLORS */
	_setbkcolor( _RED );
	_settextcolor( _WHITE );
	_clearscreen( _GCLEARSCREEN );
	_displaycursor( _GCURSORON );

	center_message( 1, "Now, let's build a polynomial for a generator."
				    "  Hit ENTER when finished.");

	/* GIVE THE USER THE OPPORTUNITY TO USE SUGGESTED DATA                */
	c = prompt( 3, 0, prompt_color, "Would you like me to suggest data? (Y/N):" );

	switch( toupper( c ) )
		{
		case 'Y':
			suggest = 1;
			center_message(3,"Here are six points for MW_Out and MBtu_In."
						  "  Edit them if you like.");
			mw_out[0] = 140;
			mw_out[1] = 267;
			mw_out[2] = 310;
			mw_out[3] = 430;
			mw_out[4] = 499;
			mw_out[5] = 615;
			mbtu_in[0] = 1600;
			mbtu_in[1] = 2800;
			mbtu_in[2] = 3200;
			mbtu_in[3] = 4547;
			mbtu_in[4] = 5108;
			mbtu_in[5] = 6078;
			numobs = 6;
			break;
		default:
			suggest = 0;
			_settextposition( 3, 5 );
			_outtext( "Input six points for MW_Out "
					"and MBtu_In below.  ENTER when done.");

/* upgrade - allow various numbers of data points */
			numobs = 6;
			break;
		}

	_settextposition( 5, 30);
	_outtext( "MW Output   MBtu Input" );

	/* PRINT OUT THE DATA POINTS FOR EDITING                              */
	for( i = 0; i < numobs; i++)
		{
		_settextposition( 6 + i, 30 );
		if( suggest != 1 )
			{
			mw_out[i] = 0;
			mbtu_in[i] = 0;
			}
		sprintf( line,  "%8.1f%12.1f  ", mw_out[i], mbtu_in[i]);
		_outtext( line );
		}

	/* LOOP THROUGH ADDING DATA POINTS UNTIL FINISHED                     */
	i = 0;
	ret_key = 0;
	start_pos = 0;


	while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) )
		{
		_settextposition( 6 + i, 30 );
		sprintf( line,  "%8.1f%12.1f  ", mw_out[i], mbtu_in[i]);
		ret_key = editline( line, start_pos, &end_pos );
		start_pos = end_pos;
		sscanf( line, "%f %f", &mw_out[i], &mbtu_in[i] );

		/* BASIC DATA CHECKING                                           */
		_settextposition( 16, 23 );
		if( (mw_out[i] < 0) || (mbtu_in[i] <= 0) )
			{
			_outtext( "Negative data not allowed. Redo the last one." );
			}
		else
			{
			sprintf( buffer,"    Data point number%2i is OK.     ", i);
			_outtext( buffer );
			}

		/* MOVE AROUND DEPENDING ON KEY PRESS                            */
		switch( ret_key )
			{
			case KEY_UP:
				if( i > 0 ) i--;
				break;

			case KEY_DOWN:
				if( i < numobs - 1 ) i++;
				break;

			default:
				break;
			}
		}

	/* CHECK FOR TERMINATION BY ESCAPE KEY */
	if( ret_key == KEY_ESCAPE )
		{
		strcpy( mp, "Polynomial terminated by user" );
		return( 'F' );
		}

	/* NOW ASK FOR THE MINIMUM AND MAXIMUM OUTPUT OF THE UNIT             */
	strcpy( buffer,
			"Now input the minimum and maximum output for this unit." );
	center_message( 16, buffer );

	if( suggest == 1 )
		{
		strcpy( line,  "   200.0       700.0  ");
		}
	  else
		{
		strcpy( line,  "     0.0         0.0  ");
		}
	_settextposition( 18, 30 );

	/* EDIT THIS LINE UNTIL FINISHED                                      */
	ret_key = 0;
	while( (ret_key != KEY_ENTER) && (ret_key != KEY_ESCAPE) )
		{
		ret_key = editline( line, 0, &end_pos );
		}
	sscanf( line, "%f %f", &min_out, &max_out );
	_displaycursor( _GCURSOROFF );

	/* CHECK FOR TERMINATION BY ESCAPE KEY */
	if( ret_key == KEY_ESCAPE )
		{
		strcpy( mp, "Polynomial terminated by user" );
		return( 'F' );
		}

	/* CALL THE POLYNOMIAL PLOT                                           */
	c = plot_gen_poly( min_out, max_out, mw_out, mbtu_in, numobs, coef);

	/* ALL DONE.                                                          */
	if( c == 'T' )
		{
		strcpy( mp, "Normal Return");
		return( 'T' );
		}
	return( 'F' );
}

/* ------------------------- PLOT_ITERATIONS ----------------------------- */
/*  PLOT THE ITERATIONS FROM WHATEVER SOLUTION METHOD WAS USED             */
/*  REQUIRES AN ARRAY FOR EACH GUESS OF LAMBDA AND THE SYSTEM LOAD AT      */
/*  EACH GUESS.  It also requires the size of the array.                   */
/* ----------------------------------------------------------------------- */
void plot_iterations( float *each_guess, int num_guess )
{
	char      c;
	int       i;
	float     *iteration;

	if( print_all == 'Y' )
	    prompt( 25, -1, prompt_color, "Entered Plot" );

	iteration = GetMem( num_guess );
	if( BadMem( iteration ) )
		{
		printf("Can't allocate memory for iteration counter");
		exit( 0 );
		}

	/* BUILD AN X ARRAY FOR THE AUTO AXIS ROUTINE */
	for( i = 0; i < num_guess; i++)
		{
		iteration[i] = i;
		}

	/* SET UP GRAPHICS WINDOW FOR PLOTTING                                */
	_setbkcolor( _BLACK );
	_clearscreen( _GCLEARSCREEN );

	/* WINDOW 1 */
	SetPercentWindow(0,0,.85,.9,1);
	SetCurrentWindow(1);
	SetWin2PlotRatio(1, 0.12,0.1,0.05,0.14 );
	setlinestyleXX( 0, 0, 1);
	BorderCurrentWindow( border_color );
	SelectColor( border_color );
	TitleWindow("ITERATION SUMMARY");
	SelectColor( axis_color );
	SetAxesType(0,0);
	ScalePlotArea( 0, floor(min_lambda), MAXITERATIONS, ceil(max_lambda));
	SetXYIntercepts( 0, floor(min_lambda) );
	DrawXAxis( 1, 0 );
	LabelXAxis( 5, 0);
	DrawYAxis( 1, 0 );
	LabelYAxis( 2, 0 );
	TitleXAxis("ITERATION NUMBER", 0);
	TitleYAxis("GUESSED VALUE OF LAMBDA", 0);
	setlinestyleXX(3,0,1);             /* Set line style for grids        */
	DrawGridX( 5 );
	DrawGridY( 2 );
	LinePlotData( iteration, each_guess, num_guess, iter_color, 0);

	/* PRINT OUT THE ITERATION SUMMARY */
	_settextcolor( iter_color );
	_settextposition( 1, 70 );
	_outtext( " #  Lambda");
	for( i = 0; i < num_guess; i++ )
		{
		sprintf( buffer, "%2i %7.4f", i, each_guess[i] );
		_settextposition( i + 2, 70 );
		_outtext( buffer );
		}

	if( fabs( system_load - net_out ) < TOL )
		{
		sprintf( buffer,
			"This case converged in %2i iterations.",
			num_guess);
		}
	else
		{
		sprintf( buffer,
			"This case NEVER CONVERGED after %2i iterations.",
			num_guess);
		}
	_settextcolor( _BRIGHTWHITE );
	center_message( 24, buffer );
	c = any_key( 25, -1, prompt_color );
	free( iteration );

}



/* ------------------------ DISPLAY_UNIT_INCREMENTALS--------------------- */
/*                                                                         */
/*  THIS ROUTINE SIMPLY GRAPHS THE UNIT LAMBDA CURVES, SHOWS THE SYSTEM    */
/*  LAMBDA, AND DISPLAYS THE SUMMARY DATA FOR THE SYSTEM.                  */
/*                                                                         */
/*  IT ALSO ALLOWS SHOWING THE INTERMEDIATE STEPS.                         */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char display_unit_incrementals( float disp_lambda, char show_steps )
{
	int   i;                           /* COUNTER                         */
	int   j;                           /* COUNTER                         */
	float minX;                        /* MINIMUM X RANGE FOR PLOT        */
	float maxX;                        /* MAXIMUM X RANGE FOR PLOT        */
	float minY;                        /* MINIMUM Y RANGE FOR PLOT        */
	float maxY;                        /* MAXIMUM Y RANGE FOR PLOT        */
	int   count;                       /* GRAPHICS POSITION COUNTER       */
	float x[11];                       /* GENERATOR OUTPUT IN MEGAWATTS   */
	float y[11];                       /* INCREMENTAL COST                */
	char  c;                           /* CHARACTER                       */
	char  status[8];                   /* STATUS STRING POINTER           */
	char  det_stat[101];               /* DETAILED STATUS STRING POINTER  */

	/* TEMPORARY VALUES FOR MAX X AND Y                                   */
	minX = 0;
	maxX = 0;
	for( i = 0; i < num_gen; i++)
		{
		if( max_out[i] > maxX ) maxX = max_out[i];
		}
	minY = min_lambda;
	maxY = max_lambda;
	x[0] = minX;
	x[1] = maxX;
	y[0] = minY;
	y[1] = maxY;


	/* SET UP GRAPHICS WINDOW FOR PLOTTING                                */
	_setbkcolor( _BLACK );
	_clearscreen( _GCLEARSCREEN );

	/* WINDOW 1 IS TOP 80% OF  SCREEN  */
	SetPercentWindow(0,0,1,.8,1);
	SetCurrentWindow(1);
	SetWin2PlotRatio(1, 0.12,0.1,0.05,0.14 );
	BorderCurrentWindow( border_color );
	SelectColor( border_color );
	TitleWindow("INDIVIDUAL UNIT INCREMENTAL COST CURVES");
	SelectColor( axis_color );
	TitleXAxis("MW OUTPUT", 0);
	TitleYAxis("LAMBDA", 0);
	SetAxesType(0,0);
	AutoAxes( x, y, 2, 0);
	DrawGridX(20);
	DrawGridY(10);
	setlinestyleXX(3,0,1);             /* Set line style for grids        */

	/* SET UP DATA AND PLOT LAMBDA AS A HORIZONTAL LINE                    */
	x[0] = minX;
	x[1] = maxX;
	y[0] = disp_lambda;
	y[1] = disp_lambda;
	LinePlotData( x, y, 2, lambda_color, 0);

	/* PRINT OUT SUMMARY DATA ON INFORMATION PANEL                         */
	_settextcolor(15);
	_settextposition( 21, 2);
	sprintf(buffer, "----  TOTAL GENERATION =%7.1f  "
				"LOAD =%7.1f  SYSTEM LAMBDA =%7.3f ----",
				net_out,
				system_load,
				disp_lambda);
	_outtext( buffer );
	_settextposition( 22, 2);
	_outtext( "LABMDA" );
	_settextposition( 23, 2);
	_outtext( "UNIT LOAD" );
	_settextposition( 24, 2);
	_outtext( "STATUS" );


	/* LOOP THROUGH EACH GENERATOR and GRAPH THE INCREMENTAL COST         */
	for( i = 0; i < num_gen; i++)
		{

		/* DISPLAY GENERATOR LAMBDAS                                     */
		/* OTHER SECTIONS MADE SURE WE CAN DO THIS                       */
		switch(gen_status[i])
			{
			case 'R':
				gen_lambda[i] = disp_lambda;
				strcpy( status, "Reg.");
				strcpy( det_stat,
					 "System lambda is withing min/max criteria."
					 " This unit is regulating.");
				break;
			case 'L':
				gen_lambda[i] = gen_min_lambda[i];
				strcpy( status, "Low");
				strcpy(det_stat,
					 "System lambda is lower than this unit's "
						 "minimum.  Therefore the unit is held to "
						 "minimum output.");
				break;
			case 'H':
				gen_lambda[i] = gen_max_lambda[i];
				strcpy( status, "High");
				strcpy( det_stat,
					  "System lambda is higher than this unit's "
					  "maximum.  Run it at full output.");
				break;
			case 'O':
				gen_lambda[i] = 0;
				strcpy( status, "Off");
				strcpy( det_stat, "This unit is scheduled off line.");
				break;
			case 'F':
				gen_lambda[i] = 0;
				strcpy( status, "Held");
				sprintf( det_stat, "This unit is set for %6.1 MW and is "
					    "not available for regulated output.",
					    gen_load[i]);
				break;
			default :
				gen_lambda[i] = 0;
				strcpy( status, "UNK.");
				strcpy( det_stat,
					   "I don't know what is going on with this one!");
				break;
			}

		if( show_steps == 'T' )
			{
			c = any_key( 25, -1, prompt_color );

			/* DISPLAY A SUMMARY OF THE INFORMATION                    */
			/* SWAP PAGES TO PRESERVE THE DEVELOPING GRAPH             */
			_setactivepage(1);
			_settextcolor( line_color[i] );
			_clearscreen(_GCLEARSCREEN);
			_setvisualpage(1);
			_settextposition( 3, 20);
			sprintf(buffer,"Condition Summary for Unit %3.0f",gen_name[i]);
			_outtext( buffer );
			_settextposition( 5, 20);
			_outtext( "CONDITION     MIN OUTPUT     MAX OUTPUT");
			_settextposition( 6, 20);
			sprintf(buffer,"Capacity%15.1f%15.1f", min_out[i], max_out[i]);
			_outtext( buffer );
			_settextposition( 7, 20);
			sprintf( buffer, "Lambda%17.3f%15.3f",
				gen_min_lambda[i], gen_max_lambda[i]);
			_outtext( buffer );
			_settextposition( 9, 25 );
			sprintf( buffer, "System Lambda =%7.3f", disp_lambda );
			_outtext( buffer );
			_settextposition( 11, 5);
			_outtext( det_stat );
			_settextposition( 14, 20);
			sprintf( buffer, "This unit is contributing%6.1f"
				" to the system load.", gen_load[i]);
			_outtext( buffer );
			_settextposition( 15, 20);
			_outtext( "Let's see how this unit fits on the lambda graph." );
			_settextposition( 16, 20);
			_outtext( "Look for it in this color." );
			c = any_key( 25, -1, i+1 );

			_setactivepage(0);
			_setvisualpage(0);
		}

		/* CALCULATE TEN POINTS FOR THE INCREMENTAL COST GRAPH           */
		for( j = 0; j < 11; j++)
			{
			x[j] = min_out[i] + ( j * ( max_out[i] - min_out[i] ) / 10 );
			y[j] = dfdp_0[i] +
				  dfdp_1[i] * x[j] +
				  dfdp_2[i] * x[j] * x[j];
			}

		/* PLOT EACH GENERATOR IN A DIFFERENT COLOR AND LABEL AT BOTTOM  */
		LinePlotData(x, y, 11, line_color[i], 0);

		/* GRAPH THE VERTICAL LINES TO THE LAMBDA CURVE                  */
		x[0] = gen_load[i];
		x[1] = gen_load[i];
		y[0] = minX;
		y[1] = gen_lambda[i];
		LinePlotData( x, y, 2, line_color[i], 0);

		/* PRINT SUMMARY DATA ALONG THE BOTTOM LINE                      */
		_settextcolor( line_color[i] );
		_settextposition( 22, (i + 1) * 7 + 3);
		sprintf( buffer, "%7.3f", gen_lambda[i]);
		_outtext( buffer );
		_settextposition( 23, (i + 1) * 7 + 4);
		sprintf(buffer, "%6.1f", gen_load[i]);
		_outtext( buffer );
		_settextposition( 24, (i + 1) * 7 + 6);
		_outtext( status );

		}


}

/* ------------------------ GUESS_A_LAMBDA ------------------------------- */
/*                                                                         */
/*  THIS ROUTINE SIMPLY ASKS THE USER TO MAKE A GUESS FOR LAMBDA AND THEN  */
/*  DISPLAYS THE RESULTS GRAPHICALLY.                                      */
/*                                                                         */
/*                                                                         */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char guess_a_lambda( void )
{
	float     guess;                   /* GUESS FOR LAMBDA                */
	char      c;
	int       num_guesses;             /* NUMBER OF GUESSES               */
	unsigned  ret_key;                 /* RETURN KEY FROM EDITLINE        */
	char      line[10];                /* LINE OF TEXT                    */
	int       end_pos;                 /* ENDING POSITION FROM EDITLINE   */

	/* UPDATE DISPATCH CONSTANTS & FIND LAMBDA RANGE */
	calc_dfdp_constants();

	_setbkcolor( _BLACK );
	_clearscreen( _GCLEARSCREEN );

	system_load = 3000;
	guess = 0;
	num_guesses = 1;
	while( num_guesses < 10 )
		{

		/* SET UP INPUT LINE, TURN ON CURSOR, AND ASK FOR A LAMBDA       */
		_settextcolor( prompt_color );
		_settextposition( 25, 1 );
		sprintf( buffer, "Make a guess for Lambda between %6.3f and %6.3f "
			   "or ESCAPE to Exit:",
			   min_lambda,
			   max_lambda);
		_outtext( buffer );

		/* SET UP THE EDITOR */
		ret_key = 0;
		strcpy( line, " 0.000" );
		while( (ret_key != KEY_ENTER ) && (ret_key != KEY_ESCAPE ) )
			{
			_displaycursor( _GCURSORON );
			_settextposition( 25, 70);
			ret_key = editline( line, 1, &end_pos );
			_displaycursor( _GCURSOROFF );
			}

		/* CHECK TO SEE IF USER WANTS TO QUIT */
		if( ret_key == KEY_ESCAPE )
			{
			strcpy( mp, "Make a Guess Terminated by User");
			return( 'F' );
			}

		/* RESET THE RETURN KEY FOR THE NEXT INPUT                       */
		ret_key = 0;

		/* CHECK INPUT FOR VALID DATA */
		sscanf( line, "%f", &guess );

		/* ADD ERROR HANDLING ON LAMBDA  */
		if( guess > max_lambda)
			{
			c = prompt(25, -1, _RED,
					"Your guess is too HIGH to graph!  Press any key.");
			}
		if( guess < min_lambda)
			{
			c = prompt(25, -1, _RED,
					"Your guess is too LOW to graph!  Press any key.");
			}

		/* DO THE GRAPH IF THE GUESS IS WITHIN RANGE                     */
		if( (guess >= min_lambda) && (guess <= max_lambda) )
			{
			lambda = guess;
			calc_output();
			c = display_unit_incrementals( lambda, 'T' );
			}
		num_guesses++;
		}

}



/* ------------------------ TEACH_EQUAL_LAMBDA --------------------------- */
/*                                                                         */
/*  THIS IS AN INDEPENDENT ROUTINE THAT TEACHES THE BASICS THAT MINIMUM    */
/*  COST IS ACHIEVED WHEN THE LAMBDAS OF TWO UNITS ARE EQUAL.              */
/*  IT DOES USE SOME OF THE EXTERNAL VARIABLES FOR SIMPLICITY.             */
/*                                                                         */
/*                                                                         */
/* ----------------------------------------------------------------------- */

char teach_equal_lambda (void)
{
	int   i;                           /* COUNTER                         */
	int   j;                           /* COUNTER                         */
	unsigned ret_key;                 /* RETURN KEY FROM EDITLINE        */
	int   start_pos;                   /* STARTING POSITION FOR EDITLINE  */
	int   end_pos;                     /* ENDING POSITION FROM EDITLINE   */
	char  line[81];                    /* LINE OF DATA FOR EDITING        */
	float min_lambda;                  /* LAMBDA AT MINIMUM SOLUTION      */
	float minX;                        /* MINIMUM X RANGE FOR PLOT        */
	float maxX;                        /* MAXIMUM X RANGE FOR PLOT        */
	float minY;                        /* MINIMUM Y RANGE FOR PLOT        */
	float maxY;                        /* MAXIMUM Y RANGE FOR PLOT        */
	float minGen;                      /* MINIMUM GENERATION FOR PLOT     */
	float maxGen;                      /* MAXIMUM GENERATION FOR PLOT     */
	float Cost;                        /* ENERGY COST FOR SYSTEM          */
	float minCost;                     /* MINIMUM ENERGY COST FOR PLOT    */
	float maxCost;                     /* MAXIMUM ENERGY COST FOR PLOT    */
	int   count;                       /* GRAPHICS POSITION COUNTER       */
	float x[11];                       /* TEMPORARY DATA FOR PLOTTING     */
	float y[11];                       /* TEMPORARY DATA FOR PLOTTING     */
	float yy[11];                      /* DATA FOR AXIS PLOTTING          */
	float tcost;                       /* TOTAL OUTPUT COST OF GENERATORS */
	char  c;                           /* CHARACTER                       */
	int   color;                       /* COLOR TO FOR EACH ITERATION     */

	/* ARBITRARILY SET THE SYSTEM LOAD                                    */
	system_load = 800;

	/* LOAD UP THE FILE DATA WE NEED AND CALCULATE CONSTANTS              */
	/* THIS SETS UP ALL OF THE NEEDED QUANTITIES FOR OTHER ROUTINES       */
	strcpy( genfile_buff, "TEACHMIN.GEN" );
	c = get_gen_data();
	calc_dfdp_constants();

	if( show_message( "EQUALLAMBDA1" ) == 'F') return ( 'F' );
	if( teach_mode == 'T') any_key( 25, 1, _RED );

	if( show_message( "EQUALLAMBDA2" ) == 'F') return ( 'F' );

	/* CLEAR THE SCREEN IF WE ARE NOT IN TEACH MODE */
	if( teach_mode != 'T' ) _clearscreen( _GCLEARSCREEN );

	/* PRINT OUT THE EQUATION AND SET UP THE MIN/MAX PLOT DATA            */
	for( i = 0; i < 2; i++)
		{
		sprintf( buffer, "%6.1f%8.1f%15.8f*P^3 +%8.5f*P^2 +"
			"%7.4f*P +%6.2f",
			min_out[i],
			max_out[i],
			h_3[i],
			h_2[i],
			h_1[i],
			h_0[i]);
		_settextposition( 8 + i, 8);
		_outtext( buffer );

		/* This is an array of genrator limits and Lambda limits         */
		x[i] = min_out[i];
		x[i+2] = max_out[i];

		/* YY is the axis scaler for the right graph (incremental)       */
		yy[i] = gen_min_lambda[i];
		yy[i+2] = gen_max_lambda[i];
		}

	/* THIS SECTION FINDS THE VERTICAL AXIS SCALING FOR THE LEFT PLOT */
	/* First set unit 0 at minimum */
	gen_load[0] = min_out[0];
	gen_load[1] = system_load - min_out[0];
	calc_energy_cost();
	maxCost = (gen_output_cost[0] + gen_output_cost[1])/system_load;

	/* Now set unit 1 at minimum */
	gen_load[1] = min_out[1];
	gen_load[0] = system_load - min_out[1];
	calc_energy_cost();
	Cost = (gen_output_cost[0] + gen_output_cost[1])/system_load;
	if( Cost > maxCost) maxCost = Cost;

	/* Now find the minimum cost by dispatching */
	c = binary_solution();
	min_lambda = lambda;
	calc_energy_cost();
	minCost = (gen_output_cost[0] + gen_output_cost[1])/system_load;

	/* FIND THE GENERATION RANGES */
	if( min_out[0] < min_out[1] )
		{
		minGen = min_out[0];
		}
	else
		{
		minGen = min_out[1];
		}
	if( max_out[0] > max_out[1] )
		{
		maxGen = max_out[0];
		}
	else
		{
		maxGen = max_out[1];
		}

	/* END OF ESTIMATING RANGE FOR AXIS */

	c = any_key( 25, -1, prompt_color );

	/* INITIALIZE GRAPHICS HERE                                           */
	_setbkcolor( _BLACK );
	_clearscreen( _GCLEARSCREEN );

	/* SET UP GRAPHICS WINDOW # 1 FOR PLOTTING                            */
	SetPercentWindow(0,0,.5,.8,1);     /* WINDOW 1 IS UPPER LEFT  SCREEN  */
	SetCurrentWindow(1);
	SetWin2PlotRatio(1, 0.2,0.1,0.05,0.14 );
	setlinestyleXX(0,0,1);
	BorderCurrentWindow( border_color );
	SelectColor( border_color );
	TitleWindow("ENERGY COST mil/kWH");
	SetAxesType(0,0);
	ScalePlotArea( floor(minGen/100)*100,
				floor(minCost),
				ceil(maxGen/100)*100,
				ceil(maxCost));
	SelectColor( axis_color );
	SetXYIntercepts( floor(minGen/100)*100, floor(minCost) );
	DrawXAxis( 10, 0 );
	LabelXAxis( 10, 0);
	DrawYAxis( 1, 0 );
	LabelYAxis( 1, 0 );
	TitleXAxis("UNIT 1 MW OUTPUT", 0);
	TitleYAxis("COST MILS/kWH", 0);
	setlinestyleXX(3,0,1);
	DrawGridX(10);
	DrawGridY(1);

	/* SET UP GRAPHICS WINDOW # 2 FOR PLOTTING                            */
	SetPercentWindow(.5,0,1,.8,2);     /* WINDOW 2 IS UPPER RIGHT SCREEN  */
	SetCurrentWindow(2);
	SetWin2PlotRatio(2, 0.2,0.1,0.05,0.14 ); /* How plot fills window     */
	setlinestyleXX(0,0,1);
	BorderCurrentWindow( border_color );
	SelectColor( border_color );
	TitleWindow("INCREMENTAL COST CURVES");
	SetAxesType(0,0);
	SelectColor( axis_color );
	setlinestyleXX(0,0,1);
	AutoAxes( x, yy, 4, 0);
	TitleXAxis("UNIT 1 & UNIT 2 MW OUTPUT", 0);
	TitleYAxis("LAMBDA", 0);
	setlinestyleXX(3,0,1);
	DrawGridX(10);
	DrawGridY(10);
	setlinestyleXX(0,0,1);             /* Return to plotting line style   */

	/* LOOP THROUGH EACH GENERATOR and GRAPH THE INCREMENTAL COST         */
	for( i = 0; i < num_gen; i++)
		{
		/* CALCULATE TEN POINTS FOR THE INCREMENTAL COST GRAPH           */

		for( j = 0; j < 11; j++)
			{
			x[j] = min_out[i] + ( j * ( max_out[i] - min_out[i] ) / 10 );
			y[j] = dfdp_0[i] +
				  dfdp_1[i] * x[j] +
				  dfdp_2[i] * x[j] * x[j];
			}

		/* PLOT EACH GENERATOR IN A DIFFERENT COLOR AND LABEL            */
		if( i == 0 )
			{
			LinePlotData(x, y, 11, gen1_color, 0);
			}
		else
			{
			LinePlotData(x, y, 11, gen2_color, 0);
			}
		}

	/* SET UP THE INTERACTIVE SESSION LABELS                              */
	sprintf( buffer,
		    "MAKE A FEW LOAD GUESSES FOR UNIT_1.  UNIT_2 =%6.1f - UNIT_1.",
		    system_load );
	center_message( 21, buffer );
	_settextposition( 22, 1 );
	_settextcolor( gen1_color );       /* Match color to the line graph */
	_outtext( "Unit_1 MW Load" );
	_settextposition( 23, 1 );
	_settextcolor( gen2_color );       /* Match color to the line graph */
	_outtext( "Unit_2 MW Load" );
	_settextposition( 24, 1 );
	_settextcolor( _RED );
	_outtext( "Energy Cost" );

	/* LOOP THROUGH UP TO 8 TIMES AND PLOT EACH NEW POINT ON THE GRAPHS */
	_displaycursor( _GCURSORON );
	ret_key = 0;
	for( i = 0; i < 8; i++ )
		{

		/* CHANGE COLOR FOR EACH OUTPUT CASE                             */
		_settextcolor( line_color[i] );
		strcpy( line, "   00.0" );
		while( (ret_key != KEY_ENTER ) && (ret_key != KEY_ESCAPE ) )
			{
			_settextposition( 22, 15 + 7 * i );
			ret_key = editline( line, 2, &end_pos );
			}
		/* ALLOW USER TO ESCAPE */
		if( ret_key == KEY_ESCAPE )
			{
			strcpy( mp, "Equal lambda demonstration terminated by user.");
			return( 'F' );
			}

		/* RESET THE RETURN KEY FOR THE NEXT INPUT                       */
		ret_key = 0;

		/* GET THE DATA FROM THE EDITING SESSION                         */
		sscanf( line, "%f", &gen_load[0] );

		/* CHECK FOR INPUT ERRORS ON UNIT 1                              */
		if( (gen_load[0] >= min_out[0]) && (gen_load[0] <= max_out[0]) )
			{

			/* NO ERRORS ON UNIT 1 CALCULATE AND PLOT                   */
			gen_load[1] = system_load - gen_load[0];
			calc_energy_cost();
			sprintf( buffer, "%7.1f", gen_load[1]);
			_settextposition( 23, 15 + 7 * i );
			_outtext( buffer );

			/* CALCULATE ENERGY COST $/MWH OR mil/KWH                   */
			tcost = (gen_output_cost[0] + gen_output_cost[1])/system_load;
			sprintf( buffer, "%7.3f", tcost );
			_settextposition( 24, 15 + 7 * i );
			_outtext( buffer );
			center_message(25, "Look at the new points."
						    " Is the cost at a minimum?"
						    " Are the lambdas equal?" );

			/* PLOT THE POINT  ON THE ENERGY COST CURVE                 */
			SetCurrentWindow(1);
			x[0] = gen_load[0];
			y[0] = tcost;
			ScatterPlotData( x, y, 1, line_color[i], 2);

			/* CALCULATE & PLOT BOTH UNITS ON THE INCREMENTAL CURVE     */
			SetCurrentWindow(2);
			for( j = 0; j < 2; j++ )
				{
				x[j] = gen_load[j];
				y[j] = dfdp_0[j] +
					  dfdp_1[j] * x[j] +
					  dfdp_2[j] * x[j] * x[j];
				}
			ScatterPlotData( x, y, 2, line_color[i], 2);
			}

		  /* LOAD IS TOO BIG OR SMALL - ISSUE WARNING                    */
		  else
			{
			sprintf( buffer,
				    "Unit_1 = %6.1fMW.  Must be between%6.1f and%6.1f",
				    gen_load[0],
				    min_out[0],
				    max_out[0]);
			center_message( 25, buffer );
			i--;
			}

		}
	_displaycursor( _GCURSOROFF );
	_settextcolor( _RED );
	sprintf( buffer, "Minimum cost when both lambdas are equal."
		    "  Lambda =%7.3f.  Press any key....",
		    min_lambda);
	center_message( 25, buffer );
	c = getch();
	return( 'T' );
}


/* -------------------------- GET LOSS DATA ------------------------------ */
/*  ALLOW USER TO REQUEST LOSS DATA FROM A FORMATTED FILE                  */
/*  CHECK FOR VALID FILE AND VALID DATA - CALCULATE Y MATRIX               */
/*                                                                         */
/*  THREE DATA BLOCKS ARE READ.                                            */
/*                              1) LINE IMPEDANCE AND CONNECTIVITY         */
/*                              2) BUS VOLTAGES                            */
/*                              3) LOSS COEFFECIENTS FOR EACH BUS          */
/*  RETURNS: T-rue                                                         */
/*           F-alse                                                        */
/*   ALSO SETS VALID DATA FLAG ACCORDINGLY                                 */
/* ----------------------------------------------------------------------- */

char get_loss_data(void)
{

    int   j, k, i, n;              /* Y(j,k)  LINE(i)      GEN-n           */
    int   from, to;                /* FROM BUS & TO BUS                    */
    float r,                       /* RESISTANCE OF LINE DATA              */
		x,                       /* REACTANCE OF LINE DATA               */
		b;                       /* LINE SHUNT REACTANCE                 */
    char  loss_file_buff[81];      /* LOSS DATA FILE BUFFER                */
    FILE  *loss_file;              /* FILE POINTER FOR LOSS DATA           */

    struct complex zline[MAXLINE]; /* LINE IMPEDANCE                       */
    struct complex yline;

    struct complex one;            /* 1.0 angle zero                       */
		 one.x = 1.0;
		 one.y = 0.0;

    valid_data = 'T';              /* ASSUME DATA IS GOOD-WE'LL FIND ERRORS */

    _settextposition(24, 10);
    fflush( stdin );
    printf( "Enter of loss data file name: " );
    gets( loss_file_buff );
    if( (loss_file = fopen( loss_file_buff, "rt" )) == NULL )
	   {

	    /* File open failed */
	   strcpy( mp, "\aUnable to open data file!");
	   return( 'F' );
	   }

    /* File opened successfully - read data */

    fscanf( loss_file, "%i%i\n", &num_line, &num_bus);

    if( print_all == 'Y' )
	 {
	 printf( "\n%i %i\n", num_line, num_bus);
	 }

/* Since I do not have a callable load flow  I will have to read in   */
/* all of the voltages and the A coeffecients.  I can build the Y bus */
/* from the line data.                                                */


    /* CHECK TO SEE IF WE HAVE LINE DATA   EXIT WITH WARNING IF NOT */

    if( (num_line <= 0) || (num_line > MAXLINE))
		{
		strcpy( mp,"\Invalid line data in this case!");
		line_data = 'N';
		return( 'F' );
		}

    /* WE HAVE LINE DATA AVAILABLE - TRY TO READ IT AND MAKE THE Y MATRIX  */

    /* RESET Y BUS VALUES                                                  */

    for( j = 0; j < MAXBUS; j++)       /* C STARTS THE MATRIX AT 0, not 1   */
	  {
	  for( k = 0; k <MAXBUS; k++)
		{
		y[j][k].x = 0;
		y[j][k].y = 0;
		}
	  }

    /* READ LINE DATA */
    for( i = 0; i < num_line; i++)
	  {
	  fscanf( loss_file, "%i %i %f %f %f\n",
		&from,
		&to,
		&r,
		&x,
		&b);
	  if( print_all == 'Y' )
		{
		printf( "%i %i %f %f %f\n",
			from,
			to,
			r,
			x,
			b);
		}

	  /* BASIC ERROR CHECKING                                             */
	  if( (from > MAXBUS) || (to > MAXBUS) )
		{
		strcpy( mp,
			"\aFrom or To bus outside limits of 1 to MAXGEN in Y matrix");
		return( 'F' );
		}
	  if( (r == 0) && (x == 0) )
		{
		strcpy( mp, "\aZero impedance line building Y matrix.");
		return( 'F' );
		}

	  /* NO ERRORS - BUILD Y MATRIX */
	  /* HANDLE C's QUIRK ABOUT STARTING A MATRIX AT ZERO BY SUBTRACTING  */
	  /* ONE FROM THE FROM AND TO BUS                                     */

	  from--;
	  to--;

	  zline[i].x = r;
	  zline[i].y = x;
	  yline = cdiv( one, zline[i] );
	  y[from][from].x = y[from][from].x + yline.x + 0;
	  y[from][from].y = y[from][from].y + yline.y + b/2;
	  y[to  ][to  ].x = y[to  ][to  ].x + yline.x + 0;
	  y[to  ][to  ].y = y[to  ][to  ].y + yline.y + b/2;
	  y[from][to  ].x = y[from][to  ].x - yline.x;
	  y[from][to  ].y = y[from][to  ].y - yline.y;
	  y[to  ][from].x = y[to  ][from].x - yline.x;
	  y[to  ][from].y = y[to  ][from].y - yline.y;
	  }

    /* LINE DATA SEEMS TO BE OK */

    line_data = 'Y';

    /* PRINT OUT Y BUS JUST TO BE SURE IT IS OK */
    /* THIS REQUIRES PRINT SCREEN TO BE ON IF YOU WANT TO SEE IT */

   if( print_all == 'Y')
	{
	_settextposition( 1, 1);
	printf("   j   k  y[j][k].x  y[j][k].y\n");
	for( j = 0; j < num_bus; j++)
		{
		for( k = 0; k < num_bus; k++)
			{
			printf( "%4i%4i %10.5f %10.5f\n", j, k, y[j][k].x, y[j][k].y);
			}
		}
	}

    /* THAT WORKED OK  NOW READ BUS VOLTAGE DATA (IF WE HAVE SOME)       */
    /* CHECK TO SEE IF WE HAVE BUS DATA   EXIT WITH WARNING IF NOT */

    if( (num_bus <= 0) || (num_bus > MAXBUS))
		{
		strcpy( mp, "No bus data in this case!");
		bus_data = 'N';
		return( 'F' );
		}
	/* SO FOR SO GOOD TRY TO READ IT */
   if( print_all == 'Y')
	{
	printf("\nBus Volts    Bus Angle\n");
	}
	for( j = 0; j < num_bus; j++)
		{
		if( fscanf( loss_file, "%f %f\n", &v[j], &theta[j]) == EOF)
			 {
			 strcpy( mp, "\aEnd of file reached reading bus voltages");
			 return( 'F' );
			 }
		if( print_all == 'Y')
			{
			printf( "%10.5f %10.5f\n", v[j], theta[j]);
			}
		}

   if( print_all == 'Y')
	{
	printf("\nA( bus, gen ) Data\n");
	}
   bus_data = 'Y';

	/* LAST CALL - READ IN THE A(j,n) data                                */

	for( j = 0; j < num_bus; j++)
		{
		for( n = 0; n < num_gen; n++)
			{
			if( fscanf( loss_file, "%f", &a[j][n]) == EOF)
				{
				strcpy( mp, "\aEnd of file reached reading A values");
				return( 'F' );
				}
			 if( print_all == 'Y')
				{
				printf( "%5i %5i %10.5f \n", j, n, a[j][n]);
				}
			 }
		if( fscanf( loss_file, "\n" ) == EOF)
			{
			 strcpy( mp,"\aEnd of file reached reading next A value line");
			 return( 'F' );
			 }
		}

	/* LOOKS LIKE ALL DATA IS IN - LET'S DO IT                            */
	a_data = 'T';
	return( 'T' );
}


/* --------------------------- CDIV -------------------------------------- */
/*                                                                         */
/*  COMPLEX DIVIDE A/B                                                     */
/*  INPUT: COMPLEX a AND COMPLEX b                                         */
/*  RETURNS: COMPLEX QUOTIENT  A/B                                         */
/*                                                                         */
/* ----------------------------------------------------------------------- */

struct complex cdiv(struct complex a, struct complex b)
{
	double tmp;
	struct complex c;

	tmp = b.x * b.x + b.y * b.y;

	c.x = ( a.x * b.x + a.y * b.y ) / tmp;
	c.y = ( a.y * b.x - a.x * b.y ) / tmp;

	return(c);
}