// Copyright 1999
// College of Computer Science
// Northeastern University Boston MA 02115

// This software may be used for educational purposes as long as this copyright
// notice is retained at the top of all files

// Should this software be modified, the words "Modified from Original" must be
// included as a comment below this notice

// All publication rights are retained.  This software or its documentation may
// not be published in any media either in whole or in part.

///////////////////////////////////////////////////////////////////////////////

// IOTools.cp

#include "CoreTools.h"				// needed for core tools compile in Win32

#include "IOTools.h"

// Globals visible internally

CaseAction DefaultCaseAction = None;
	

// function definitions

void SetCaseAction(CaseAction  action) {
	switch (action) {
		case LowerCase:
		case UpperCase:
		case MixedCase:
			DefaultCaseAction = action;
			break;
		
		default:
			DefaultCaseAction = None;
			break;
	}
}


void GetCaseAction(CaseAction& action) {
	action = DefaultCaseAction;
}


void PromptAnswer(
	const string&	prompt,					// prompt to user
	const string&	answer					// default answer
) {
	// Output prompt and default answer
	
	int psize = prompt.length();
	
	if (psize) {
		// Output prompt
		cout << prompt;
		
		// If prompt does not end with newline then output blank
		if (prompt[psize - 1] != NL) cout << " ";
	}
	
	int asize = answer.length();
	
	if (asize) {
		// Output default answer in brackets with trailing blank
		cout << "[" << answer << "] ";
	}
}


void RequestString(
	const string&	prompt,					// prompt to user
	const string&	answer,					// default answer
	string& 		response				// user response
) {
	// Output prompt and default answer
	PromptAnswer(prompt, answer);
	
	// Read one or more lines of input
	
	response.erase();					// Clear the response
	bool loop = true;					// Loop control
	
	while(loop) {
		string line;					// Line variable
		GetOneLine(line);				// Read the line
		int size = line.length();		// Find its size
		
		// stop reading if input line is empty
		if (!size)
			break;
		
		// check for final backslash to signal read-another-line
		if (line[size - 1] == BackSlash)
			line.erase(size - 1, 1);
		else
			loop = false;
		
		// append line to full response
		response += line;
		
		// check for input error
		if (InputError())
			loop = false;
	}
	
	// Do case change and squash if needed
	
	if (response.length()) {
		// Test case change
		if (response[0] == BackSlash)		// Signal no case change
			response[0] = Blank;			// This blank will be squashed!
		else
			ChangeCase(response, DefaultCaseAction);
		
		// Do squash
		SquashString(response);
	}
	
	// Check if string is now empty
	// If so return squashed answer
	
	if (response.length() == 0) {
		response = answer;					// Copy answer to response
		SquashString(response);				// Squash again
	}
}


int ReadingString(
	const string&	prompt,				// prompt to user
	string& 		response			// user response
) {
	RequestString(prompt, response);
	return response.length();
}


bool InputError(istream& is) {
	int state = is.rdstate();
	return (state != 0);
}


bool InputErrorVerbose(istream& is) {
	// check first using simple form
	if (! InputError(is))
		return false;
	
	// if we reach here then error has happened
	
	// print error nature
	// check order is critical since fail() checks both badbit and failfit
	
	if (is.eof())
		cout << "\n\nEOF Detected in ";
	else
	if (is.bad())
		cout << "\n\nFatal Error in ";
	else
	if (is.fail())
		cout << "\n\nError in ";
	
	// print error stream
	
	if (&is == &cin)
		cout << "user IO\n\n";
	else
		cout << "file IO\n\n";
	
	// signal error has happened
	
	return true;
}


void GetOneLine(istream& is, string& s) {
	// clear the string
	s.erase();

	// clear earlier errors on is
	is.clear();
	
	// exit if is still has an error condition
	if (InputErrorVerbose(is))
		return;

	char c;					// character

	while(true) {
		// read the next character
		is.get(c);

		// break if error or if newline was just read
		if(InputErrorVerbose(is) || (c == '\n'))
			break;

		// otherwise accumulate the character
		s += c;
	}
}


void GetOneLine(string& s) { GetOneLine(cin, s); };


void GetOneLine(istream& is, char* buffer, int room) {
	// if the buffer has no space
	//     send error message
	//     eat the input line
	//     then exit
	if (room <= 0) {
		cout << "\n\nInput buffer array has no space\n\n";
		SkipPastNewline(is);
		return;
	}
	
	// clear the buffer array
	buffer[0] = '\0';

	// clear earlier errors on is
	is.clear();
	
	// exit if is still has an error condition
	if (InputErrorVerbose(is))
		return;

	char c;						// character
	int  p = 0;					// index position
	bool earlybreak = false;	// signal early break

	// decrement room so that room = maximum non-null
	// character space available in the buffer
	room--;

	while(p < room) {
		// read the next character
		is.get(c);
		
		// test for error or if newline was just read
		earlybreak = InputErrorVerbose(is) || (c == '\n');
		
		// break if error or if newline was just read
		if(earlybreak)
			break;

		// otherwise accumulate the character
		buffer[p] = c;

		// increment index position
		p++;
	}

	// add null termination
	buffer[p] = '\0';
	
	// exit if error or if newline was just read
	if (earlybreak)
		return;
	
	// otherwise skip remaining input
	SkipPastNewline(is);
}


