// Name:     Micheal H. McCabe
// Date:     April 20, 2009
// Program:  Calculator V10 / Monolithic Version
// Filename: Calc_V10_Main.cpp

// Include Various Libraries

#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cmath>

#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

// Define the namespace

using namespace std;

#define MAX_USER_LIST 1000
#define HELP_e 	     "'e'  to enter N1 and N2\n"
#define HELP_i       "'i'  to enter a list of values (Q to stop)\n"
#define HELP_fi      "'fi' File input:  prompt for a filename and read values into list [] \n"
#define HELP_fo      "'fo' File Output: prompt for a filename and write list values out. \n"
#define HELP_plus    "'+'  Add (N1 + N2) \n"
#define HELP_minus   "'-'  Subtract (N1 - N2) \n"
#define HELP_star    "'*'  Multiply (N1 * N2) \n"
#define HELP_slash   "'/'  Divide (N1 / N2) \n"
#define HELP_caret   "'^'  Raise any real N1 to the power of N2 for any integer N2 (+|-) \n"
#define HELP_exclaim "'!'  Calculate the factorial for any integer N1 (round if real) \n"
#define HELP_c       "'c'  Clear (N1, N2) \n"
#define HELP_cL      "'cL' Clear the memory list of all values. \n"
#define HELP_h       "'h'  Help \n"
#define HELP_l       "'l'  List all the values currently in List () \n"
#define HELP_q       "'q'  Quit \n"
#define HELP_o       "'o'  Output the last calculation results to the screen. \n"
#define HELP_a       "'a'  Average of the values in list [] \n"
#define HELP_d       "'d#' Delete the value at the index # in List [] \n"
#define HELP_m       "'m#' Memorize (save) the last calculation result at index # in list. \n"
#define HELP_s       "'s'  Sum all the values in List [] \n"
#define HELP_sd      "'sd' Calulate the standard deviation in the list [] \n"
#define HELP_p       "'p'  Calculate the product of the values in the list \n"
#define HELP_M1      "'M1' Prompt for index '#' and store N1 into list[#] \n"
#define HELP_M2      "'M2' Prompt for index '#' and store N2 into list[#] \n"
#define HELP_N1      "'N1' Prompt for index '#' and store List[#] into N1 \n"
#define HELP_N2      "'N2' Prompt for index '#' and store List[#] into N2 \n"
#define NO_COMMAND   " No command recognized \n"
#define CMD_PROMPT   "Enter Command: "

// Declare the fancy new datatype

struct NewDataView

	{
	int Used;
	float N1;
	float N2;
	float Last_Result;
	float Array[MAX_USER_LIST];
	};

// Declare the Variables

NewDataView UserData;

// Function Prototypes

int add(NewDataView& Data);
int sub(NewDataView& Data);
int mult(NewDataView& Data);
int div(NewDataView& Data);
int exponent(NewDataView& Data);
int mod(NewDataView& Data);
int sum(NewDataView& Data);
int product(NewDataView& Data);
int mean(NewDataView& Data);
int deviant(NewDataView& Data);
double long factorial(int x);
int ReadInputFile(NewDataView& Data);
int WriteOutputFile(NewDataView& Data);
void list_data(NewDataView& Data);
bool get_numeric(string prompt, string stop, float& output);
void getN1N2(NewDataView& Data);
void clearl(NewDataView& Data);
void clearn(NewDataView& Data);
void helpscreen();
void output_last(NewDataView& Data);
void GetArray(NewDataView& Data);
int directory();
int GetNumArg (string command_line);
int deletelist(int p, NewDataView& Data);
int moveresult(int p, NewDataView& Data);
void storen1(NewDataView& Data);
void storen2(NewDataView& Data);
void loadn1(NewDataView& Data);
void loadn2(NewDataView& Data);


////////////////////////////////////////////////////////////////////////////////
/*----------------------------------------------------------------------------*/
/*	Cut HERE to divide program into INCLUDES and MAIN Routine             */
/*----------------------------------------------------------------------------*/
////////////////////////////////////////////////////////////////////////////////

