// 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; } } */