void GetOneLine(char* buffer, int room) { GetOneLine(cin, buffer, room); };


// Read input characters until error occurs or until a newline is reached

void SkipPastNewline(istream& is) {
	char c = '\0';
	
	while(!(InputErrorVerbose(is) || (c == '\n')))
		is.get(c);
}


void ChangeCase(char* s, CaseAction action) {

	bool capitalize = true;


	switch (action) {
		case LowerCase:
			while (*s++ = tolower(*s));
			break;
	
		case UpperCase:
			while (*s++ = toupper(*s));
			break;
	
		case MixedCase:
			for ( ; *s; s++) {
				if (isalpha(*s)) {
					if (capitalize)
						*s = toupper(*s);
					else
						*s = tolower(*s);
					
					capitalize = false;
				}
				else
					capitalize = true;
			}
			
			break;
	
		default:
			break;
	}
}


void SquashString(char* s) {
	int size = strlen(s);
	
	if (size > 0) {
		// find location of last non-space character
		//   or stop when search pointer t passes s
		
		char* t = s + (size - 1);
		while ((s <= t) && isspace(*t)) t--;
		
		// increment t past last non-space character
		// insert null to terminate string
		
		*(++t) = Null;
		
		// exit if first character in s is not white
		// space
		
		if (!isspace(*s)) return;
		
		// otherwise find first non-space character

		char* u = s;
		while (isspace(*u)) u++;
		
		// move characters (including null) and exit
		size = (t - u) + 1;
		memmove(s, u, size);
	}
}


void SkipSpace(const char*& s) {
	while (isspace(*s)) s++;
}


void SkipWhile(const char*& s, IntFcnPtr Test) {
	while ((*s) && Test(*s)) s++;
}


void ChangeCase(string& s, CaseAction action) {
	int max = s.length();
	int pos = 0;
	bool capitalize = true;


	switch (action) {
		case LowerCase:
			for( ; pos < max; pos++)
				s[pos] = tolower(s[pos]);
			break;
	
		case UpperCase:
			for( ; pos < max; pos++)
				s[pos] = toupper(s[pos]);
			break;
	
		case MixedCase:
			for( ; pos < max; pos++) {
				if (isalpha(s[pos])) {
					if (capitalize)
						s[pos] = toupper(s[pos]);
					else
						s[pos] = tolower(s[pos]);
					
					capitalize = false;
				}
				else
					capitalize = true;
			}
			
			break;
	
		default:
			break;
	}
}


void SquashString(string& s) {
	int min = 0;
	int max = s.length() - 1;
	
	int newmin;
	int newmax;
	
	// search
	SkipSpace(s, min, newmin);
	
	// easy case: all white space
	if (newmin > max) {
		s.erase();
		return;
	}
	
	// reverse search
	ReverseSkipSpace(s, max, newmax);
	
	// hard cases
	
	// must erase first from the right to preserve indices on the left
	if (newmax < max)
		s.erase(newmax + 1, max - newmax);
	
	// now can erase from the left
	if (min < newmin)
		s.erase(min, newmin - min);
}


void SkipSpace(const string& s, int start, int& stop) {
	SkipWhile(s, isspace, start, stop);
}


void ReverseSkipSpace(const string& s, int start, int& stop) {
	ReverseSkipWhile(s, isspace, start, stop);
}


void SkipWhile(const string& s, IntFcnPtr Test, int start, int& stop) {
	int min = 0;
	int max = s.length();
	
	// error checks
	if (start < min)
		start = min;
	
	if (start >= max) {
		stop = max;
		return;
	}
	
	// search
	stop = start;
	
	while (stop < max && Test(s[stop]))
		stop++;
}


void ReverseSkipWhile(const string& s, IntFcnPtr Test, int start, int& stop) {
	int min = -1;
	int max = s.length() - 1;
	
	// error checks
	if (start > max)
		start = max;
	
	if (start <= min) {
		stop = min;
		return;
	}
	
	// search
	stop = start;
	
	while (stop > min && Test(s[stop]))
		stop--;
}


void PressReturn(const string& message) {
	// blank line
	cout << endl;
	
	// caller message
	if (message.length())
		cout << message << endl;
	
	// standard message
	cout << "Press Return To Continue ";
	
	// wait for and then ignore next input line
	string dummy;
	RequestString(dummy);
	
	// blank line
	cout << endl;
}


bool Confirm(
	const string&	prompt,			// prompt to user
	bool			answer			// default answer
) {
	// set default
	bool choice = answer;
	
	// set default as string
	string yesno;
	
	if (answer)
		yesno = "Y";
	else
		yesno = "N";
	
	// define reply string
	string reply;
	
	// loop until user choice is clear
	while (true) {
		RequestString(prompt, yesno, reply);
		
		if (reply.length() == 0)
			break;
		
		ChangeCase(reply, UpperCase);
		
		if (reply[0] == 'Y') {
			choice = true;
			break;
		}
		
		if (reply[0] == 'N') {
			choice = false;
			break;
		}
		
		// request clarification
		cout << "Must answer Y or N" << endl << endl;
	}
	
	cout << endl;
	
	return choice;
}