/*----------------------------------------------------------------------------*/
/*      This is the main event loop and dispatch routine                      */
/*----------------------------------------------------------------------------*/

int main()
	{
	bool command_loop = true;
	string command_line;
	char cmd_chr1;
	char cmd_chr2;
	string temp;
	int iresult=0;
	int p;

	while (command_loop)
		{
    		temp="";
    		cout << CMD_PROMPT;
		getline(cin, command_line);
		cmd_chr1 = command_line[0];
		cmd_chr2 = command_line[1];
		
		// Decode it

		switch (cmd_chr1)
			{
			case 'D':
				directory();
				break;
			case 'e':
				getN1N2(UserData);
				break;
			case 'i':
				GetArray(UserData);
				break;
			case 'f':
             			if (cmd_chr2 == 'i')
               			 	{
               				 ReadInputFile(UserData); 
                		 	}
             			else if (cmd_chr2 == 'o')
                  			WriteOutputFile(UserData);
                  			else temp=NO_COMMAND;
             			break;
        		case '+':
             			add(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case '-':
             			sub(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case '*':
             			mult(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case '/':
             			div(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case '^':
             			exponent(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case '!':
             			iresult=factorial((int)UserData.N1);
             			cout << "Result:  " << iresult << endl;
	     			UserData.Last_Result = (float) iresult;
             			break;
       			case 'c':
             			if (cmd_chr2=='L')
					{
                			clearl(UserData);
                			break;
                			}
             			else
                 			{
                 			clearn(UserData);
                 			break;
                 			}
        		case 'h':
             			helpscreen();
             			break;
        		case 'l':
             			list_data(UserData);
             			break;
        		case 'q':
             			command_loop = false;
             			temp = "Bye, Bye!";
             			break;
        		case 'o':
             			output_last(UserData);
             			break;
        		case 'a':
             			mean(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case 'd':
             			p=GetNumArg(command_line);
				deletelist(p, UserData);
             			break;
        		case 'm':
             			p=GetNumArg(command_line);
				cout << "Store result in List Item #" << p << endl;
				moveresult(p, UserData);
             			break;
        		case 's':
	     			if (cmd_chr2 == 'd')
                			{
                			deviant(UserData);
                			cout << "Result:  " << UserData.Last_Result << endl;
                 			}	
             			else
                			{
                			sum(UserData);
                			cout << "Result:  " << UserData.Last_Result << endl;
                			}
             			break;
        		case 'p':
            		 	product(UserData);
             			cout << "Result:  " << UserData.Last_Result << endl;
             			break;
        		case 'M':
             			if (cmd_chr2 == '1')
                		storen1(UserData);
                		else if (cmd_chr2 == '2')
                     		storen2(UserData);
             			break;
        		case 'N':
              			if (cmd_chr2 == '1')
                		loadn1(UserData);
                		else if (cmd_chr2 == '2')
                     		loadn2(UserData);
            		 	break;
			default:
				cout << NO_COMMAND;
			}	
		}
	return 0;
	}

////////////////////////////////////////////////////////////////////////////////
/*----------------------------------------------------------------------------*/
/*	Cut HERE to divide program into MAIN and SUB Routines                 */
/*----------------------------------------------------------------------------*/
////////////////////////////////////////////////////////////////////////////////



/*----------------------------------------------------------------------------*/
/*      This is the "simple add" function that adds N1 and N2                 */
/*----------------------------------------------------------------------------*/

int add(NewDataView& Data)
{
	Data.Last_Result = Data.N1 + Data.N2;
	return 0;
}

/*----------------------------------------------------------------------------*/
/*      This is the "simple sub" function that subtracts N2 from N1           */
/*----------------------------------------------------------------------------*/

int sub(NewDataView& Data)
{
	Data.Last_Result = Data.N1 - Data.N2;
	return 0;
}

/*----------------------------------------------------------------------------*/
/*     This is the "simple mult" function that multiplies N1 by N2            */
/*----------------------------------------------------------------------------*/

int mult(NewDataView& Data)
{
	Data.Last_Result = Data.N1 * Data.N2;
	return 0;
}

/*----------------------------------------------------------------------------*/
/*     This is the "simple div" function that divides N1 by N2                */
/*----------------------------------------------------------------------------*/

int div(NewDataView& Data)
{
	if (Data.N2==0)
		{cout << "Error! Division by zero is annoying. \n"; return -1;}
	Data.Last_Result = Data.N1 / Data.N2;
	return 0;
}

/*----------------------------------------------------------------------------*/
/*     This is the "exponent" function that raises N1 to the N2  power        */
/*----------------------------------------------------------------------------*/

int exponent(NewDataView& Data)
{
	Data.Last_Result = pow(Data.N1,Data.N2);
	return 0;
}

/*----------------------------------------------------------------------------*/
/*     This is the "simple mod" function that divides N1 by N2                */
/*----------------------------------------------------------------------------*/

int mod(NewDataView& Data)
{
	if (Data.N2==0)
		{cout << "Error! Division by zero is annoying. \n"; return -1;}
	Data.Last_Result = (int)Data.N1 % (int)Data.N2;
	return 0;
}

/*----------------------------------------------------------------------------*/
/*      This is the "sum" function that adds element j through element k      */
/*----------------------------------------------------------------------------*/

int sum(NewDataView& Data)
{
	int i;
	if (Data.Used==0) 
		{cout << "Error! No Data in the silly array. \n"; return -1;}
	Data.Last_Result=0;
	for (i=0; i<=Data.Used-1; i++)
		Data.Last_Result = Data.Last_Result + Data.Array[i];
	return 0;
}

/*----------------------------------------------------------------------------*/
/*   This is the "product" function that multiplies element j thru element l  */
/*----------------------------------------------------------------------------*/

int product(NewDataView& Data)
{
	int i;
	if (Data.Used==0) 
		{cout << "Error! No Data in the silly array. \n"; return -1;}
	Data.Last_Result=1;
	for (i=0; i<=Data.Used-1; i++)
		Data.Last_Result = Data.Last_Result * Data.Array[i];
	return 0;
}

/*----------------------------------------------------------------------------*/
/*   This is the "mean" function that finds the arithmetic mean of array      */
/*----------------------------------------------------------------------------*/

int mean(NewDataView& Data)
{
	int i;
	if (Data.Used==0) 
		{cout << "Error! No Data in the silly array. \n"; return -1;}
	Data.Last_Result=0;
	for (i=0; i<=Data.Used-1; i++)
		Data.Last_Result = Data.Last_Result + Data.Array[i];
	Data.Last_Result = Data.Last_Result / (Data.Used);
	return 0;
}

/*----------------------------------------------------------------------------*/
/*   This is the "deviant" function that finds the standard deviation         */
/*----------------------------------------------------------------------------*/

int deviant(NewDataView& Data)
{
	if (Data.Used==0) 
		{cout << "Error! No Data.  Pervert. \n"; return -1;}
	float list_mean;
        float td=0;
        float sd=0;
        int i=0;
        // My understanding of statistics is a little rusty, so bear with me!
        // First we gotta find the mean...
        mean(Data);
	list_mean = Data.Last_Result;
        // Now we gotta find the difference from the mean for each element,
        // square the difference, and total the squared differences.
        for (i=0; i<=Data.Used-1; i++)
           td=td+pow(fabs(Data.Array[i]-list_mean),2);
        // divide that by the number of elements in the list and take the square
        // root -- then we're done!
        Data.Last_Result=sqrt(td/(Data.Used-1));
        return 0;
}

/*----------------------------------------------------------------------------*/
/*	This is the factorial function that calculates N! via iteration       */
/*----------------------------------------------------------------------------*/
                                
double long factorial(int x)      // This function implements the n-factorial
                                  // operation using an iterative algorithm.
{
       double long temp = (double long) abs(x); // Allocate storage for our 
                                                // argument and stuff it
                                                // with the absolute value of
                                                // the integer we were given as
                                                // input.
                                                
       double long fact = 1;      // This is our accumulator where the result
                                  // will be developed over the course of the
                                  // while loop that follows.
       
       while (temp>1)             // While temp is >1, keep on truckin'.
             {
             fact = fact * temp;  // Self explanatory multiplication.
             temp --;             // Decrement the loop counter.
             }                    // This is really a GOTO, hidden from Dijkstra
                                  // for its own protection.
       if (x<0)                   // If X was negative, we need to change the
             fact = -fact;        // sign of our result back to negative.
       return fact;               // Home again, Home Again, Jiggity Jig!
}

/*----------------------------------------------------------------------------*/
/*	This function reads data into the list from a file                    */
/*----------------------------------------------------------------------------*/

int ReadInputFile(NewDataView& Data)
{
    ifstream InFileDesc;
    string Filename="";
    char response;
    int start;
    float value;
    bool Read_ok = true;
    int count=-1;
	

	cout << Data.Used << " Records in array. \n";
	
	if (Data.Used != 0)
		{
		cout << "Append (A) or Replace (R)? ";
		cin >> response;
		if (response=='A') start=Data.Used;
		else 
			{
			start=0;
			Data.Used=0;
			}
		}


    while (Filename == "")
          {
          cout << "Enter File Name to read: ";
          cin >> Filename;
          if (Filename == "Cancel")
             {
             Read_ok = false;
             break;
             }
             }
    if (Read_ok)
       {
       InFileDesc.open(Filename.c_str());
       if (InFileDesc)
          {
          // File Open
          while (!InFileDesc.eof())
                {
                InFileDesc >> value;
                if (InFileDesc.fail())
                   {
                   // Non-Numeric Encountered
                   InFileDesc.clear();
                   InFileDesc.ignore(100,'\n');
                   }
                else
                    // We have a numeric
                    {
                    count++;
                    if ((Data.Used+count) < MAX_USER_LIST)
                       {
                       Data.Array[Data.Used+count] = value;
                       InFileDesc.ignore(100,'\n');
                       }
                    else
                        {
                        cout << "Array is Full \n";
                        count--;
                        break;
                        }
                        }
                        }
                        }
       InFileDesc.close();
       }
Data.Used=Data.Used+count+1;
cout << "Read in " << (count+1) << " values from " << Filename << endl;
cin.ignore(80,'\n');
return 0;
}

/*----------------------------------------------------------------------------*/
/*	This function writes the user data list into a file                   */
/*----------------------------------------------------------------------------*/

int WriteOutputFile(NewDataView& Data)
	{
	string Filename;
	bool Read_ok;
	int i;
	int return_value;
	ofstream outputfile;
	while (Filename == "")
		{
		cout << "Enter File Name to write: ";
		cin >> Filename;
		cout << endl;
		if (Filename == "Cancel")
			{
			Read_ok = false;
			break;
			}
		}
	outputfile.open(Filename.c_str());
	if (outputfile.is_open())
          	{
          	cout << "Writing " << Data.Used << " records to disk." << endl;
          	for (i=0; i<=Data.Used-1; i++)
              		{
              		outputfile << Data.Array[i] << endl;
              		}
              	outputfile.close();
		return_value=1;
         	}
       else
		{
		cout << "WTF?  Output file could not be opened! \n";
		return_value=-1;
		}           
	cin.ignore(80,'\n');
	return return_value;
	}

/*----------------------------------------------------------------------------*/
/*	This is a simple routine to list all program data                     */
/*----------------------------------------------------------------------------*/

void list_data(NewDataView& Data)
{
	int i;
	cout << setw(10) << fixed << setprecision(4); 
	cout << "Number of Records in Data List:  " << Data.Used << endl;
	cout << "Value Stored in N1:              " << Data.N1 << endl;
	cout << "Value Stored in N2:              " << Data.N2 << endl;
	cout << "Last Result:                     " << Data.Last_Result << endl;
	for (i=0; i<=Data.Used-1; i++)
		{
		cout << i << " : " << Data.Array[i] << endl;
		}
	return;
}


/*----------------------------------------------------------------------------*/
/*	This is the get_numeric routine provided by Dr. Schuyler              */
/*----------------------------------------------------------------------------*/

bool get_numeric(string prompt, string stop, float& output)
{
     bool get_it = true;           // This flag controls the while loop
                                   // executed below.
     
     bool got_one = false;         // Another flag - if set true on return
                                   // indicates a successful numeric value
                                   // stored at the address of output.
                                     
     string temp="";               // A place to store the input string.
     
     float value=0;                // Intermediate result -- stores a numeric
                                   // entry until passed out to the address
                                   // of output.
     
     while (get_it)                // Do this loop while get_it remains true.
           {
           if (prompt !="")        // If the prompt string is not null,
              cout << prompt;      // send the prompt to standard output.
              
           cin >> temp;            // Grab something from standard input.
           
           if (temp==stop)         // If the input is equal to the 'stop' value,
              get_it = false;      // set the control flag to false (in order
                                   // to end the loop after this iteration.)
                
           else {
                istringstream in_stream(temp);  // More black magic from Bell...
                in_stream >> value;             // See following block for an
                                                // interpretation of how I think
                                                // it works!
                                                
                // We're simulating a file operation here, effectively writing
                // out the string we just obtained to a "string stream" and then
                // immediately bringing that data back in as a numeric value.
                
                if (!in_stream)                  // Did it not work?
                   {
                   cout << "Bad entry: (enter " << stop << " to cancel) \n";
                   continue;
                   }
                else                            // Okay, it did work.
                   {
                   output = value;              // Store our value in output.
                   get_it = false;              // Reset the control flag.
                   got_one = true;              // Set out success flag.
                   break;                       // Get out of the current block.
                   }                            
                }                               
           }
     cin.ignore(80,'\n');   
     return got_one;                            // Return from the procedure.
}


/*----------------------------------------------------------------------------*/
/*	This routine prompts the user for N1 and N2 values and accepts them   */
/*----------------------------------------------------------------------------*/

void getN1N2(NewDataView& Data)
{
	float value;
	string prompt = "Please enter a value for N1: ";
	get_numeric(prompt, "Q", value);
	Data.N1=value;
	prompt = "Please enter a value for N2: ";
	get_numeric(prompt, "Q", value);
	Data.N2=value;
	return;
}

/*----------------------------------------------------------------------------*/
/*	This function clears the data list and resets the count to zero       */
/*----------------------------------------------------------------------------*/

void clearl(NewDataView& Data)
{
	int i;
	for (i=0; i<=Data.Used; i++)
		Data.Array[i]=0;
	Data.Used=0;
	return;
}

/*----------------------------------------------------------------------------*/
/*	This function clears only the N1, N2, and Last_Result Values          */
/*----------------------------------------------------------------------------*/

void clearn(NewDataView& Data)
{
	Data.N1 = 0;
	Data.N2 = 0;
	Data.Last_Result=0;
	return;
}

/*----------------------------------------------------------------------------*/
/*	This function displays the help screen                                */
/*----------------------------------------------------------------------------*/

void helpscreen()
{
cout <<  HELP_e;
cout <<  HELP_i;
cout <<  HELP_fi;
cout <<  HELP_fo;
cout <<  HELP_plus;
cout <<  HELP_minus;
cout <<  HELP_star;
cout <<  HELP_slash;
cout <<  HELP_caret;
cout <<  HELP_exclaim;
cout <<  HELP_c;
cout <<  HELP_cL;
cout <<  HELP_h;
cout <<  HELP_l;
cout <<  HELP_q;
cout <<  HELP_o;
cout <<  HELP_a;
cout <<  HELP_d;
cout <<  HELP_m;
cout <<  HELP_s;
cout <<  HELP_sd;
cout <<  HELP_p;
cout <<  HELP_M1;
cout <<  HELP_M2;
cout <<  HELP_N1;
cout <<  HELP_N2;
cout <<  HELP_i;
return;
}

/*----------------------------------------------------------------------------*/
/*	This function displays the result of the last calculation             */
/*----------------------------------------------------------------------------*/

void output_last(NewDataView& Data)
{
	cout << "The result of the last calculation was: " << Data.Last_Result << endl;
	return;
}

/*----------------------------------------------------------------------------*/
/*	This function adds items to the list interactively                    */
/*----------------------------------------------------------------------------*/

void GetArray(NewDataView& Data)
{
	int start=0;
	char response;
	bool continue_entry=true;
	float value;
	bool j;
	cout << Data.Used << " Records in array. \n";
	
	if (Data.Used != 0)
		{
		cout << "Append (A) or Replace (R)? \n";
		cin >> response;
		if (response=='A') start=Data.Used;
		else start=0;
		}

	while (continue_entry)
		{
		cout << start << "(" << Data.Array[start] << ")";
		j=get_numeric(" --->","quit",value);
		if (j)
			{
			Data.Array[start]=value;
			start++;
			}
		else
			{
			continue_entry=false;
			}
		}
	Data.Used=start;
	return;
}

/*----------------------------------------------------------------------------*/
/*	This function displays the files in the current linux directory       */
/*      Please note this is platform dependant and will not work on DOS       */
/*----------------------------------------------------------------------------*/
     
     int directory()
     	{
       	cout << "\nFiles in the current Linux Directory: \n \n:";
       	DIR *dp;
      	struct dirent *ep;
       	dp = opendir ("./");
      	if (dp != NULL)
       		{
           	while (ep = readdir (dp))
			{
             		puts (ep->d_name);
			}
           	(void) closedir (dp);
         	}
       	else
         	perror ("Couldn't open the directory");
	cout << endl;
       	return 0;
    	}

/*----------------------------------------------------------------------------*/
/* 	Function GetNumArg -- gets a numeric argument from command line       */
/*----------------------------------------------------------------------------*/

	int GetNumArg (string command_line)
		{
		string temp;
		int value;
		temp=command_line.substr(1,command_line.length()-1);
		istringstream in_stream(temp);  
                in_stream >> value;             
                if (!in_stream)
                   {
                   cout << "Bad Parameter! \a \n";
                   }
		return value;
		}

/*----------------------------------------------------------------------------*/
/*	This function deletes an element of the data list                     */
/*----------------------------------------------------------------------------*/

	int deletelist(int p, NewDataView& Data)
		{
		int c;
		if (p>=0 and (p<=Data.Used-1))
			{
			for (c=p; c<=(Data.Used-1); c++)
				Data.Array[c]=Data.Array[c+1];
			Data.Used=Data.Used-1;
			}
		else
			{
			cout << "Out of bounds error! \n";
			}
		return 0;
		}

/*----------------------------------------------------------------------------*/
/*	This function copies the result into an element of the data list      */
/*----------------------------------------------------------------------------*/

	int moveresult(int p, NewDataView& Data)
		{
		Data.Array[p]=Data.Last_Result;
		if (p>=Data.Used-1) Data.Used=p+1;
		return 0;
		}
/*----------------------------------------------------------------------------*/
/*	This is a group of functions to handle the M1/M2 N1/N2 commands       */
/*----------------------------------------------------------------------------*/
	void storen1(NewDataView& Data)
		{
		float temp;
		get_numeric("Index No? ","quit",temp);
		Data.Array[(int)temp]=Data.N1;
		if (temp>=Data.Used-1) Data.Used=temp+1;
		return;
		}
	void storen2(NewDataView& Data)
		{
		float temp;
		get_numeric("Index No? ","quit",temp);
		Data.Array[(int)temp]=Data.N2;
		if (temp>=Data.Used-1) Data.Used=temp+1;
		return;
		}
	void loadn1(NewDataView& Data)
		{
		float temp;
		get_numeric("Index No? ","quit",temp);
		Data.N1=Data.Array[(int)temp];
		return;
		}
	void loadn2(NewDataView& Data)
		{
		float temp;
		get_numeric("Index No? ","quit",temp);
		Data.N2=Data.Array[(int)temp];
		return;
		}
/*----------------------------------------------------------------------------*/



	