char RequestChar(
	const string&	prompt,			// prompt to user
	char			answer			// default answer
) {
	// Output prompt and default answer
	string answerstring(1, answer);
	PromptAnswer(prompt, answerstring);
	
	// Read user input
	string response;
	GetOneLine(response);
	
	if (response.length())
		return response[0];			// non-empty: return first char
	
	return answer;					// empty: return default answer
}


char RequestChar(
	const string&	prompt			// prompt to user
) {
	while (true) {
		// Output prompt
		PromptAnswer(prompt, "");
		
		// Read user input
		string response;
		GetOneLine(response);
		
		if (response.length())
			return response[0];		// non-empty: return first char
		
		// Print error and loop again
		cout << IOErrorMessage(NoDataError) << endl << endl;
		
		if (InputError())
			return Blank;			// error: return blank
	}
}


bool ReadingChar(
	const string&	prompt,			// prompt to user
	char&			answer			// user answer
) {
	// Default answer if no data entered
	answer = Blank;
	
	// Output prompt
	PromptAnswer(prompt, "");
	
	// Read user input
	string response;
	GetOneLine(response);
	
	if (response.length()) {
		answer = response[0];
		return true;
	}
	
	return false;					// empty: return false
}


// Double precision parser class


class DoubleHelper {
	const char*		start;			// start position
	const char*&	next;			// next position to read
	int&			error;			// error code
	
	double Term();
	double Factor();
	double Number();
	double Symbol();
	void GetOneArgument (double& x);
	void GetTwoArguments(double& x, double& y);
	
public:
	DoubleHelper
		(const char* Start, const char*& Next, int& Error) :
		start(Start), next(Next), error(Error)
		{ next = start; error = 0; };
	
	double Eval();
};


// evaluate expression as a summmation of terms
// handle fact that leading term may or may not
// be preceded by + or -
//
// do not permit expressions of form: 3 - -4
// require instead: 3 - (-4)

double DoubleHelper::Eval() {
	double	sum = 0;
	double	s;
	
	bool	first = true;
	
	while (!error) {
		// skip white space
		SkipSpace(next);
		
		// look for + or - operation
		char	operation = ' ';
		
		switch (*next) {
			case '+':
			case '-':
				operation = *next++;

			default:
				break;
		}
		
		// handle missing operation
		
		if (operation == ' ')
			if (first)
				operation = '+';	// default to + on first round
			else
				break;				// exit loop if no operation
		
		first = false;				// reset first for later rounds
		
		// skip white space again
		SkipSpace(next);
		
		// look for spurious + or - operation
		
		if ((*next == '+') || (*next == '-')) {
			error = BadDataError;
			break;					// exit loop if duplicate sign
		}
		
		// find next term and do + or - operation
		
		s = Term();
		
		if (!error)
			switch (operation) {
				case '+':
					sum += s;
					break;
				
				case '-':
					sum -= s;
					break;
				
				default:
					break;
			}
		
		// check for overflow error only if no earlier error detected
		
		if (!error)
			if (!ValidDouble(sum))
				switch (operation) {
					case '+':
						error = AddError;
						break;
					
					case '-':
						error = SubError;
						break;
					
					default:
						break;
				}
	}
	
	return sum;
}


// evaluate term as a product of factors
// let / be ordinary division
// let # be truncated division
// handle fact that first factor may NOT
// be preceded by * or / or % or #

double DoubleHelper::Term() {
	double	product = Factor();		// obtain the first factor
	double	f;
	
	while (!error) {
		// skip white space
		SkipSpace(next);
		
		// look for * or / or % or # operation
		char	operation = ' ';
		
		switch (*next) {
			case '*':
			case '/':
			case '%':
			case '#':
				operation = *next++;

			default:
				break;
		}
		
		// handle missing operation
		
		if (operation == ' ')
			break;					// exit loop if no operation
		
		// find factor and do * or / or % or # operation
		
		f = Factor();
		
		if (!error)
			switch (operation) {
				case '*':
					product *= f;
					break;
				
				case '/':
					if (f != 0)
						product /= f;
					else 
						error = DivError;			// division by zero
					
					break;
				
				case '%':
					if (f != 0)
						product = fmod(product, f);
					else
						error = ModError;			// division by zero
					
					break;
				
				case '#':	// integer division truncated towards zero
					if (f != 0) {
						product /= f;
						
						// must check for overflow before truncation
						if (ValidDouble(product)) {
							if (product >= 0)
								product = floor(product);
							else
								product =  ceil(product);
						}
						else
							error = DivError;		// overflow
					}
					else 
						error = DivError;			// division by zero
					
					break;
				
				default:
					break;
			}
		
		// check for overflow error only if no earlier error detected
		
		if (!error)
			if (!ValidDouble(product))
				switch (operation) {
					case '*':
						error = MulError;
						break;
					
					case '/':
						error = DivError;
						break;
					
					case '%':
						error = ModError;
						break;

					case '#':
						error = DivError;
						break;
					
					default:
						break;
				}
	}
	
	return product;
}


// evaluate factor
// check for number or symbol
// check for expression in parenthesis
// check for expression in absolute value bars

double DoubleHelper::Factor() {
	const char LP = '(';
	const char RP = ')';
	const char BAR = '|';
	
	SkipSpace(next);
	
	// read number
	
	if (isdigit(*next) || (*next == '.'))
		return Number();
	
	// read symbol
	
	if (isalpha(*next))
		return Symbol();
	
	// read (expression)
	
	if (*next == LP) {
		next++;
		
		double d = Eval();
		
		SkipSpace(next);
		
		if (!error)
			if (*next == RP)
				next++;
			else
				error = NoRPError;
		
		return d;	
	}
	
	// read |expression| : absolute value
	
	if (*next == BAR) {
		next++;
		
		double d = Eval();
		
		if (d < 0)
			d = -d;
		
		SkipSpace(next);
		
		if (!error)
			if (*next == BAR)
				next++;
			else
				error = NoBARError;
		
		return d;	
	}
	
	// if we reach this line there is an error
	// the only issue is to decide which error
	
	if (*next == Null)
		error = EndDataError;
	else
		error = BadDataError;
	
	return 0;
}


// read double precision data from string

double DoubleHelper::Number() {
	char*	s = (char*) next;
	char*	t = s;
	double	d = 0;
	
	errno = 0;					// reset ANSI error state
	
	d = strtod(s, &t);			// use ANSI routine to read data
	
	next = t;					// store position at end of data
	
	if (errno) 
		error = NumberError;	// on error: set our error state
	
	return d;
}


// read expression starting with symbol

double DoubleHelper::Symbol() {
	double	d = 0;				// result variable
	
	enum { MAXSIZE = 8, MAXLIST = 24 };
	
	// order string constants and enums using ASCII lexicographic ordering
	// this is required by the search algorithm
	
	const char* namelist[MAXLIST] = {
		"acos",
		"acosdeg",
		"asin",
		"asindeg",
		"atan",
		"atan2",		// 2 arguments
		"atan2deg",		// 2 arguments
		"atandeg",
		"cos",
		"cosdeg",
		"cosh",
		"exp",
		"ln",			// same as log
		"log",			// same as ln
		"log10",
		"pi",			// constant
		"pow",			// 2 arguments
		"sin",
		"sindeg",
		"sinh",
		"sqrt",
		"tan",
		"tandeg",
		"tanh"
	};
	
	enum {
		ACOS,
		ACOSDEG,
		ASIN,
		ASINDEG,
		ATAN,
		ATAN2,
		ATAN2DEG,
		ATANDEG,
		COS,
		COSDEG,
		COSH,
		EXP,
		LN,
		LOG,
		LOG10,
		PI,
		POW,
		SIN,
		SINDEG,
		SINH,
		SQRT,
		TAN,
		TANDEG,
		TANH
	};
	
	// copy the symbol and lower case as we go along
	// make sure that no more than MAXSIZE characters are copied
	
	char name[MAXSIZE + 1];
	int spot = 0;
	
	while (isalpha(*next) || isdigit(*next)) {
		if (spot < MAXSIZE)
			name[spot] = tolower(*next);
		
		spot++;
		next++;
	}
	
	// check for too many characters in symbol
	
	if (spot > (MAXSIZE + 1)) {
		error = SymbolError;
		return d;
	}
	
	// fill in null character
	name[spot] = '\0';
	
	// use simple binary search for name in namelist
	
	int index = 0;
	int lower = 0;
	int upper = MAXLIST - 1;
	int match = 1;
	
	while (lower <= upper) {
		index = (lower + upper) / 2;
		match = strcmp(namelist[index], name);
		
		if (match == 0) break;
		
		if (match < 0)
			lower = index + 1;
		else
			upper = index - 1;
	}
	
	// check for match failure
	
	if (match != 0) {
		error = SymbolError;
		return d;
	}
	
	// begin to process function arguments (except for pi)
	
	double x = 0;
	double y = 0;
	
	if (index != PI) {
		if (index == ATAN2 || index == ATAN2DEG || index == POW)
			GetTwoArguments(x, y);
		else
			GetOneArgument(x);
		
		// check if error occured while getting arguments
		
		if (error)
			return d;
	}
	
	// compute the function on its arguments
	
	errno = 0;					// reset ANSI error state
	
	switch (index) {
		case ACOS:
			d = acos(x);
			break;
			
		case ACOSDEG:
			d = acosdeg(x);
			break;
			
		case ASIN:
			d = asin(x);
			break;
		
		case ASINDEG:
			d = asindeg(x);
			break;
		
		case ATAN:
			d = atan(x);
			break;
			
		case ATANDEG:
			d = atandeg(x);
			break;
			
		case ATAN2:
			d = atan2(x, y);
			break;
		
		case ATAN2DEG:
			d = atan2deg(x, y);
			break;
		
		case COS:
			d = cos(x);
			break;
			
		case COSDEG:
			d = cosdeg(x);
			break;
			
		case COSH:
			d = cosh(x);
			break;
		
		case EXP:
			d = exp(x);
			break;
		
		case LN:
		case LOG:
			d = log(x);
			break;
		
		case LOG10:
			d = log10(x);
			break;
		
		case PI:
			d = pi;
			break;
		
		case POW:
			d = pow(x, y);
			break;
		
		case SIN:
			d = sin(x);
			break;
		
		case SINDEG:
			d = sindeg(x);
			break;
		
		case SINH:
			d = sinh(x);
			break;
		
		case SQRT:
			d = sqrt(x);
			break;
		
		case TAN:
			d = tan(x);
			break;
		
		case TANDEG:
			d = tandeg(x);
			break;
		
		case TANH:
			d = tanh(x);
			break;
		
		default:
			d = 0;
			break;
	}
	
	// check for function evaluation error two ways
	
	if (errno || !ValidDouble(d)) {
		error = FunctionEvalError;
		d = 0;
	}
	
	return d;
}


// read one function argument enclosed in parenthesis

void DoubleHelper::GetOneArgument (double& x) {
	x = 0;
	
	const char LP = '(';
	const char RP = ')';
	
	// find left parenthesis
	
	SkipSpace(next);
	
	if (*next != LP) {
		error = NoLPError;
		return;
	}
	
	next++;
	
	// eval argument x
	
	x = Eval();
	
	if (error)
		return;
	
	// find right parenthesis
	
	SkipSpace(next);
	
	if (*next != RP) {
		error = NoRPError;
		return;
	}
	
	next++;
}


// read two function arguments enclosed in parenthesis and comma separated

void DoubleHelper::GetTwoArguments(double& x, double& y) {
	x = 0;
	y = 0;
	
	const char LP = '(';
	const char RP = ')';
	
	// find left parenthesis
	
	SkipSpace(next);
	
	if (*next != LP) {
		error = NoLPError;
		return;
	}
	
	next++;
	
	// eval argument x
	
	x = Eval();
	
	if (error)
		return;
	
	// find comma
	
	SkipSpace(next);
	
	if (*next != Comma) {
		error = NoCommaError;
		return;
	}
	
	next++;
	
	// eval argument y
	
	y = Eval();
	
	if (error)
		return;
	
	// find right parenthesis
	
	SkipSpace(next);
	
	if (*next != RP) {
		error = NoRPError;
		return;
	}
	
	next++;
}

// Double precision floats

double RequestDouble(		// with default
	const string&	prompt,			// prompt to user
	double			answer			// default answer
) {
	// response string
	string response;

	// convert default to string
	string stringanswer;
	Format(stringanswer, answer);
	
	while (true) {
		// request data with prompt and default answer
		RequestString(prompt, stringanswer, response);
		
		if (response.length() == 0)
			return answer;			// empty: return default answer
		
		int next  = 0;
		int error = NoError;
		
		double d = StringToDouble(response, 0, next, error);
		
		if (!error)
			return d;				// valid: done
		
		ErrorPrint(response, next, error);
		
		if (InputError())
			return answer;			// input error: return default answer
	}
}


double RequestDouble(		// without default
	const string&	prompt			// prompt to user
) {
	// response string
	string response;

	while (true) {
		// request data with prompt
		RequestString(prompt, response);
		
		int next  = 0;
		int error = NoError;
		double d  = 0;
		
		if (response.length() == 0)
			error = NoDataError;
		else
			d = StringToDouble(response, 0, next, error);
		
		if (!error)
			return d;				// valid: done
		
		ErrorPrint(response, next, error);
		
		if (InputError())
			return 0;				// input error: return zero
	}
}


bool ReadingDouble(
	const string&	prompt,			// prompt to user
	double&			answer			// user answer if provided
) {
	// response string
	string response;

	answer = 0;						// default answer if error or no input
	
	while (true) {
		// request data with prompt
		RequestString(prompt, response);

		if (response.length() == 0)
			return false;			// empty: return false
			
		int next  = 0;
		int error = NoError;
		
		double d = StringToDouble(response, 0, next, error);
		
		if (!error) {
			answer = d;
			return true;			// valid: done
		}
		
		ErrorPrint(response, next, error);
		
		if (InputError())
			return false;			// input error: return false
	}
}


double StringToDouble(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
) {
	// initialize error
	error = NoError;
	
	// find size
	int size = source.length();
	
	// check start
	if (start < 0)
		start = 0;
	
	if (start >= size) {
		next  = size;
		error = NoDataError;
		return 0;
	}
	
	// process string via const char* pointer
	
	const char* temp = source.c_str() + start;
	const char* stop = temp;
	
	// use C string version of StringToDouble
	
	double d = StringToDouble(temp, stop, error);
	
	// clean up
	next = start + (stop - temp);
	
	return d;
}


double StringToDouble(
	const char*		start,			// start position
	const char*&	next,			// next position to read
	int&			error			// error code
) {
	DoubleHelper helper(start, next, error);
	
	return helper.Eval();
}


// Single precision floats

float RequestFloat(			// with default
	const string&	prompt,			// prompt to user
	float			answer			// default answer
) {
	while (true) {
		double d = RequestDouble(prompt, answer);
		float x;
		
		if (DoubleToFloat(d, x))
			return x;
		else {
			// format d in scientific notation with 18 decimals
			string result;
			Format(result, d, 0, 18, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


float RequestFloat(			// without default
	const string&	prompt			// prompt to user
) {
	while (true) {
		double	d = RequestDouble(prompt);
		float x;
		
		if (DoubleToFloat(d, x))
			return x;
		else {
			// format d in scientific notation with 18 decimals
			string result;
			Format(result, d, 0, 18, false);
			
			// print actual value of d and the error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


bool ReadingFloat(
	const string&	prompt,			// prompt to user
	float&			answer			// user answer if provided
) {
	answer = 0;						// default answer if error or no input
	
	while (true) {
		double d;
		
		if (ReadingDouble(prompt, d)) {
			float x;
			
			if (DoubleToFloat(d, x)) {
				answer = x;
				return true;
			}
			else {
				// format d in scientific notation with 18 decimals
				string result;
				Format(result, d, 0, 18, false);
				
				// print actual value of d and the error message
				ErrorPrint(result, 0, ConvertError);
			}
		}
		else
			return false;
	}
}


float StringToFloat(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(source, start, next, error);
	float x;
	
	if (!DoubleToFloat(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


float StringToFloat(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(start, next, error);
	float x;
	
	if (!DoubleToFloat(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


bool DoubleToFloat(double d, float& x) {
	// overflow cases
	if (d > maximum_float) {
		x = maximum_float;
		return false;
	}
	
	if (d < minimum_float) {
		x = minimum_float;
		return false;
	}
	
	// normal case
	x = d;
	return true;
}


// Integer: int

int RequestInt(				// with default
	const string&	prompt,			// prompt to user
	int				answer			// default answer
) {
	while (true) {
		double	d = RequestDouble(prompt, answer);
		int x;
		
		if (DoubleToInt(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


int RequestInt(				// without default
	const string&	prompt			// prompt to user
) {
	while (true) {
		double	d = RequestDouble(prompt);
		int x;
		
		if (DoubleToInt(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


bool ReadingInt(
	const string&	prompt,			// prompt to user
	int&			answer			// user answer if provided
) {
	answer = 0;						// default answer if error or no input
	
	while (true) {
		double d;
		
		if (ReadingDouble(prompt, d)) {
			int x;
			
			if (DoubleToInt(d, x)) {
				answer = x;
				return true;
			}
			else {
				// format d in scientific notation with 10 decimals
				string result;
				Format(result, d, 0, 10, false);
				
				// print actual value of d and error message
				ErrorPrint(result, 0, ConvertError);
			}
		}
		else
			return false;
	}
}


int StringToInt(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(source, start, next, error);
	int x;
	
	if (!DoubleToInt(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


int StringToInt(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(start, next, error);
	int x;
	
	if (!DoubleToInt(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


bool DoubleToInt(double d, int& x) {
	// overflow cases
	const double upper = maximum_int + 0.5;
	
	if (d >= upper) {
		x = maximum_int;
		return false;
	}
	
	const double lower = minimum_int - 0.5;
	
	if (d <= lower) {
		x = minimum_int;
		return false;
	}
	
	// normal case
	
	x = (d >= 0) ? (d + 0.5) : (d - 0.5);
	return true;
}


// Integer: long

long RequestLong(			// with default
	const string&	prompt,			// prompt to user
	long			answer			// default answer
) {
	while (true) {
		double	d = RequestDouble(prompt, answer);
		long x;
		
		if (DoubleToLong(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


long RequestLong(			// without default
	const string&	prompt			// prompt to user
) {
	while (true) {
		double	d = RequestDouble(prompt);
		long x;
		
		if (DoubleToLong(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


bool ReadingLong(
	const string&	prompt,			// prompt to user
	long&			answer			// user answer if provided
) {
	answer = 0;						// default answer if error or no input
	
	while (true) {
		double d;
		
		if (ReadingDouble(prompt, d)) {
			long x;
			
			if (DoubleToLong(d, x)) {
				answer = x;
				return true;
			}
			else {
				// format d in scientific notation with 10 decimals
				string result;
				Format(result, d, 0, 10, false);
				
				// print actual value of d and error message
				ErrorPrint(result, 0, ConvertError);
			}
		}
		else
			return false;
	}
}


long StringToLong(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(source, start, next, error);
	long x;
	
	if (!DoubleToLong(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


long StringToLong(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(start, next, error);
	long x;
	
	if (!DoubleToLong(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


bool DoubleToLong(double d, long& x) {
	// overflow cases
	const double upper = maximum_long + 0.5;
	
	if (d >= upper) {
		x = maximum_long;
		return false;
	}
	
	const double lower = minimum_long - 0.5;
	
	if (d <= lower) {
		x = minimum_long;
		return false;
	}
	
	// normal case
	
	x = (d >= 0) ? (d + 0.5) : (d - 0.5);
	return true;
}


// Integer: short

short RequestShort(			// with default
	const string&	prompt,			// prompt to user
	short			answer			// default answer
) {
	while (true) {
		double	d = RequestDouble(prompt, answer);
		short x;
		
		if (DoubleToShort(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


short RequestShort(			// without default
	const string&	prompt			// prompt to user
) {
	while (true) {
		double	d = RequestDouble(prompt);
		short x;
		
		if (DoubleToShort(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


bool ReadingShort(
	const string&	prompt,			// prompt to user
	short&			answer			// user answer if provided
) {
	answer = 0;						// default answer if error or no input
	
	while (true) {
		double d;
		
		if (ReadingDouble(prompt, d)) {
			short x;
			
			if (DoubleToShort(d, x)) {
				answer = x;
				return true;
			}
			else {
				// format d in scientific notation with 10 decimals
				string result;
				Format(result, d, 0, 10, false);
				
				// print actual value of d and error message
				ErrorPrint(result, 0, ConvertError);
			}
		}
		else
			return false;
	}
}


short StringToShort(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(source, start, next, error);
	short x;
	
	if (!DoubleToShort(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


short StringToShort(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(start, next, error);
	short x;
	
	if (!DoubleToShort(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


bool DoubleToShort(double d, short& x) {
	// overflow cases
	const double upper = maximum_short + 0.5;
	
	if (d >= upper) {
		x = maximum_short;
		return false;
	}
	
	const double lower = minimum_short - 0.5;
	
	if (d <= lower) {
		x = minimum_short;
		return false;
	}
	
	// normal case
	
	x = (d >= 0) ? (d + 0.5) : (d - 0.5);
	return true;
}

// Integer: unsigned long

unsigned long RequestUnsignedLong(	// with default
	const string&	prompt,			// prompt to user
	unsigned long	answer			// default answer
) {
	while (true) {
		double	d = RequestDouble(prompt, answer);
		unsigned long x;
		
		if (DoubleToUnsignedLong(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


unsigned long RequestUnsignedLong(	// without default
	const string&	prompt			// prompt to user
) {
	while (true) {
		double	d = RequestDouble(prompt);
		unsigned long x;
		
		if (DoubleToUnsignedLong(d, x))
			return x;
		else {
			// format d in scientific notation with 10 decimals
			string result;
			Format(result, d, 0, 10, false);
			
			// print actual value of d and error message
			ErrorPrint(result, 0, ConvertError);
		}
	}
}


bool ReadingUnsignedLong(
	const string&	prompt,			// prompt to user
	unsigned long&	answer			// user answer if provided
) {
	answer = 0;						// default answer if error or no input
	
	while (true) {
		double d;
		
		if (ReadingDouble(prompt, d)) {
			unsigned long x;
			
			if (DoubleToUnsignedLong(d, x)) {
				answer = x;
				return true;
			}
			else {
				// format d in scientific notation with 10 decimals
				string result;
				Format(result, d, 0, 10, false);
				
				// print actual value of d and error message
				ErrorPrint(result, 0, ConvertError);
			}
		}
		else
			return false;
	}
}


unsigned long StringToUnsignedLong(
	const string&	source,			// string to read
	int				start,			// start position to read
	int&			next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(source, start, next, error);
	unsigned long x;
	
	if (!DoubleToUnsignedLong(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


unsigned long StringToUnsignedLong(
	const char*		start,			// start position to read
	const char*&	next,			// next  position to read
	int&			error			// error code
) {
	double d = StringToDouble(start, next, error);
	unsigned long x;
	
	if (!DoubleToUnsignedLong(d, x))
		if (!error)
			error = ConvertError;
	
	return x;
}


bool DoubleToUnsignedLong(double d, unsigned long& x) {
	// overflow cases
	const double upper = maximum_unsigned_long + 0.5;
	
	if (d >= upper) {
		x = maximum_unsigned_long;
		return false;
	}
	
	const double lower = - 0.5;
	
	if (d <= lower) {
		x = 0;
		return false;
	}
	
	// normal case
	
	x = (d >= 0) ? (d + 0.5) : (d - 0.5);
	return true;
}


// format

void Format(stringstream& stream, double x) {
	if (x < 0) {
		stream << '-';		// output minus sign
		x = -x;				// make x positive
	}
	
	// check if x is equal to an unsigned long 
	unsigned long n;
		
	if (DoubleToUnsignedLong(x, n)) {
		if (x == n) {
			stream << n;
			return;
		}
	}
	
	// use fixed format if x between 0.1 and 1,000,000 otherwise scientific
	// use precision of 6
	
	Format(stream, x, 0, 6, 0.1 <= x && x <= 1000000);
}


void Format(stringstream& stream, float  x) {
	double xx = x;
	Format(stream, xx);
}


void Format(
	stringstream& stream,
	double x,
	int width,
	int precision,
	bool fixed)
{
	// error check and fix precision parameter
	const int plimit = 18;
	
	if (precision > plimit)
		precision = plimit;
	
	if (precision < 0)
		precision = 0;
	
	// set up stream parameters
	stream.precision(precision);

	if (fixed)
		// fixed format
		stream.setf(ios::fixed, ios::floatfield);
	else
		// scientific format
		stream.setf(ios::scientific, ios::floatfield);
	
	// if positive width is specified then use it
	// and set justification
	
	if (width > 0) {
		// justification
		
		if (fixed)
			// fixed format:		justify right
			stream.setf(ios::right, ios::adjustfield);
		else {
			// scientific format:	justify left
			stream.setf(ios::left,  ios::adjustfield);
		
			if (x >= 0) {		// when x >= 0
				stream << " ";	// send leading blank instead of sign
				width--;		// reduce width to compensate for blank
			}
		}
		
		// width
		stream.width(width);
	}

	// output x to stream
	stream << x;
}


void Format(
	stringstream& stream,
	float  x,
	int width,
	int precision,
	bool fixed)
{
	double xx = x;
	Format(stream, xx, width, precision, fixed);
}


void Format(stringstream& stream, long  x, int width) {
	// if positive width is specified then use it
	// and set right justification
	
	if (width > 0) {
		stream.width(width);
		stream.setf(ios::right, ios::adjustfield);
	}

	// output x to stream
	stream << x;
}


void Format(stringstream& stream, int   x, int width) {
	long xx = x;
	Format(stream, xx, width);
}


void Format(stringstream& stream, short x, int width) {
	long xx = x;
	Format(stream, xx, width);
}


void Format(stringstream& stream, unsigned long  x, int width) {
	// if positive width is specified then use it
	// and set right justification
	
	if (width > 0) {
		stream.width(width);
		stream.setf(ios::right, ios::adjustfield);
	}

	// output x to stream
	stream << x;
}


void Format(stringstream& stream, unsigned int   x, int width) {
	unsigned long xx = x;
	Format(stream, xx, width);
}


void Format(stringstream& stream, unsigned short x, int width) {
	unsigned long xx = x;
	Format(stream, xx, width);
}


void Format(string& target, double x) {
	// define stream
	stringstream stream;
	
	// format to the stream
	Format(stream, x);
	
	// copy stream to string
	target = stream.str();
}


void Format(string& target, float  x) {
	double xx = x;
	Format(target, xx);
}


void Format(
	string& target,
	double x,
	int width,
	int precision,
	bool fixed)
{
	// define stream
	stringstream stream;
	
	// format to the stream
	Format(stream, x, width, precision, fixed);
	
	// copy stream to string
	target = stream.str();
}


void Format(
	string& target,
	float  x,
	int width,
	int precision,
	bool fixed)
{
	double xx = x;
	Format(target, xx, width, precision, fixed);
}


void Format(string& target, long  x, int width) {
	// define stream
	stringstream stream;
	
	// format to the stream
	Format(stream, x, width);
	
	// copy stream to string
	target = stream.str();
}


void Format(string& target, int   x, int width) {
	long xx = x;
	Format(target, xx, width);
}


void Format(string& target, short x, int width) {
	long xx = x;
	Format(target, xx, width);
}


void Format(string& target, unsigned long  x, int width) {
	// define stream
	stringstream stream;
	
	// format to the stream
	Format(stream, x, width);
	
	// copy stream to string
	target = stream.str();
}


void Format(string& target, unsigned int   x, int width) {
	long xx = x;
	Format(target, xx, width);
}


void Format(string& target, unsigned short x, int width) {
	long xx = x;
	Format(target, xx, width);
}


void PrintDouble(double x) {
	string target;
	Format(target, x);
	cout << target;
}


const char* IOErrorMessage(int error) {
	switch (error) {
		case NoError:
			return "No error";
		
		case NumberError:
			return "Error: Reading numeric data";
		
		case ColorError:
			return "Error: Reading color data";
		
		case SymbolError:
			return "Error: Undefined identifier";

		case ConvertError:
			return "Error: Out of bounds";
		
		case AddError:
			return "Error: Addition";
		
		case SubError:
			return "Error: Subtraction";
		
		case MulError:
			return "Error: Multiplication";
		
		case DivError:
			return "Error: Division";
		
		case ModError:
			return "Error: Modulus";
		
		case NoLPError:
			return "Error: No starting (";
		
		case NoRPError:
			return "Error: No matching )";
		
		case NoCommaError:
			return "Error: Comma required";
		
		case NoBARError:
			return "Error: No matching |";
		
		case NoDataError:
			return "Error: Data required";
		
		case BadDataError:
			return "Error: Unexpected data";
		
		case EndDataError:
			return "Error: Data ends prematurely";
		
		case FunctionEvalError:
			return "Error: Function evaluation error";
		
		default:
			return "Unknown error";
	}
}


void ErrorPrint(const string& source, int next, int error) {
	// print error message
	cout << IOErrorMessage(error) << '\n';
	
	int size = source.length();
	
	if (next <= 0)
		cout << "? " << source;
	else if (next >= size)
		cout << source << " ?";
	else {
		string s1 = source.substr(0, next);
		string s2 = source.substr(next, size - next);
		cout << s1 << " ? " << s2;
	}
	
	// complete line and request repeat of input data
	cout << "\nRepeat input\n\n";	
}


void ErrorPrint(const char* start, const char* next, int error) {
	// print error message
	cout << IOErrorMessage(error) << '\n';
	
	// truncate start string at next position
	char  c = *next;				// character *next
	char* spot = (char*) next;		// non-const alias for next
	
	if (c) *spot = Null;
	
	// print truncated start string and " ? "
	cout << start << " ? ";
	
	// restore next string and print it also
	// maintain const correctness for next
	if (c) {
		*spot = c;
		cout << next;
	}
	
	// complete line and request repeat of input data
	cout << "\nRepeat input\n\n";	
}

// Console control
// Omit: Summer 97
/*
void ConsoleTextJustify(int how) {
	switch (how) {
		case Left:
				cout.setf(ios::left,  ios::adjustfield);
				break;
		
		case Right:
				cout.setf(ios::right, ios::adjustfield);
				break;
		
		default:
			break;
	}
}
*/
