diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2020-12-03 17:11:43 +0100 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2020-12-08 10:21:07 +0100 |
| commit | 0c39051ba80f04b1177833a006f2d442a7170b56 (patch) | |
| tree | 9e657946a061b5b305f9cf75634db7b37e979eb3 /labb8/lib | |
| parent | 7b7f6808a7b2db2ed21103767434c1445f7815c2 (diff) | |
| download | tddd86-0c39051ba80f04b1177833a006f2d442a7170b56.tar.gz | |
add initial files l8
Diffstat (limited to 'labb8/lib')
58 files changed, 17950 insertions, 0 deletions
diff --git a/labb8/lib/StanfordCPPLib/console.cpp b/labb8/lib/StanfordCPPLib/console.cpp new file mode 100755 index 0000000..c55564c --- /dev/null +++ b/labb8/lib/StanfordCPPLib/console.cpp @@ -0,0 +1,164 @@ +/* + * File: console.cpp + * ----------------- + * This file implements the console.h interface. + */ + +#include <string> +#include "console.h" +#include "error.h" +#include "platform.h" +using namespace std; + +static void sclTerminateHandler(); + +static Platform *pp = getPlatform(); +static bool consoleEcho = false; +static bool consolePrintExceptions = false; +static string consoleLogFile = ""; +static void (*old_terminate)() = NULL; + +void clearConsole() { + pp->clearConsole(); +} + +bool getConsoleEcho() { + return consoleEcho; +} + +string getConsoleLogFile() { + return consoleLogFile; +} + +bool getConsolePrintExceptions() { + return consolePrintExceptions; +} + +void setConsoleEcho(bool echo) { + consoleEcho = echo; +} + +void setConsoleFont(const string & font) { + pp->setConsoleFont(font); +} + +void setConsoleLogFile(const string & filename) { + consoleLogFile = filename; +} + +void setConsolePrintExceptions(bool printExceptions) { + if (printExceptions && !consolePrintExceptions) { + old_terminate = set_terminate(sclTerminateHandler); + } else if (!printExceptions && consolePrintExceptions) { + set_terminate(old_terminate); + } + consolePrintExceptions = printExceptions; +} + +void setConsoleSize(double width, double height) { + pp->setConsoleSize(width, height); +} + +static void sclTerminateHandler() { + ostream& out = cerr; + try { + throw; // re-throws the exception that already occurred + } catch (const ErrorException& ex) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** An ErrorException occurred during program execution: \n"; + msg += " *** "; + msg += ex.what(); + msg += "\n ***\n\n"; + cout.flush(); + out << msg; + throw ex; + } catch (const std::exception& ex) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** An exception occurred during program execution: \n"; + msg += " *** "; + msg += ex.what(); + msg += "\n ***\n\n"; + cout.flush(); + out << msg; + throw ex; + } catch (std::string str) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A string exception occurred during program execution: \n"; + msg += " *** \""; + msg += str; + msg += "\"\n ***\n"; + cout.flush(); + out << msg; + throw str; + } catch (char const* str) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A string exception occurred during program execution: \n"; + msg += " *** \""; + msg += str; + msg += "\"\n ***\n"; + cout.flush(); + out << msg; + throw str; + } catch (int n) { + char buf[128]; + snprintf(buf, 128, "%d", n); + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** An int exception occurred during program execution: \n"; + msg += " *** "; + msg += buf; + msg += "\n ***\n\n"; + cout.flush(); + out << msg; + throw n; + } catch (long l) { + char buf[128]; + snprintf(buf, 128, "%ld", l); + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A long exception occurred during program execution: \n"; + msg += " *** "; + msg += buf; + msg += "\n ***\n\n"; + cout.flush(); + out << msg; + throw l; + } catch (char c) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A char exception occurred during program execution: \n"; + msg += " *** '"; + msg += c; + msg += "'\n ***\n"; + cout.flush(); + out << msg; + throw c; + } catch (bool b) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A bool exception occurred during program execution: \n"; + msg += " *** "; + msg += (b ? "true" : "false"); + msg += "\n ***\n\n"; + cout.flush(); + out << msg; + throw b; + } catch (double d) { + char buf[128]; + snprintf(buf, 128, "%lf", d); + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A double exception occurred during program execution: \n"; + msg += " *** "; + msg += buf; + msg += "\n ***\n\n"; + cout.flush(); + out << msg; + throw d; + } + abort(); +} diff --git a/labb8/lib/StanfordCPPLib/console.h b/labb8/lib/StanfordCPPLib/console.h new file mode 100755 index 0000000..dd3d863 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/console.h @@ -0,0 +1,132 @@ +/* + * File: console.h + * --------------- + * This file redirects the <code>cin</code>, <code>cout</code>, + * and <code>cerr</code> channels to use a console window. This file + * must be included in the source file that contains the <code>main</code> + * method, although it may be included in other source files as well. + */ + +#ifndef _console_h +#define _console_h + +#include <string> + +/* + * Function: clearConsole + * Usage: clearConsole(); + * ---------------------- + * Erases the contents of the console window. + */ + +void clearConsole(); + +/* + * Function: getConsoleEcho + * Usage: bool echo = getConsoleEcho(); + * ---------------------------- + * Returns whether or not the input/output from the Stanford graphical + * console window is being echoed onto the standard operating system terminal + * window. Initially this is false unless set to true by a previous call to + * setConsoleEcho(true). + */ + +bool getConsoleEcho(); + +/* + * Function: getConsoleLogFile + * Usage: string consoleLogFile = getConsoleLogFile(); + * ---------------------------- + * Returns the file name, if any, that was set by a previous call to + * setConsoleLogFile into which console in/output is being logged. + * If setConsoleLogFile has not been called yet by this program, returns + * an empty string (""). + */ + +std::string getConsoleLogFile(); + +/* + * Function: getConsolePrintExceptions + * Usage: bool ex = getConsolePrintExceptions(); + * ---------------------------- + * Returns whether or not a feature is enabled that causes exceptions to be + * echoed to the Stanford graphical console window when they are thrown. + * Disabled (false) by default. + */ + +bool getConsolePrintExceptions(); + +/* + * Function: setConsoleEcho + * Usage: setConsoleEcho(true); + * ---------------------------- + * Enables or disables echoing the input/output from the Stanford graphical + * console window onto the standard operating system terminal window. + * Normally you don't need this echoing, but if you want to be able to copy + * and paste your console interaction into another window, it is useful. + */ + +void setConsoleEcho(bool echo); + +/* + * Function: setConsoleFont + * Usage: setConsoleFont(font); + * ---------------------------- + * Changes the font used for the console. The <code>font</code> parameter + * is typically a string in the form <code>family-style-size</code>. + * In this string, <code>family</code> is the name of the font family; + * <code>style</code> is either missing (indicating a plain font) or one + * of the strings <code>Bold</code>, <code>Italic</code>, or + * <code>BoldItalic</code>; and <code>size</code> is an integer + * indicating the point size. If any of these components is + * specified as an asterisk, the existing value is retained. + * The <code>font</code> parameter can also be a sequence of + * such specifications separated by semicolons, in which case the + * first available font on the system is used. + */ + +void setConsoleFont(const std::string & font); + +/* + * Function: setConsoleLog + * Usage: setConsoleLog("myoutput.txt"); + * ---------------------------- + * Begins dumping a copy of all future console in/output to the given file name. + * Useful for capturing output logs and writing auto-grader scripts. + * If you are logging to a file, the output still also appears on the console. + * By default, logging is not initially enabled. + * Log text is appended to any existing content in the file as it is printed. + * If you pass the name of an invalid file, or one that the current user does + * not have permission to write, a file I/O error will occur the next time + * your program performs a console I/O operation to cout or cin. + * Set to an empty string ("") to disable logging. + */ + +void setConsoleLogFile(const std::string & filename); + +/* + * Function: setConsolePrintExceptions + * Usage: setConsolePrintExceptions(true); + * ---------------------------- + * Enables or disables a feature that causes exceptions to be echoed to the + * Stanford graphical console window when they are thrown. + * Disabled (false) by default. + * Note that using this feature may make it harder to get a stack trace in the + * debugger if you are debugging the cause of an exception. + */ + +void setConsolePrintExceptions(bool printExceptions); + +/* + * Function: setConsoleSize + * Usage: setConsoleSize(width, height); + * ------------------------------------- + * Changes the size of the console to the specified dimensions, measured + * in pixels. + */ + +void setConsoleSize(double width, double height); + +#include "private/main.h" + +#endif diff --git a/labb8/lib/StanfordCPPLib/direction.cpp b/labb8/lib/StanfordCPPLib/direction.cpp new file mode 100755 index 0000000..4156d56 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/direction.cpp @@ -0,0 +1,106 @@ +/* + * File: direction.cpp + * ------------------- + * This file implements the direction.h interface. + */ + +#include "direction.h" +#include "error.h" +#include "strlib.h" +#include "tokenscanner.h" +using namespace std; + +/* + * Implementation notes: leftFrom, rightFrom, opposite + * --------------------------------------------------- + * These functions use the remainder operator to cycle through the + * internal values of the enumeration type. Note that the leftFrom + * function cannot subtract 1 from the direction because the result + * might then be negative; adding 3 achieves the same effect but + * ensures that the values remain positive. + */ + +Direction leftFrom(Direction dir) { + return Direction((dir + 3) % 4); +} + +Direction rightFrom(Direction dir) { + return Direction((dir + 1) % 4); +} + +Direction opposite(Direction dir) { + return Direction((dir + 2) % 4); +} + +/* + * Implementation notes: directionToString + * --------------------------------------- + * The C++ compiler requires the default clause to ensure that this + * function always returns a string, even if the direction is not one + * of the legal values. + */ + +string directionToString(Direction dir) { + switch (dir) { + case NORTH: return "NORTH"; + case EAST: return "EAST"; + case SOUTH: return "SOUTH"; + case WEST: return "WEST"; + default: return "???"; + } +} + +/* + * Implementation notes: << + * ------------------------ + * This operator must return the stream by reference after printing + * the value. The operator << returns this stream, so the function + * can be implemented as a single line. + */ + +std::ostream & operator<<(std::ostream & os, const Direction & dir) { + return os << directionToString(dir); +} + +/* + * Implementation notes: >> + * ------------------------ + * This implementation uses the TokenScanner to read tokens from the + * stream. + */ + +std::istream & operator>>(std::istream & is, Direction & dir) { + TokenScanner scanner(is); + scanner.ignoreWhitespace(); + string token = toUpperCase(scanner.nextToken()); + if (token == "") { + dir = Direction(-1); + } else if (startsWith("NORTH", token)) { + dir = NORTH; + } else if (startsWith("EAST", token)) { + dir = EAST; + } else if (startsWith("SOUTH", token)) { + dir = SOUTH; + } else if (startsWith("WEST", token)) { + dir = WEST; + } else { + error("Direction: Unrecognized direction " + token); + } + return is; +} + +/* + * Implementation notes: ++ + * ------------------------ + * The int parameter in the signature for this operator is a marker used + * by the C++ compiler to identify the suffix form of the operator. Note + * that the value after incrementing a variable containing WEST will be + * out of the Direction range. That fact will not cause a problem if + * this operator is used only in the for loop idiom for which it is defined. + */ + +Direction operator++(Direction & dir, int) { + Direction old = dir; + dir = Direction(dir + 1); + return old; +} diff --git a/labb8/lib/StanfordCPPLib/direction.h b/labb8/lib/StanfordCPPLib/direction.h new file mode 100755 index 0000000..d858aca --- /dev/null +++ b/labb8/lib/StanfordCPPLib/direction.h @@ -0,0 +1,95 @@ +/* + * File: direction.h + * ----------------- + * This file exports an enumerated type called <code>Direction</code> + * whose elements are the four compass points: <code>NORTH</code>, + * <code>EAST</code>, <code>SOUTH</code>, and <code>WEST</code>. + */ + +#ifndef _direction_h +#define _direction_h + +#include <iostream> +#include <string> +#include "foreach.h" + +/* + * Type: Direction + * --------------- + * This enumerated type is used to represent the four compass directions. + */ + +enum Direction { NORTH, EAST, SOUTH, WEST }; + +/* + * Function: leftFrom + * Usage: Direction newdir = leftFrom(dir); + * ---------------------------------------- + * Returns the direction that is to the left of the argument. + */ + +Direction leftFrom(Direction dir); + +/* + * Function: rightFrom + * Usage: Direction newdir = rightFrom(dir); + * ----------------------------------------- + * Returns the direction that is to the right of the argument. + */ + +Direction rightFrom(Direction dir); + +/* + * Function: opposite + * Usage: Direction newdir = opposite(dir); + * ---------------------------------------- + * Returns the direction that is opposite to the argument. + */ + +Direction opposite(Direction dir); + +/* + * Function: directionToString + * Usage: string str = directionToString(dir); + * ------------------------------------------- + * Returns the name of the direction as a string. + */ + +std::string directionToString(Direction dir); + +/* + * Operator: << + * Usage: os << dir; + * ----------------- + * Overloads the <code><<</code> operator so that it is able + * to display <code>Direction</code> values. + */ + +std::ostream & operator<<(std::ostream & os, const Direction & dir); + +/* + * Operator: >> + * Usage: is >> dir; + * ----------------- + * Overloads the <code>>></code> operator so that it is able + * to read <code>Direction</code> values. + */ + +std::istream & operator>>(std::istream & os, Direction & dir); + +/* + * Operator: ++ + * Usage: dir++ + * ------------ + * Overloads the suffix version of the <code>++</code> operator to + * work with <code>Direction</code> values. The sole purpose of this + * definition is to support the idiom + * + *<pre> + * for (Direction dir = NORTH; dir <= WEST; dir++) ... + *</pre> + */ + +Direction operator++(Direction & dir, int); + +#endif diff --git a/labb8/lib/StanfordCPPLib/error.cpp b/labb8/lib/StanfordCPPLib/error.cpp new file mode 100755 index 0000000..c0e0a36 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/error.cpp @@ -0,0 +1,42 @@ +/* + * File: error.cpp + * --------------- + * Implementation of the error function. + */ + +#include <exception> +#include <string> +#include <iostream> +#include "error.h" +using namespace std; + +/* Definitions for the ErrorException class */ + +ErrorException::ErrorException(string msg) { + this->msg = msg; +} + +ErrorException::~ErrorException() throw () { + /* Empty */ +} + +string ErrorException::getMessage() const { + return msg; +} + +const char *ErrorException::what() const throw () { + return ("Error: " + msg).c_str(); +} + +/* + * Implementation notes: error + * --------------------------- + * Earlier implementations of error made it possible, at least on the + * Macintosh, to help the debugger generate a backtrace at the point + * of the error. Unfortunately, doing so is no longer possible if + * the errors are catchable. + */ + +void error(string msg) { + throw ErrorException(msg); +} diff --git a/labb8/lib/StanfordCPPLib/error.h b/labb8/lib/StanfordCPPLib/error.h new file mode 100755 index 0000000..359ea99 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/error.h @@ -0,0 +1,56 @@ +/* + * File: error.h + * ------------- + * This file defines the <code>ErrorException</code> class and the + * <code>error</code> function. + */ + +#ifndef _error_h +#define _error_h + +#include <string> +#include <exception> + +/* + * Class: ErrorException + * --------------------- + * This exception is thrown by calls to the <code>error</code> + * function. Typical code for catching errors looks like this: + * + *<pre> + * try { + * ... code in which an error might occur ... + * } catch (ErrorException & ex) { + * ... code to handle the error condition ... + * } + *</pre> + * + * If an <code>ErrorException</code> is thrown at any point in the + * range of the <code>try</code> (including in functions called from + * that code), control will jump immediately to the error handler. + */ + +class ErrorException : public std::exception { +public: + ErrorException(std::string msg); + virtual ~ErrorException() throw (); + virtual std::string getMessage() const; + virtual const char *what() const throw (); + +private: + std::string msg; +}; + +/* + * Function: error + * Usage: error(msg); + * ------------------ + * Signals an error condition in a program by throwing an + * <code>ErrorException</code> with the specified message. + */ + +void error(std::string msg); + +#include "private/main.h" + +#endif diff --git a/labb8/lib/StanfordCPPLib/filelib.cpp b/labb8/lib/StanfordCPPLib/filelib.cpp new file mode 100755 index 0000000..c598db4 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/filelib.cpp @@ -0,0 +1,360 @@ +/* + * File: filelib.cpp + * ----------------- + * This file implements the filelib.h interface. All platform dependencies + * are managed through the platform interface. + */ + +#include <algorithm> +#include <cctype> +#include <cstdio> +#include <fstream> +#include <iostream> +#include <string> +#include <vector> +#include "filelib.h" +#include "foreach.h" +#include "platform.h" +#include "strlib.h" +#include "vector.h" +using namespace std; + +static Platform *pp = getPlatform(); + +/* Prototypes */ + +static void splitPath(string path, Vector<string> list); +static bool recursiveMatch(string str, int sx, string pattern, int px); + +/* Implementations */ + +bool openFile(ifstream & stream, string filename) { + stream.clear(); + stream.open(expandPathname(filename).c_str()); + return !stream.fail(); +} + +bool openFile(ofstream & stream, string filename) { + stream.clear(); + stream.open(expandPathname(filename).c_str()); + return !stream.fail(); +} + +string promptUserForFile(ifstream & stream, string prompt) { + while (true) { + cout << prompt; + string filename; + getline(cin, filename); + openFile(stream, filename); + if (!stream.fail()) return filename; + stream.clear(); + cout << "Unable to open that file. Try again." << endl; + if (prompt == "") prompt = "Input file: "; + } +} + +string promptUserForFile(ofstream & stream, string prompt) { + while (true) { + cout << prompt; + string filename; + getline(cin, filename); + openFile(stream, filename); + if (!stream.fail()) return filename; + stream.clear(); + cout << "Unable to open that file. Try again." << endl; + if (prompt == "") prompt = "Output file: "; + } +} + +string openFileDialog(ifstream & stream) { + return openFileDialog(stream, "Open File", ""); +} + +string openFileDialog(ifstream & stream, string title) { + return openFileDialog(stream, title, ""); +} + +string openFileDialog(ifstream & stream, string title, string path) { + string filename = pp->openFileDialog(title, "load", path); + if (filename == "") return ""; + stream.open(filename.c_str()); + return (stream.fail()) ? "" : filename; +} + +string openFileDialog(ofstream & stream) { + return openFileDialog(stream, "Open File", ""); +} + +string openFileDialog(ofstream & stream, string title) { + return openFileDialog(stream, title, ""); +} + +string openFileDialog(ofstream & stream, string title, string path) { + string filename = pp->openFileDialog(title, "save", path); + if (filename == "") return ""; + stream.open(filename.c_str()); + return (stream.fail()) ? "" : filename; +} + +void readEntireFile(istream & is, Vector<string> & lines) { + lines.clear(); + while (true) { + string line; + getline(is, line); + if (is.fail()) break; + lines.add(line); + } +} + +void readEntireFile(istream & is, vector<string> & lines) { + lines.clear(); + while (true) { + string line; + getline(is, line); + if (is.fail()) break; + lines.push_back(line); + } +} + +string getRoot(string filename) { + int dot = -1; + int len = filename.length(); + for (int i = 0; i < len; i++) { + char ch = filename[i]; + if (ch == '.') dot = i; + if (ch == '/' || ch == '\\') dot = -1; + } + if (dot == -1) { + return filename; + } else { + return filename.substr(0, dot); + } +} + +string getExtension(string filename) { + int dot = -1; + int len = filename.length(); + for (int i = 0; i < len; i++) { + char ch = filename[i]; + if (ch == '.') dot = i; + if (ch == '/' || ch == '\\') dot = -1; + } + if (dot == -1) { + return ""; + } else { + return filename.substr(dot); + } +} + +string getHead(string filename) { + int slash = -1; + int len = filename.length(); + for (int i = 0; i < len; i++) { + char ch = filename[i]; + if (ch == '/' || ch == '\\') slash = i; + } + if (slash < 0) { + return ""; + } else if (slash == 0) { + return "/"; + } else { + return filename.substr(0, slash); + } +} + +string getTail(string filename) { + int slash = -1; + int len = filename.length(); + for (int i = 0; i < len; i++) { + char ch = filename[i]; + if (ch == '/' || ch == '\\') slash = i; + } + if (slash < 0) { + return filename; + } else { + return filename.substr(slash + 1); + } +} + +string defaultExtension(string filename, string ext) { + bool force = (ext[0] == '*'); + if (force) ext = ext.substr(1); + int dot = -1; + int len = filename.length(); + for (int i = 0; i < len; i++) { + char ch = filename[i]; + if (ch == '.') dot = i; + if (ch == '/' || ch == '\\') dot = -1; + } + if (dot == -1) { + force = true; + dot = len; + } + if (force) { + return filename.substr(0, dot) + ext; + } else { + return filename; + } +} + +string openOnPath(ifstream & stream, string path, string filename) { + Vector<string> paths; + splitPath(path, paths); + foreach (string dir in paths) { + string pathname = dir + "/" + filename; + if (openFile(stream, pathname)) return pathname; + } + return ""; +} + +string openOnPath(ofstream & stream, string path, string filename) { + Vector<string> paths; + splitPath(path, paths); + foreach (string dir in paths) { + string pathname = dir + "/" + filename; + if (openFile(stream, pathname)) return pathname; + } + return ""; +} + +string findOnPath(string path, string filename) { + ifstream stream; + string result = openOnPath(stream, path, filename); + if (result != "") stream.close(); + return result; +} + +void deleteFile(string filename) { + remove(expandPathname(filename).c_str()); +} + +void renameFile(string oldname, string newname) { + oldname = expandPathname(oldname); + newname = expandPathname(newname); + rename(oldname.c_str(), newname.c_str()); +} + +void createDirectoryPath(string path) { + size_t cp = 1; + if (path == "") return; + while ((cp = path.find('/', cp + 1)) != string::npos) { + createDirectory(path.substr(0, cp - 1)); + } + createDirectory(path); +} + +bool matchFilenamePattern(string filename, string pattern) { + return recursiveMatch(filename, 0, pattern, 0); +} + +bool fileExists(string filename) { + return pp->fileExists(filename); +} + +bool isFile(string filename) { + return pp->isFile(filename); +} + +bool isSymbolicLink(string filename) { + return pp->isSymbolicLink(filename); +} + +bool isDirectory(string filename) { + return pp->isDirectory(filename); +} + +void setCurrentDirectory(string path) { + return pp->setCurrentDirectory(path); +} + +string getCurrentDirectory() { + return pp->getCurrentDirectory(); +} + +void createDirectory(string path) { + return pp->createDirectory(path); +} + +string getDirectoryPathSeparator() { + return pp->getDirectoryPathSeparator(); +} + +string getSearchPathSeparator() { + return pp->getSearchPathSeparator(); +} + +string expandPathname(string filename) { + return pp->expandPathname(filename); +} + +void listDirectory(string path, Vector<string> & list) { + vector<string> vec; + listDirectory(path, vec); + list.clear(); + foreach (string file in vec) { + list.add(file); + } +} + +void listDirectory(string path, vector<string> & list) { + return pp->listDirectory(path, list); +} + +/* Private functions */ + +static void splitPath(string path, Vector<string> list) { + char sep = (path.find(';') == string::npos) ? ':' : ';'; + path += sep; + size_t start = 0; + while (true) { + size_t finish = path.find(sep, start); + if (finish == string::npos) break; + if (finish > start + 1) { + list.add(path.substr(start, finish - start - 1)); + } + start = finish + 1; + } +} + +static bool recursiveMatch(string str, int sx, string pattern, int px) { + int slen = str.length(); + int plen = pattern.length(); + if (px == plen) return (sx == slen); + char pch = pattern[px]; + if (pch == '*') { + for (int i = sx; i <= slen; i++) { + if (recursiveMatch(str, i, pattern, px + 1)) return true; + } + return false; + } + if (sx == slen) return false; + char sch = str[sx]; + if (pch == '[') { + bool match = false; + bool invert = false; + px++; + if (px == plen) { + error("matchFilenamePattern: missing ]"); + } + if (pattern[px] == '^') { + px++; + invert = true; + } + while (px < plen && pattern[px] != ']') { + if (px + 2 < plen && pattern[px + 1] == '-') { + match |= (sch >= pattern[px] && sch <= pattern[px + 2]); + px += 3; + } else { + match |= (sch == pattern[px]); + px++; + } + } + if (px == plen) { + error("matchFilenamePattern: missing ]"); + } + if (match == invert) return false; + } else if (pch != '?') { + if (pch != sch) return false; + } + return recursiveMatch(str, sx + 1, pattern, px + 1); +} diff --git a/labb8/lib/StanfordCPPLib/filelib.h b/labb8/lib/StanfordCPPLib/filelib.h new file mode 100755 index 0000000..f0c90ea --- /dev/null +++ b/labb8/lib/StanfordCPPLib/filelib.h @@ -0,0 +1,352 @@ +/* + * File: filelib.h + * --------------- + * This file exports a standardized set of tools for working with + * files. The library offers at least some portability across the + * file systems used in the three supported platforms: Mac OSX, + * Windows, and Linux. Directory and search paths are allowed to + * contain separators in any of the supported styles, which usually + * makes it possible to use the same code on different platforms. + */ + +#ifndef _filelib_h +#define _filelib_h + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> +#include "vector.h" + +/* + * Function: openFile + * Usage: if (openFile(stream, filename)) ... + * ------------------------------------------ + * Opens the filestream <code>stream</code> using the specified + * filename. This function is similar to the <code>open</code> + * method of the stream classes, but uses a C++ <code>string</code> + * object instead of the older C-style string. If the operation + * succeeds, <code>openFile</code> returns <code>true</code>; + * if it fails, <code>openFile</code> sets the failure flag in the + * stream and returns <code>false</code>. + */ + +bool openFile(std::ifstream & stream, std::string filename); +bool openFile(std::ofstream & stream, std::string filename); + +/* + * Function: promptUserForFile + * Usage: string filename = promptUserForFile(stream, prompt); + * ----------------------------------------------------------- + * Asks the user for the name of a file. The file is opened using + * the reference parameter <code>stream</code>, and the function + * returns the name of the file. If the requested file cannot be + * opened, the user is given additional chances to enter a valid file. + * The optional <code>prompt</code> argument provides an input prompt + * for the user. + */ + +std::string promptUserForFile(std::ifstream & stream, std::string prompt = ""); +std::string promptUserForFile(std::ofstream & stream, std::string prompt = ""); + +/* + * Function: openFileDialog + * Usage: string filename = openFileDialog(stream); + * string filename = openFileDialog(stream, title); + * string filename = openFileDialog(stream, title, path); + * ------------------------------------------------------------- + * Opens a dialog that allows the user to choose the file. The + * <code>title</code> parameter is displayed in the dialog title. + * The <code>path</code> parameter is used to set the working directory; + * if <code>path</code> does not appear, <code>openFileDialog</code> + * uses the current directory. + */ + +std::string openFileDialog(std::ifstream & stream); +std::string openFileDialog(std::ifstream & stream, std::string title); +std::string openFileDialog(std::ifstream & stream, std::string title, + std::string path); +std::string openFileDialog(std::ofstream & stream); +std::string openFileDialog(std::ofstream & stream, std::string title); +std::string openFileDialog(std::ofstream & stream, std::string title, + std::string path); + +/* + * Function: readEntireFile + * Usage: readEntireFile(is, lines); + * --------------------------------- + * Reads the entire contents of the specified input stream into the + * string vector <code>lines</code>. The client is responsible for + * opening and closing the stream. The vector can be either an STL + * <code>vector</code> or a <code>Vector</code> as defined in the + * Stanford C++ libraries. + */ + +void readEntireFile(std::istream & is, Vector<std::string> & lines); +void readEntireFile(std::istream & is, std::vector<std::string> & lines); + +/* + * Function: getRoot + * Usage: string root = getRoot(filename); + * --------------------------------------- + * Returns the root of <code>filename</code>. The root consists + * of everything in <code>filename</code> up to the last dot and + * the subsequent extension. If no dot appears in the final component + * of the filename, <code>getRoot</code> returns the entire name. + */ + +std::string getRoot(std::string filename); + +/* + * Function: getExtension + * Usage: ext = getExtension(filename); + * ------------------------------------ + * Returns the extension of <code>filename</code>. The extension + * consists of the separating dot and all subsequent characters. + * If no dot exists in the final component, <code>getExtension</code> + * returns the empty string. These semantics ensure that concatenating + * the root and the extension always returns the original filename. + */ + +std::string getExtension(std::string filename); + +/* + * Function: getHead + * Usage: head = getHead(filename); + * -------------------------------- + * Returns all but the last component of a path name. The components + * of the path name can be separated by any of the directory path + * separators (forward or reverse slashes). The special cases are + * illustrated by the following examples: + * + *<pre> + * getHead("a/b") = "a" getTail("a/b") = "b" + * getHead("a") = "" getTail("a") = "a" + * getHead("/a") = "/" getTail("/a") = "a" + * getHead("/") = "/" getTail("/") = "" + *</pre> + */ + +std::string getHead(std::string filename); + +/* + * Function: getTail + * Usage: tail = getTail(filename); + * -------------------------------- + * Returns the last component of a path name. The components of the + * path name can be separated by any of the directory path separators + * (forward or reverse slashes). For details on the interpretation of + * special cases, see the comments for the <code>getHead</code> function. + */ + +std::string getTail(std::string filename); + +/* + * Function: defaultExtension + * Usage: string newname = defaultExtension(filename, ext); + * -------------------------------------------------------- + * Adds an extension to a file name if none already exists. If the + * <code>extension</code> argument begins with a leading <code>*</code>, + * any existing extension in <code>filename</code> is replaced by + * <code>ext</code>. + */ + +std::string defaultExtension(std::string filename, std::string ext); + +/* + * Function: openOnPath + * Usage: string pathname = openOnPath(stream, path, filename); + * ------------------------------------------------------------ + * Opens a file using a search path. If <code>openOnPath</code> + * is successful, it returns the first path name on the search path + * for which <code>stream.open</code> succeeds. The <code>path</code> + * argument consists of a list of directories that are prepended to the + * filename, unless <code>filename</code> begins with an absolute + * directory marker, such as <code>/</code> or <code>~</code>. + * The directories in the search path may be separated either + * by colons (Unix or Mac OS) or semicolons (Windows). If the file + * cannot be opened, the failure bit is set in the <code>stream</code> + * parameter, and the <code>openOnPath</code> function returns the + * empty string. + */ + +std::string openOnPath(std::ifstream & stream, std::string path, + std::string filename); +std::string openOnPath(std::ofstream & stream, std::string path, + std::string filename); + +/* + * Function: findOnPath + * Usage: string pathname = findOnPath(path, filename); + * ---------------------------------------------------- + * Returns the canonical name of a file found using a search path. + * The <code>findOnPath</code> function is similar to + * <code>openOnPath</code>, except that it doesn't actually + * return an open stream. If no matching file is found, + * <code>findOnPath</code> returns the empty string. + */ + +std::string findOnPath(std::string path, std::string filename); + +/* + * Function: deleteFile + * Usage: deleteFile(filename); + * ---------------------------- + * Deletes the specified file. Errors are reported by calling + * <code>error</code>. + */ + +void deleteFile(std::string filename); + +/* + * Function: renameFile + * Usage: renameFile(oldname, newname); + * ------------------------------------ + * Renames a file. Errors are reported by calling + * <code>error</code> in the implementation. + */ + +void renameFile(std::string oldname, std::string newname); + +/* + * Function: fileExists + * Usage: if (fileExists(filename)) ... + * ------------------------------------ + * Returns <code>true</code> if the specified file exists. + */ + +bool fileExists(std::string filename); + +/* + * Function: isFile + * Usage: if (isFile(filename)) ... + * -------------------------------- + * Returns <code>true</code> if the specified file is a regular file. + */ + +bool isFile(std::string filename); + +/* + * Function: isSymbolicLink + * Usage: if (isSymbolicLink(filename)) ... + * ---------------------------------------- + * Returns <code>true</code> if the specified file is a symbolic link. + */ + +bool isSymbolicLink(std::string filename); + +/* + * Function: isDirectory + * Usage: if (isDirectory(filename)) ... + * ------------------------------------- + * Returns <code>true</code> if the specified file is a directory. + */ + +bool isDirectory(std::string filename); + +/* + * Function: setCurrentDirectory + * Usage: setCurrentDirectory(filename); + * ------------------------------------- + * Changes the current directory to the specified path. + */ + +void setCurrentDirectory(std::string path); + +/* + * Function: getCurrentDirectory + * Usage: string filename = getCurrentDirectory(); + * ----------------------------------------------- + * Returns an absolute filename for the current directory. + */ + +std::string getCurrentDirectory(); + +/* + * Function: createDirectory + * Usage: createDirectory(path); + * ----------------------------- + * Creates a new directory for the specified path. The + * <code>createDirectory</code> function does not report an error if + * the directory already exists. Unlike <code>createDirectoryPath</code>, + * <code>createDirectory</code> does not create missing directories + * along the path. If some component of <code>path</code> does + * not exist, this function signals an error. + */ + +void createDirectory(std::string path); + +/* + * Function: createDirectoryPath + * Usage: createDirectoryPath(path); + * --------------------------------- + * Creates a new directory for the specified path. If intermediate + * components of <code>path</code> do not exist, this function creates + * them as needed. + */ + +void createDirectoryPath(std::string path); + +/* + * Function: expandPathname + * Usage: string pathname = expandPathname(filename); + * -------------------------------------------------- + * Expands a filename into a canonical name for the platform. + */ + +std::string expandPathname(std::string filename); + +/* + * Function: listDirectory + * Usage: listDirectory(path, list); + * --------------------------------- + * Adds an alphabetized list of the files in the specified directory + * to the string vector <code>list</code>. This list excludes the + * names <code>.</code> and <code>..</code> entries. + */ + +void listDirectory(std::string path, Vector<std::string> & list); +void listDirectory(std::string path, std::vector<std::string> & list); + +/* + * Function: matchFilenamePattern + * Usage: if (matchFilenamePattern(filename, pattern)) ... + * ------------------------------------------------------- + * Determines whether the filename matches the specified pattern. The + * pattern string is interpreted in much the same way that a Unix shell + * expands filenames and supports the following wildcard options: + * + *<pre> + * ? Matches any single character + * * Matches any sequence of characters + * [...] Matches any of the specified characters + * [^...] Matches any character <i>except</i> the specified ones + *</pre> + * + * The last two options allow a range of characters to be specified in the + * form <code>a-z</code>. + */ + +bool matchFilenamePattern(std::string filename, std::string pattern); + +/* + * Function: getDirectoryPathSeparator + * Usage: string sep = getDirectoryPathSeparator(); + * ------------------------------------------------ + * Returns the standard directory path separator used on this platform. + */ + +std::string getDirectoryPathSeparator(); + +/* + * Function: getSearchPathSeparator + * Usage: string sep = getSearchPathSeparator(); + * --------------------------------------------- + * Returns the standard search path separator used on this platform. + */ + +std::string getSearchPathSeparator(); + +#include "private/main.h" + +#endif diff --git a/labb8/lib/StanfordCPPLib/foreach.h b/labb8/lib/StanfordCPPLib/foreach.h new file mode 100755 index 0000000..15a758b --- /dev/null +++ b/labb8/lib/StanfordCPPLib/foreach.h @@ -0,0 +1,217 @@ +/* + * File: foreach.h + * --------------- + * This file defines the <code>foreach</code> keyword, which implements + * a substitute for the range-based <code>for</code> loop from C++11. + * All iterable classes in the Stanford libraries import this file, so + * clients don't ordinarily need to do so explicitly. This version of + * <code>foreach</code> also supports C++ strings and arrays. + */ + +#ifndef _foreach_h +#define _foreach_h + +/* + * Statement: foreach + * Usage: foreach (type var in collection) { ... } + * ----------------------------------------------- + * The <code>foreach</code> statement steps through the elements in + * a collection. It works correctly with the collection classes in + * both the Standard Template Library and the Stanford C++ libraries, + * but can also be used with C++ strings and statically initialized + * arrays. + * + * <p>The following code, for example, prints every element in the + * string vector <code>lines</code>: + * + *<pre> + * foreach (string str in lines) { + * cout << str << endl; + * } + *</pre> + * + * Similarly, the following function calculates the sum of the character + * codes in a string: + * + *<pre> + * int sumCharacterCodes(string str) { + * int sum = 0; + * foreach (char ch in str) sum += ch; + * return sum; + * } + *</pre> + * + * As a simplification when iterating over maps, the <code>foreach</code> + * macro iterates through the keys rather than the key/value pairs. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +#include <iterator> +#include <map> +#include <cstddef> +#include <cstring> + +/* These #includes are for files that contain "in" as a token */ + +#include <ios> +#include <fstream> +#include <sstream> +using namespace std; + +/* Redefine the ios constants (one of which is "in") */ + +static const ios::openmode IOS_APP = ios::app; +static const ios::openmode IOS_ATE = ios::ate; +static const ios::openmode IOS_BINARY = ios::binary; +static const ios::openmode IOS_IN = ios::in; +static const ios::openmode IOS_OUT = ios::out; +static const ios::openmode IOS_TRUNC = ios::trunc; + +/* Private implementation namespace */ + +namespace _fe { + struct Range { + virtual ~Range() { }; + }; + + template <typename T> + struct ArrayRange : Range { + ArrayRange(const T *begin, const T *end) : iter(begin), end(end) { } + const T *iter; + const T *end; + }; + + template <typename CType> + struct CRange : Range { + CRange(const CType& c) : + cont(c), iter(cont.begin()), end(cont.end()) { } + CType cont; + typename CType::iterator iter, end; + }; + + template <typename KT, typename VT, typename CT, typename AT> + struct MapRange : Range { + MapRange(const map<KT,VT,CT,AT> & c) : + cont(c), iter(cont.begin()), end(cont.end()) { } + map<KT,VT,CT,AT> cont; + typename map<KT,VT,CT,AT>::iterator iter, end; + }; + +/* + * The State struct glues together all of these pieces and + * stores all of the information throughout the loops. + */ + + struct State { + State() : state(0), itr(NULL) { } + ~State() { delete itr; } + int state; + Range *itr; + }; + +/* General hook function */ + + template <typename DowncastType, typename ValueType> + ValueType HookImpl(State& fe) { + DowncastType *ip = (DowncastType *) fe.itr; + if (ip->iter == ip->end) { + fe.state = 2; + return ValueType(); + } + fe.state = 1; + ValueType vp = *ip->iter; /* Subtle implementation note: */ + ++ip->iter; /* Using *ip->iter++ here would */ + return vp; /* require copying the iterator. */ + } + +/* Foreach implementation for containers */ + + template <typename CType> + CRange<CType> *Init(State & fe, const CType & collection) { + fe.itr = new CRange<CType>(collection); + return (CRange<CType>*) fe.itr; + } + + template <typename CType> + typename iterator_traits<typename CType::iterator>::value_type + Hook(State & fe, CRange<CType> *) { + return HookImpl<CRange<CType>, + typename iterator_traits<typename CType::iterator>::value_type>(fe); + } + +/* For maps */ + + template <typename K, typename V, typename C, typename A> + MapRange<K,V,C,A> *Init(State & fe, const map<K,V,C,A> & collection) { + fe.itr = new MapRange<K,V,C,A>(collection); + return (MapRange<K,V,C,A>*) fe.itr; + } + + template <typename DowncastType, typename ValueType> + ValueType MapHookImpl(State & fe) { + DowncastType *ip = (DowncastType *) fe.itr; + if (ip->iter == ip->end) { + fe.state = 2; + return ValueType(); + } + fe.state = 1; + ValueType key = ip->iter->first; + ++ip->iter; + return key; + } + + template <typename K, typename V, typename C, typename A> + K Hook(State & fe, MapRange<K,V,C,A> *) { + return MapHookImpl<MapRange<K,V,C,A>,K>(fe); + } + +/* For C strings */ + + template <size_t n> + ArrayRange<char> *Init(State & fe, char (&str)[n]) { + fe.itr = new ArrayRange<char>(str, str + strlen(str)); + return (ArrayRange<char>*) fe.itr; + } + + template <size_t n> + ArrayRange<char> *Init(State & fe, const char (&str)[n]) { + fe.itr = new ArrayRange<char>(str, str + strlen(str)); + return (ArrayRange<char>*) fe.itr; + } + +/* For arrays */ + + template <typename T, size_t n> + ArrayRange<T> *Init(State & fe, T (&arr)[n]) { + fe.itr = new ArrayRange<T>(arr, arr + n); + return (ArrayRange<T>*) fe.itr; + } + + template <typename T, size_t n> + ArrayRange<T> *Init(State & fe, const T (&arr)[n]) { + fe.itr = new ArrayRange<T>(arr, arr + n); + return (ArrayRange<T>*) fe.itr; + } + + template <typename T> + T Hook(State& fe, ArrayRange<T>*) { + return HookImpl<ArrayRange<T>, T>(fe); + } + +} + +/* The actual foreach and in macros */ + +#define foreach(arg) \ + for (_fe::State _fe; _fe.state < 2; ) \ + for (arg)); _fe.state++ == 1; _fe.state = 0) + +#define in = _fe::Hook(_fe, _fe.state != 0 ? NULL : _fe::Init(_fe, + +#endif diff --git a/labb8/lib/StanfordCPPLib/gevents.cpp b/labb8/lib/StanfordCPPLib/gevents.cpp new file mode 100755 index 0000000..72043d6 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gevents.cpp @@ -0,0 +1,335 @@ +/* + * File: gevents.cpp + * ----------------- + * This file implements the machine-independent functions for the classes + * in the gevents.h interface. The actual functions for receiving events + * from the environment are implemented in the platform package. + */ + +/* + * Implementation notes: + * --------------------- + * The classes in this hierarchy are defined in an unusual way + * for C++ in that all instance variables are part of the top-level + * class. The advantage of this design is that the GEvent class + * then has all of the memory it needs to store an event of any of + * its subclasses. + */ + +#include <iostream> +#include <sstream> +#include <string> +#include <cctype> +#include "error.h" +#include "gevents.h" +#include "gtimer.h" +#include "gtypes.h" +#include "map.h" +#include "platform.h" +using namespace std; + +/* Global variables */ + +static Platform *pp = getPlatform(); + +/* Implementation of GEvent class */ + +GEvent::GEvent() { + eventClass = NULL_EVENT; + eventType = 0; + valid = false; + modifiers = 0; +} + +EventClassType GEvent::getEventClass() const { + return eventClass; +} + +EventType GEvent::getEventType() const { + return EventType(eventType); +} + +double GEvent::getEventTime() const { + return eventTime; +} + +int GEvent::getModifiers() const { + return modifiers; +} + +string GEvent::toString() const { + if (eventClass == 0) { + return "GEvent(NULL)"; + } else if (eventClass == WINDOW_EVENT) { + GWindowEvent windowEvent(*this); + return (&windowEvent)->toString(); + } else if (eventClass == ACTION_EVENT) { + GActionEvent actionEvent(*this); + return (&actionEvent)->toString(); + } else if (eventClass == MOUSE_EVENT) { + GMouseEvent mouseEvent(*this); + return (&mouseEvent)->toString(); + } else if (eventClass == KEY_EVENT) { + GKeyEvent keyEvent(*this); + return (&keyEvent)->toString(); + } else if (eventClass == TIMER_EVENT) { + GTimerEvent timerEvent(*this); + return (&timerEvent)->toString(); + } else { + return "GEvent(?)"; + } +} + +bool GEvent::isValid() { + return valid; +} + +void GEvent::setEventTime(double time) { + eventTime = time; +} + +void GEvent::setModifiers(int modifiers) { + this->modifiers = modifiers; +} + +GWindowEvent::GWindowEvent() { + valid = false; +} + +GWindowEvent::GWindowEvent(GEvent e) { + valid = e.valid && e.eventClass == WINDOW_EVENT; + if (valid) { + eventClass = e.eventClass; + eventType = e.eventType; + modifiers = e.modifiers; + eventTime = e.eventTime; + gwd = e.gwd; + } +} + +GWindowEvent::GWindowEvent(EventType type, const GWindow & gw) { + this->eventClass = WINDOW_EVENT; + this->eventType = int(type); + this->gwd = gw.gwd; + valid = true; +} + +GWindow GWindowEvent::getGWindow() const { + return GWindow(gwd); +} + +string GWindowEvent::toString() const { + if (!valid) return "GWindowEvent(?)"; + ostringstream os; + os << "GWindowEvent:"; + switch (eventType) { + case WINDOW_CLOSED: os << "WINDOW_CLOSED"; break; + case WINDOW_RESIZED: os << "WINDOW_RESIZED"; break; + } + os << "()"; + return os.str(); +} + +GActionEvent::GActionEvent() { + valid = false; +} + +GActionEvent::GActionEvent(GEvent e) { + valid = e.valid && e.eventClass == ACTION_EVENT; + if (valid) { + eventClass = e.eventClass; + eventType = e.eventType; + modifiers = e.modifiers; + eventTime = e.eventTime; + source = e.source; + actionCommand = e.actionCommand; + } +} + +GActionEvent::GActionEvent(EventType type, GObject *source, + string actionCommand) { + this->eventClass = ACTION_EVENT; + this->eventType = int(type); + this->source = source; + this->actionCommand = actionCommand; + valid = true; +} + +GObject *GActionEvent::getSource() const { + return source; +} + +string GActionEvent::getActionCommand() const { + if (!valid) error("getActionCommand: Event is not valid"); + return actionCommand; +} + +string GActionEvent::toString() const { + if (!valid) return "GActionEvent(?)"; + ostringstream os; + os << "GActionEvent:ACTION_PERFORMED(" << actionCommand << ")"; + return os.str(); +} + +GMouseEvent::GMouseEvent() { + valid = false; +} + +GMouseEvent::GMouseEvent(GEvent e) { + valid = e.valid && e.eventClass == MOUSE_EVENT; + if (valid) { + eventClass = e.eventClass; + eventType = e.eventType; + modifiers = e.modifiers; + eventTime = e.eventTime; + x = e.x; + y = e.y; + } +} + +GMouseEvent::GMouseEvent(EventType type, const GWindow & gw, + double x, double y) { + this->eventClass = MOUSE_EVENT; + this->eventType = int(type); + this->gwd = gw.gwd; + this->x = x; + this->y = y; + valid = true; +} + +GWindow GMouseEvent::getGWindow() const { + return GWindow(gwd); +} + +double GMouseEvent::getX() const { + if (!valid) error("getX: Event is not valid"); + return x; +} + +double GMouseEvent::getY() const { + if (!valid) error("getY: Event is not valid"); + return y; +} + +string GMouseEvent::toString() const { + if (!valid) return "GMouseEvent(?)"; + ostringstream os; + os << "GMouseEvent:"; + switch (eventType) { + case MOUSE_PRESSED: os << "MOUSE_PRESSED"; break; + case MOUSE_RELEASED: os << "MOUSE_RELEASED"; break; + case MOUSE_CLICKED: os << "MOUSE_CLICKED"; break; + case MOUSE_MOVED: os << "MOUSE_MOVED"; break; + case MOUSE_DRAGGED: os << "MOUSE_DRAGGED"; break; + } + os << "(" << x << ", " << y << ")"; + return os.str(); +} + +GKeyEvent::GKeyEvent() { + valid = false; +} + +GKeyEvent::GKeyEvent(GEvent e) { + this->eventClass = KEY_EVENT; + valid = e.valid && e.eventClass == KEY_EVENT; + if (valid) { + eventClass = e.eventClass; + eventType = e.eventType; + modifiers = e.modifiers; + eventTime = e.eventTime; + keyChar = e.keyChar; + keyCode = e.keyCode; + } +} + +GKeyEvent::GKeyEvent(EventType type, const GWindow & gw, + int keyChar, int keyCode) { + this->eventClass = KEY_EVENT; + this->eventType = int(type); + this->gwd = gw.gwd; + this->keyChar = keyChar; + this->keyCode = keyCode; + valid = true; +} + +GWindow GKeyEvent::getGWindow() const { + return GWindow(gwd); +} + +char GKeyEvent::getKeyChar() const { + if (!valid) error("getKey: Event is not valid"); + return char(keyChar); + // Think about wide characters at some point +} + +int GKeyEvent::getKeyCode() const { + if (!valid) error("getKey: Event is not valid"); + return keyCode; +} + +string GKeyEvent::toString() const { + if (!valid) return "GKeyEvent(?)"; + ostringstream os; + os << "GKeyEvent:"; + int ch = '\0'; + switch (eventType) { + case KEY_PRESSED: os << "KEY_PRESSED"; ch = keyCode; break; + case KEY_RELEASED: os << "KEY_RELEASED"; ch = keyCode; break; + case KEY_TYPED: os << "KEY_TYPED"; ch = keyChar; break; + } + if (isprint(ch)) { + os << "('" << char(ch) << "')"; + } else { + os << oct << "('\\" << ch << "')"; + } + return os.str(); +} + +/* Timer events */ + +GTimerEvent::GTimerEvent() { + valid = false; +} + +GTimerEvent::GTimerEvent(GEvent e) { + valid = e.valid && e.eventClass == TIMER_EVENT; + if (valid) { + eventClass = e.eventClass; + eventType = e.eventType; + modifiers = e.modifiers; + eventTime = e.eventTime; + gtd = e.gtd; + } +} + +GTimerEvent::GTimerEvent(EventType type, const GTimer & timer) { + this->eventClass = TIMER_EVENT; + this->eventType = int(type); + this->gtd = timer.gtd; + valid = true; +} + +GTimer GTimerEvent::getGTimer() const { + return GTimer(gtd); +} + +string GTimerEvent::toString() const { + if (!valid) return "GTimerEvent(?)"; + return "GTimerEvent:TIMER_TICKED()"; +} + +/* Global event handlers */ + +void waitForClick() { + waitForEvent(CLICK_EVENT); +} + +GEvent waitForEvent(int mask) { + return pp->waitForEvent(mask); +} + +GEvent getNextEvent(int mask) { + return pp->getNextEvent(mask); +} + diff --git a/labb8/lib/StanfordCPPLib/gevents.h b/labb8/lib/StanfordCPPLib/gevents.h new file mode 100755 index 0000000..49717f9 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gevents.h @@ -0,0 +1,734 @@ +/* + * File: gevents.h + * --------------- + * This file defines the event types used in the StanfordCPPLib + * graphics libraries. The structure of this package is adapted from + * the Java event model. + * <include src="pictures/ClassHierarchies/GEventHierarchy-h.html"> + */ + +#ifndef _gevents_h +#define _gevents_h + +#include <string> +#include "gtimer.h" +#include "gwindow.h" + +/* + * Type: EventClassType + * -------------------- + * This enumeration type defines the event classes. The element values + * are each a single bit and can be added or ORed together to generate + * an event mask. The <code>CLICK_EVENT</code> class responds only to + * the MOUSE_CLICKED event type. The <code>ANY_EVENT</code> class + * selects any event. + */ + +enum EventClassType { + NULL_EVENT = 0x000, + ACTION_EVENT = 0x010, + KEY_EVENT = 0x020, + TIMER_EVENT = 0x040, + WINDOW_EVENT = 0x080, + MOUSE_EVENT = 0x100, + CLICK_EVENT = 0x200, + ANY_EVENT = 0x3F0 +}; + +/* + * Type: EventType + * --------------- + * This enumeration type defines the event types for all events. + */ + +typedef enum { + WINDOW_CLOSED = WINDOW_EVENT + 1, + WINDOW_RESIZED = WINDOW_EVENT + 2, + ACTION_PERFORMED = ACTION_EVENT + 1, + MOUSE_CLICKED = MOUSE_EVENT + 1, + MOUSE_PRESSED = MOUSE_EVENT + 2, + MOUSE_RELEASED = MOUSE_EVENT + 3, + MOUSE_MOVED = MOUSE_EVENT + 4, + MOUSE_DRAGGED = MOUSE_EVENT + 5, + KEY_PRESSED = KEY_EVENT + 1, + KEY_RELEASED = KEY_EVENT + 2, + KEY_TYPED = KEY_EVENT + 3, + TIMER_TICKED = TIMER_EVENT + 1, +} EventType; + +/* + * Type: ModifierCodes + * ------------------- + * This enumeration type defines a set of constants used to check whether + * modifiers are in effect. + */ + +enum ModifierCodes { + SHIFT_DOWN = 1 << 0, + CTRL_DOWN = 1 << 1, + META_DOWN = 1 << 2, + ALT_DOWN = 1 << 3, + ALT_GRAPH_DOWN = 1 << 4, + BUTTON1_DOWN = 1 << 5, + BUTTON2_DOWN = 1 << 6, + BUTTON3_DOWN = 1 << 7 +}; + +/* + * Type: KeyCodes + * -------------- + * This type defines the names of the key codes returned in a key event. + */ + +enum KeyCodes { + BACKSPACE_KEY = 8, + TAB_KEY = 9, + ENTER_KEY = 10, + CLEAR_KEY = 12, + ESCAPE_KEY = 27, + PAGE_UP_KEY = 33, + PAGE_DOWN_KEY = 34, + END_KEY = 35, + HOME_KEY = 36, + LEFT_ARROW_KEY = 37, + UP_ARROW_KEY = 38, + RIGHT_ARROW_KEY = 39, + DOWN_ARROW_KEY = 40, + F1_KEY = 112, + F2_KEY = 113, + F3_KEY = 114, + F4_KEY = 115, + F5_KEY = 116, + F6_KEY = 117, + F7_KEY = 118, + F8_KEY = 119, + F9_KEY = 120, + F10_KEY = 121, + F11_KEY = 122, + F12_KEY = 123, + DELETE_KEY = 127, + HELP_KEY = 156 +}; + +/* Forward definitions */ + +class GWindowEvent; +class GActionEvent; +class GMouseEvent; +class GKeyEvent; +class GTimerEvent; +class GObject; + +/* + * Class: GEvent + * ------------- + * This class is the root of the hierarchy for all events. + * <include src="pictures/ClassHierarchies/GEventHierarchy.html"> + * + * <p>The standard paradigm for using <code>GEvent</code> is illustrated + * by the following program, which allows the user to draw lines on the + * graphics window: + * + *<pre> + * int main() { + * GWindow gw; + * GLine *line; + * cout << "This program lets the user draw lines by dragging." << endl; + * while (true) { + * GMouseEvent e = waitForEvent(MOUSE_EVENT); + * if (e.getEventType() == MOUSE_PRESSED) { + * line = new GLine(e.getX(), e.getY(), e.getX(), e.getY()); + * gw.add(line); + * } else if (e.getEventType() == MOUSE_DRAGGED) { + * line->setEndPoint(e.getX(), e.getY()); + * } + * } + * } + *</pre> + */ + +class GEvent { + +public: + +/* + * Friend constructor: GEvent + * Usage: GEvent event; + * -------------------- + * Ensures that an event is properly initialized to a <code>NULL</code> event. + */ + + GEvent(); + +/* + * Method: getEventClass + * Usage: EventClassType eventClass = e.getEventClass(); + * ----------------------------------------------------- + * Returns the enumerated type constant indicating the class of the + * event. + */ + + EventClassType getEventClass() const; + +/* + * Method: getEventType + * Usage: EventType type = e.getEventType(); + * ----------------------------------------- + * Returns the enumerated type constant corresponding to the specific + * event type. + */ + + EventType getEventType() const; + +/* + * Method: getEventTime + * Usage: double time = e.getEventTime(); + * -------------------------------------- + * Returns the system time in milliseconds at which the event occurred. + * To ensure portability among systems that represent time in different + * ways, the StanfordCPPLib packages use type <code>double</code> to + * represent time, which is always encoded as the number of milliseconds + * that have elapsed since 00:00:00 UTC on January 1, 1970, which is + * the conventional zero point for computer-based time systems. + */ + + double getEventTime() const; + +/* + * Method: getModifiers + * Usage: int modifiers = e.getModifiers(); + * ---------------------------------------- + * Returns an integer whose bits indicate what modifiers are in effect. + * To check whether the shift key is down, for example, one could use + * the following code: + * + *<pre> + * if (e.getModifiers() & SHIFT_DOWN) ... + *</pre> + */ + + int getModifiers() const; + +/* + * Method: toString + * Usage: string str = e.toString(); + * --------------------------------- + * Converts the event to a human-readable representation of the event. + */ + + virtual std::string toString() const; + +/* + * Method: isValid + * Usage: if (e.isValid()) ... + * --------------------------- + * Returns <code>true</code> if the event is valid. + */ + + bool isValid(); + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Method: setEventTime + * Usage: e.setEventTime(time); + * ---------------------------- + * Sets the event time field for this event. The event system needs + * access to this method, but conventional clients don't. + */ + + void setEventTime(double time); + +/* + * Method: setModifiers + * Usage: e.setModifiers(modifiers); + * --------------------------------- + * Sets the modifiers field for this event. The event system needs + * access to this method, but conventional clients don't. + */ + + void setModifiers(int modifiers); + +private: + +/* + * Instance variables + * ------------------ + * Implementation note: All the variables from the subclasses are included + * in the outer class to make it possible to convert between general events + * and the various subclasses. By keeping all event classes the same size, + * this design avoids any issues of slicing off parts of the data during + * such conversions. + */ + +/* General events */ + + EventClassType eventClass; + int eventType; + int modifiers; + double eventTime; + bool valid; + std::string sourceKey; + +/* Window, mouse, and key events */ + + GWindowData *gwd; + +/* Action events */ + + GObject *source; + std::string actionCommand; + +/* Mouse events */ + + double x; + double y; + +/* Key events */ + + int keyChar; + int keyCode; + +/* Timer events */ + + GTimerData *gtd; + +/* Friend specifications */ + +friend class GWindowEvent; +friend class GActionEvent; +friend class GMouseEvent; +friend class GKeyEvent; +friend class GTimerEvent; + +}; + +/* + * Function: waitForClick + * Usage: waitForClick(); + * ---------------------- + * Waits for a mouse click in any window, discarding any other events. + */ + +void waitForClick(); + +/* + * Function: waitForEvent + * Usage: GEvent e = waitForEvent(mask); + * ------------------------------------- + * Dismisses the process until an event occurs whose type is covered by + * the event mask. The mask parameter is a combination of the events of + * interest. For example, to wait for a mouse event or an action event, + * clients can use the following call: + * + *<pre> + * e = waitForEvent(MOUSE_EVENT + ACTION_EVENT); + *</pre> + * + * The <code>mask</code> parameter is optional. If it is missing, + * <code>waitForEvent</code> accepts any event. + * + * <p>As a more sophisticated example, the following code is the canonical + * event loop for an animated application that needs to respond to mouse, + * key, and timer events: + * + *<pre> + * GTimer timer(ANIMATION_DELAY_IN_MILLISECONDS); + * timer.start(); + * while (true) { + * GEvent e = waitForEvent(TIMER_EVENT + MOUSE_EVENT + KEY_EVENT); + * switch (e.getEventClass()) { + * case TIMER_EVENT: + * takeAnimationStep(); + * break; + * case MOUSE_EVENT: + * handleMouseEvent(GMouseEvent(e)); + * break; + * case KEY_EVENT: + * handleKeyEvent(GKeyEvent(e)); + * break; + * } + * } + *</pre> + */ + +GEvent waitForEvent(int mask = ANY_EVENT); + +/* + * Function: getNextEvent + * Usage: GEvent e = getNextEvent(mask); + * ------------------------------------- + * Checks to see if there are any events of the desired type waiting on the + * event queue. If so, this function returns the event in exactly the same + * fashion as <code>waitForEvent</code>; if not, <code>getNextEvent</code> + * returns an invalid event. The <code>mask</code> parameter is optional. + * If it is missing, <code>getNextEvent</code> accepts any event. + */ + +GEvent getNextEvent(int mask = ANY_EVENT); + +/* + * Class: GWindowEvent + * ------------------- + * This event subclass represents a window event. + * Each <code>GWindowEvent</code> keeps track of the event type + * (<code>WINDOW_CLOSED</code>, <code>WINDOW_RESIZED</code>) along + * with the identity of the window. + */ + +class GWindowEvent : public GEvent { + +public: + +/* + * Constructor: GWindowEvent + * Usage: GWindowEvent windowEvent(type, gw); + * ------------------------------------------ + * Creates a <code>GWindowEvent</code> using the specified parameters. + */ + + GWindowEvent(EventType type, const GWindow & gw); + +/* + * Method: getGWindow + * Usage: GWindow gw = e.getGWindow(); + * ----------------------------------- + * Returns the graphics window in which this event occurred. + */ + + GWindow getGWindow() const; + +/* + * Method: toString + * Usage: string str = e.toString(); + * --------------------------------- + * Converts the event to a human-readable representation of the event. + */ + + std::string toString() const; + +/* Private section */ + + GWindowEvent(); + GWindowEvent(GEvent e); + +}; + +/* + * Class: GActionEvent + * ------------------- + * This event subclass represents an action event. + * Action events are generated by the classes in the + * <a href="GInteractor-class.html"><code>GInteractor</code></a> + * hierarchy. As an example, the following program displays + * a button that, when pushed, generates the message + * “Please do not press this button again” + * (with thanks to Douglas Adams’s <i>Hitchhiker’s + * Guide to the Galaxy</i>): + * + *<pre> + * int main() { + * GWindow gw; + * GButton *button = new GButton("RED"); + * gw.addToRegion(button, "SOUTH"); + * while (true) { + * GEvent e = waitForEvent(ACTION_EVENT | CLICK_EVENT); + * if (e.getEventType() == MOUSE_CLICKED) break; + * cout << "Please do not press this button again." << endl; + * } + * return 0; + * } + *</pre> + */ + +class GActionEvent : public GEvent { + +public: + +/* + * Constructor: GActionEvent + * Usage: GActionEvent actionEvent(type, source, actionCommand); + * ------------------------------------------------------------- + * Creates a <code>GActionEvent</code> using the specified parameters. + */ + + GActionEvent(EventType type, GObject *source, std::string actionCommand); + +/* + * Method: getSource + * Usage: GObject *gobj = e.getSource(); + * ------------------------------------- + * Returns a pointer to the <code>GObject</code> that generated this event. + */ + + GObject *getSource() const; + +/* + * Method: getActionCommand + * Usage: string cmd = e.getActionCommand(); + * ----------------------------------------- + * Returns the action command associated with this event. + */ + + std::string getActionCommand() const; + +/* + * Method: toString + * Usage: string str = e.toString(); + * --------------------------------- + * Converts the event to a human-readable representation of the event. + */ + + std::string toString() const; + +/* Private section */ + + GActionEvent(); + GActionEvent(GEvent e); + +}; + +/* + * Class: GMouseEvent + * ------------------ + * This event subclass represents a mouse event. Each mouse event + * records the event type (<code>MOUSE_PRESSED</code>, + * <code>MOUSE_RELEASED</code>, <code>MOUSE_CLICKED</code>, + * <code>MOUSE_MOVED</code>, <code>MOUSE_DRAGGED</code>) along + * with the coordinates of the event. Clicking the mouse generates + * three events in the following order: <code>MOUSE_PRESSED</code>, + * <code>MOUSE_RELEASED</code>, <code>MOUSE_CLICKED</code>. + * + * <p>As an example, the following program uses mouse events to let + * the user draw rectangles on the graphics window. The only + * complexity in this code is the use of the library functions + * <code>min</code> and <code>abs</code> to ensure that the + * dimensions of the rectangle are positive. + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program lets the user draw rectangles." << endl; + * GRect *rect; + * double startX; + * double startY; + * while (true) { + * GMouseEvent e = waitForEvent(); + * if (e.getEventType() == MOUSE_PRESSED) { + * startX = e.getX(); + * startY = e.getY(); + * rect = new GRect(startX, startY, 0, 0); + * rect->setFilled(true); + * gw.add(rect); + * } else if (e.getEventType() == MOUSE_DRAGGED) { + * double x = min(e.getX(), startX); + * double y = min(e.getY(), startY); + * double width = abs(e.getX() - startX); + * double height = abs(e.getY() - startY); + * rect->setBounds(x, y, width, height); + * } + * } + * } + *</pre> + */ + +class GMouseEvent : public GEvent { + +public: + +/* + * Constructor: GMouseEvent + * Usage: GMouseEvent mouseEvent(type, gw, x, y); + * ---------------------------------------------- + * Creates a <code>GMouseEvent</code> using the specified parameters. + */ + + GMouseEvent(EventType type, const GWindow & gw, double x, double y); + +/* + * Method: getGWindow + * Usage: GWindow gw = e.getGWindow(); + * ----------------------------------- + * Returns the graphics window in which this event occurred. + */ + + GWindow getGWindow() const; + +/* + * Method: getX + * Usage: double x = getX(); + * ------------------------- + * Returns the <i>x</i> coordinate at which the event occurred relative + * to the window origin at the upper left corner of the window. + */ + + double getX() const; + +/* + * Method: getY + * Usage: double y = getY(); + * ------------------------- + * Returns the <i>y</i> coordinate at which the event occurred relative + * to the window origin at the upper left corner of the window. + */ + + double getY() const; + +/* + * Method: toString + * Usage: string str = e.toString(); + * --------------------------------- + * Converts the event to a human-readable representation of the event. + */ + + std::string toString() const; + +/* Private section */ + + GMouseEvent(); + GMouseEvent(GEvent e); + +}; + +/* + * Class: GKeyEvent + * ---------------- + * This event subclass represents a key event. Each key event records + * the event type along with two representations of the key. The + * <code>getKeyChar</code> function is more generally useful and + * returns the character after taking into account modifier keys. + * The <code>getKeyCode</code> function returns an integer identifying + * the key, which can be a function key as well as a standard key. + * The codes return by <code>getKeyCode</code> are listed in the + * <code>KeyCodes</code> enumeration. + */ + +class GKeyEvent : public GEvent { + +public: + +/* + * Constructor: GKeyEvent + * Usage: GKeyEvent keyEvent(type, gw, keyChar, keyCode); + * ------------------------------------------------------ + * Creates a <code>GKeyEvent</code> using the specified parameters. + */ + + GKeyEvent(EventType type, const GWindow & gw, int keyChar, int keyCode); + +/* + * Method: getGWindow + * Usage: GWindow gw = e.getGWindow(); + * ----------------------------------- + * Returns the graphics window in which this event occurred. + */ + + GWindow getGWindow() const; + +/* + * Method: getKeyChar + * Usage: char ch = e.getKeyChar(); + * -------------------------------- + * Returns the character represented by the keystroke, taking the modifier + * keys into account. For example, if the user types the <code>'a'</code> + * key with the shift key down, <code>getKeyChar</code> will return + * <code>'A'</code>. If the key code in the event does not correspond + * to a character, <code>getKeyChar</code> returns the null character. + */ + + char getKeyChar() const; + +/* + * Method: getKeyCode + * Usage: int key = getKeyCode(); + * ------------------------------ + * Returns the integer code associated with the key in the event. + */ + + int getKeyCode() const; + +/* + * Method: toString + * Usage: string str = e.toString(); + * --------------------------------- + * Converts the event to a human-readable representation of the event. + */ + + std::string toString() const; + +/* Private section */ + + GKeyEvent(); + GKeyEvent(GEvent e); + +}; + +/* + * Class: GTimerEvent + * ------------------ + * This event subclass represents a timer event. Timer events are + * generated by a <a href="GTimer-class.html"><code>GTimer</code></a> + * object, which produces a new event at a fixed interval measured in + * milliseconds. As an example, the following program generates a + * timer event every two seconds, stopping when the user clicks + * somewhere in the window: + * + *<pre> + * int main() { + * cout << "This program generates timer events." << endl; + * GTimer timer(2000); + * timer.start(); + * while (true) { + * GEvent e = waitForEvent(CLICK_EVENT | TIMER_EVENT); + * if (e.getEventType() == MOUSE_CLICKED) break; + * cout << "Timer ticked" << endl; + * } + * return 0; + * } + *</pre> + */ + +class GTimerEvent : public GEvent { + +public: + +/* + * Constructor: GTimerEvent + * Usage: GTimerEvent timerEvent(type, timer); + * ------------------------------------------- + * Creates a <code>GTimerEvent</code> for the specified timer. + */ + + GTimerEvent(EventType type, const GTimer & timer); + +/* + * Method: getGTimer + * Usage: GTimer timer = e.getGTimer(); + * ------------------------------------ + * Returns the timer that generated this event. + */ + + GTimer getGTimer() const; + +/* + * Method: toString + * Usage: string str = e.toString(); + * --------------------------------- + * Converts the event to a human-readable representation of the event. + */ + + std::string toString() const; + +/* Private section */ + + GTimerEvent(); + GTimerEvent(GEvent e); + +}; + +#endif diff --git a/labb8/lib/StanfordCPPLib/ginteractors.cpp b/labb8/lib/StanfordCPPLib/ginteractors.cpp new file mode 100755 index 0000000..8075372 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/ginteractors.cpp @@ -0,0 +1,204 @@ +/* + * File: gobjects.cpp + * ------------------ + * This file implements the ginteractors.h interface. + */ + +#include <iostream> +#include <sstream> +#include "gevents.h" +#include "ginteractors.h" +#include "gobjects.h" +#include "gtypes.h" +#include "gwindow.h" +#include "platform.h" + +static Platform *pp = getPlatform(); + +/* + * Implementation notes: GInteractor class + * --------------------------------------- + */ + +GInteractor::GInteractor() { + actionCommand = ""; +} + +void GInteractor::setActionCommand(string cmd) { + actionCommand = cmd; + pp->setActionCommand(this, cmd); +} + +string GInteractor::getActionCommand() { + return actionCommand; +} + +void GInteractor::setSize(const GDimension & size) { + setSize(size.getWidth(), size.getHeight()); +} + +void GInteractor::setSize(double width, double height) { + pp->setSize(this, width, height); +} + +void GInteractor::setBounds(const GRectangle & rect) { + setLocation(rect.getX(), rect.getY()); + setSize(rect.getWidth(), rect.getHeight()); +} + +void GInteractor::setBounds(double x, double y, double width, double height) { + setLocation(x, y); + setSize(width, height); +} + +GRectangle GInteractor::getBounds() const { + GDimension size = pp->getSize((GObject *) this); + return GRectangle(x, y, size.getWidth(), size.getHeight()); +} + +/* + * Implementation notes: GButton class + * ----------------------------------- + */ + +GButton::GButton(string label) { + this->label = label; + pp->createGButton(this, label); +} + +string GButton::getType() const { + return "GButton"; +} + +string GButton::toString() const { + ostringstream oss; + oss << "GButton(\"" << label << "\")"; + return oss.str(); +} + +/* + * Implementation notes: GCheckBox class + * ------------------------------------- + */ + +GCheckBox::GCheckBox(string label) { + this->label = label; + pp->createGCheckBox(this, label); +} + +bool GCheckBox::isSelected() { + return pp->isSelected(this); +} + +void GCheckBox::setSelected(bool state) { + pp->setSelected(this, state); +} + +string GCheckBox::getType() const { + return "GCheckBox"; +} + +string GCheckBox::toString() const { + ostringstream oss; + oss << "GCheckBox(\"" << label << "\")"; + return oss.str(); +} + +/* + * Implementation notes: GSlider class + * ----------------------------------- + */ + +GSlider::GSlider() { + create(0, 100, 50); +} + +GSlider::GSlider(int min, int max, int value) { + create(min, max, value); +} + +int GSlider::getValue() { + return pp->getValue(this); +} + +void GSlider::setValue(int value) { + pp->setValue(this, value); +} + +string GSlider::getType() const { + return "GSlider"; +} + +string GSlider::toString() const { + ostringstream oss; + oss << "GSlider()"; + return oss.str(); +} + +void GSlider::create(int min, int max, int value) { + this->min = min; + this->max = max; + pp->createGSlider(this, min, max, value); +} + +/* + * Implementation notes: GTextField class + * -------------------------------------- + */ + +GTextField::GTextField() { + pp->createGTextField(this, 10); +} + +GTextField::GTextField(int nChars) { + pp->createGTextField(this, nChars); +} + +string GTextField::getText() { + return pp->getText(this); +} + +void GTextField::setText(string str) { + pp->setText(this, str); +} + +string GTextField::getType() const { + return "GTextField"; +} + +string GTextField::toString() const { + ostringstream oss; + oss << "GTextField()"; + return oss.str(); +} + +/* + * Implementation notes: GChooser class + * ------------------------------------ + */ + +GChooser::GChooser() { + pp->createGChooser(this); +} + +void GChooser::addItem(string item) { + pp->addItem(this, item); +} + +string GChooser::getSelectedItem() { + return pp->getSelectedItem(this); +} + +void GChooser::setSelectedItem(string item) { + pp->setSelectedItem(this, item); +} + +string GChooser::getType() const { + return "GChooser"; +} + +string GChooser::toString() const { + ostringstream oss; + oss << "GChooser()"; + return oss.str(); +} diff --git a/labb8/lib/StanfordCPPLib/ginteractors.h b/labb8/lib/StanfordCPPLib/ginteractors.h new file mode 100755 index 0000000..f439324 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/ginteractors.h @@ -0,0 +1,370 @@ +/* + * File: ginteractors.h + * -------------------- + * This file exports a hierarchy of graphical interactors similar to those + * provided in the Java Swing libraries. + * <include src="pictures/ClassHierarchies/GInteractorHierarchy-h.html"> + */ + +#ifndef _ginteractors_h +#define _ginteractors_h + +#include "gobjects.h" +#include "gtypes.h" +#include "gwindow.h" +#include "vector.h" + +/* + * Class: GInteractor + * ------------------ + * This abstract class is the superclass for all graphical interactors. + * In most applications, interactors will be added to a control strip + * along one of the sides of the <code>GWindow</code>, but they can + * also be placed in specific positions just like any other + * <code>GObject</code>. + * <include src="pictures/ClassHierarchies/GInteractorHierarchy.html"> + */ + +class GInteractor : public GObject { + +public: + +/* + * Method: setActionCommand + * Usage: interactor.setActionCommand(cmd); + * ---------------------------------------- + * Sets the action command to the indicated string. If the string is not + * empty, activating the interactor generates a <code>GActionEvent</code>. + */ + + void setActionCommand(std::string cmd); + +/* + * Method: getActionCommand + * Usage: string cmd = interactor.getActionCommand(); + * -------------------------------------------------- + * Returns the action command associated with the interactor. + */ + + std::string getActionCommand(); + +/* + * Method: setSize + * Usage: interactor.setSize(size); + * interactor.setSize(width, height); + * ----------------------------------------- + * Changes the size of the interactor to the specified width and height. + */ + + void setSize(const GDimension & size); + void setSize(double width, double height); + +/* + * Method: setBounds + * Usage: interactor.setBounds(rect); + * interactor.setBounds(x, y, width, height); + * ------------------------------------------------- + * Changes the bounds of the interactor to the specified values. + */ + + void setBounds(const GRectangle & size); + void setBounds(double x, double y, double width, double height); + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + +protected: + + GInteractor(); + + std::string actionCommand; + +}; + +/* + * Class: GButton + * -------------- + * This interactor subclass represents an onscreen button. The following + * program displays a button that, when pressed, generates the message + * “Please do not press this button again” + * (with thanks to Douglas Adams’s <i>Hitchhiker’s + * Guide to the Galaxy</i>): + * + *<pre> + * int main() { + * GWindow gw; + * GButton *button = new GButton("RED"); + * gw.addToRegion(button, "SOUTH"); + * while (true) { + * GEvent e = waitForEvent(ACTION_EVENT | CLICK_EVENT); + * if (e.getEventType() == MOUSE_CLICKED) break; + * cout << "Please do not press this button again." << endl; + * } + * return 0; + * } + *</pre> + */ + +class GButton : public GInteractor { + +public: + +/* + * Constructor: GButton + * Usage: GButton *button = new GButton(label); + * -------------------------------------------- + * Creates a <code>GButton</code> with the specified label. This + * constructor also sets the action command for the button to the + * label string. + */ + + GButton(std::string label); + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +private: + std::string label; + +}; + +/* + * Class: GCheckBox + * ---------------- + * This interactor subclass represents an onscreen check box. Clicking + * once on the check box selects it; clicking again removes the selection. + * If a <code>GCheckBox</code> has an action command, clicking on the box + * generates a <code>GActionEvent</code>. + * <include src="pictures/GInteractorDiagrams/GCheckBox.html"> + */ + +class GCheckBox : public GInteractor { + +public: + +/* + * Constructor: GCheckBox + * Usage: GCheckBox *chkbox = new GCheckBox(label); + * ------------------------------------------------ + * Creates a <code>GCheckBox</code> with the specified label. In contrast + * to the <code>GButton</code> constructor, this constructor does not set + * an action command. + */ + + GCheckBox(std::string label); + +/* + * Method: setSelected + * Usage: chkbox->setSelected(state); + * ---------------------------------- + * Sets the state of the check box. + */ + + void setSelected(bool state); + +/* + * Method: isSelected + * Usage: if (chkbox->isSelected()) ... + * ------------------------------------ + * Returns <code>true</code> if the check box is selected. + */ + + bool isSelected(); + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +private: + std::string label; + +}; + +/* + * Class: GSlider + * -------------- + * This interactor subclass represents an onscreen slider. Dragging + * the slider control generates an <code>ActionEvent</code> if the + * slider has a nonempty action command. + * <include src="pictures/GInteractorDiagrams/GSlider.html"> + */ + +class GSlider : public GInteractor { + +public: + +/* + * Constructor: GSlider + * Usage: GSlider *slider = new GSlider(); + * GSlider *slider = new GSlider(min, max, value); + * ------------------------------------------------------ + * Creates a horizontal <code>GSlider</code>. The second form allows + * the client to specify the minimum value, maximum value, and current + * value of the slider. The first form is equivalent to calling + * <code>GSlider(0, 100, 50)</code>. Assigning an action command + * to the slider causes the slider to generate an action event whenever + * the slider value changes. + */ + + GSlider(); + GSlider(int min, int max, int value); + +/* + * Method: setValue + * Usage: slider->setValue(value); + * ------------------------------- + * Sets the current value of the slider. + */ + + void setValue(int value); + +/* + * Method: getValue + * Usage: int value = slider->getValue(); + * -------------------------------------- + * Returns the current value of the slider. + */ + + int getValue(); + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +private: + void create(int min, int max, int value); + int min; + int max; + +}; + +/* + * Class: GTextField + * ----------------- + * This interactor subclass represents a text field for entering short + * text strings. Hitting enter in a text field generates a + * <code>GActionEvent</code> if the text field has a nonempty action command. + + */ + +class GTextField : public GInteractor { + +public: + +/* + * Constructor: GTextField + * Usage: GTextField *field = new GTextField(); + * GTextField *field = new GTextField(nChars); + * -------------------------------------------------- + * Creates a text field capable of holding <code>nChars</code> characters, + * which defaults to 10. Assigning an action command to the text field + * causes it to generate an action event whenever the user types the + * ENTER key. + */ + + GTextField(); + GTextField(int nChars); + +/* + * Method: setText + * Usage: field->setText(str); + * --------------------------- + * Sets the text of the field to the specified string. + */ + + void setText(std::string str); + +/* + * Method: getText + * Usage: string str = field->getText(); + * ------------------------------------- + * Returns the contents of the text field. + */ + + std::string getText(); + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +}; + +/* + * Class: GChooser + * --------------- + * This interactor subclass represents a selectable list. The + * <code>GChooser</code> constructor creates an empty chooser. + * Once the chooser has been created, clients can use <code>addItem</code> + * to add the options. For example, the following code creates a + * <code>GChooser</code> containing the four strings + * <code>"Small"</code>, <code>"Medium"</code>, <code>"Large"</code>, + * and <code>"X-Large"</code>: + * + *<pre> + * GChooser *sizeChooser = new GChooser(); + * sizeChooser->addItem("Small"); + * sizeChooser->addItem("Medium"); + * sizeChooser->addItem("Large"); + * sizeChooser->addItem("X-Large"); + *</pre> + *<include src="pictures/GInteractorDiagrams/GChooser.html"> + */ + +class GChooser : public GInteractor { + +public: + +/* + * Constructor: GChooser + * Usage: GChooser *chooser = new GChooser(); + * ------------------------------------------ + * Creates a chooser that initially contains no items, which are added + * using the <code>addItem</code> method. Assigning an action command + * to the chooser causes it to generate an action event whenever the + * user selects an item. + */ + + GChooser(); + +/* + * Method: addItem + * Usage: chooser->addItem(item); + * ------------------------------ + * Adds a new item consisting of the specified string. + */ + + void addItem(std::string item); + +/* + * Method: setSelectedItem + * Usage: chooser->setSelectedItem(item); + * -------------------------------------- + * Sets the chooser so that it shows the specified item. If the item + * does not exist in the chooser, no change occurs. + */ + + void setSelectedItem(std::string item); + +/* + * Method: getSelectedItem + * Usage: string item = chooser->getSelectedItem(); + * ------------------------------------------------ + * Returns the current item selected in the chooser. + */ + + std::string getSelectedItem(); + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +}; + +#endif diff --git a/labb8/lib/StanfordCPPLib/gmath.cpp b/labb8/lib/StanfordCPPLib/gmath.cpp new file mode 100755 index 0000000..a5ff934 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gmath.cpp @@ -0,0 +1,50 @@ +/* + * File: gmath.cpp + * --------------- + * This file implements the gmath.h interface. In all cases, the + * implementation for each function requires only one line of code, + * which makes detailed documentation unnecessary. + */ + +#include <cmath> +#include "gmath.h" +#include "gtypes.h" + +extern const double PI = 3.14159265358979323846; +extern const double E = 2.71828182845904523536; + +double sinDegrees(double angle) { + return sin(toRadians(angle)); +} + +double cosDegrees(double angle) { + return cos(toRadians(angle)); +} + +double tanDegrees(double angle) { + return tan(toRadians(angle)); +} + +double toDegrees(double radians) { + return radians * 180 / PI; +} + +double toRadians(double degrees) { + return degrees * PI / 180; +} + +double vectorDistance(const GPoint & pt) { + return vectorDistance(pt.getX(), pt.getY()); +} + +double vectorDistance(double x, double y) { + return sqrt(x * x + y * y); +} + +double vectorAngle(const GPoint & pt) { + return vectorAngle(pt.getX(), pt.getY()); +} + +double vectorAngle(double x, double y) { + return (x == 0 && y == 0) ? 0 : toDegrees(atan2(-y, x)); +} diff --git a/labb8/lib/StanfordCPPLib/gmath.h b/labb8/lib/StanfordCPPLib/gmath.h new file mode 100755 index 0000000..7dc8aab --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gmath.h @@ -0,0 +1,104 @@ +/* + * File: gmath.h + * ------------- + * This file exports several functions for working with graphical + * geometry along with the mathematical constants <code>PI</code> + * and <code>E</code>. + */ + +#ifndef _gmath_h +#define _gmath_h + +#include "gtypes.h" + +/* + * Constant: PI + * ------------ + * The mathematical constant pi, which is the ratio of the circumference + * of a circle to its diameter. + */ + +extern const double PI; + +/* + * Constant: E + * ----------- + * The mathematical constant e, which is the base of natural logarithms. + */ + +extern const double E; + +/* + * Function: sinDegrees + * Usage: double sine = sinDegrees(angle); + * --------------------------------------- + * Returns the trigonometric sine of <code>angle</code>, which is + * expressed in degrees. + */ + +double sinDegrees(double angle); + +/* + * Function: cosDegrees + * Usage: double cosine = cosDegrees(angle); + * ----------------------------------------- + * Returns the trigonometric cosine of <code>angle</code>, which is + * expressed in degrees. + */ + +double cosDegrees(double angle); + +/* + * Function: tanDegrees + * Usage: double tangent = tanDegrees(angle); + * ------------------------------------------ + * Returns the trigonometric tangent of <code>angle</code>, which is + * expressed in degrees. + */ + +double tanDegrees(double angle); + +/* + * Function: toDegrees + * Usage: double degrees = toDegrees(radians); + * ------------------------------------------- + * Converts an angle from radians to degrees. + */ + +double toDegrees(double radians); + +/* + * Function: toRadians + * Usage: double radians = toRadians(degrees); + * ------------------------------------------- + * Converts an angle from degrees to radians. + */ + +double toRadians(double degrees); + +/* + * Function: vectorDistance + * Usage: double r = vectorDistance(pt); + * double r = vectorDistance(x, y); + * --------------------------------------- + * Computes the distance between the origin and the specified point. + */ + +double vectorDistance(const GPoint & pt); +double vectorDistance(double x, double y); + +/* + * Function: vectorAngle + * Usage: double angle = vectorAngle(pt); + * double angle = vectorAngle(x, y); + * ---------------------------------------- + * Returns the angle in degrees from the origin to the specified point. + * This function takes account of the fact that the graphics coordinate + * system is flipped in the <i>y</i> direction from the traditional + * Cartesian plane. + */ + +double vectorAngle(const GPoint & pt); +double vectorAngle(double x, double y); + +#endif diff --git a/labb8/lib/StanfordCPPLib/gobjects.cpp b/labb8/lib/StanfordCPPLib/gobjects.cpp new file mode 100755 index 0000000..ea22894 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gobjects.cpp @@ -0,0 +1,1033 @@ +/* + * File: gobjects.cpp + * ------------------ + * This file implements the gobjects.h interface. + */ + +#include <cmath> +#include <iostream> +#include <sstream> +#include "gevents.h" +#include "gmath.h" +#include "gobjects.h" +#include "gtypes.h" +#include "gwindow.h" +#include "platform.h" +#include "vector.h" + +static Platform *pp = getPlatform(); + +const double LINE_TOLERANCE = 1.5; +const double ARC_TOLERANCE = 2.5; +const double DEFAULT_CORNER = 10; +const string DEFAULT_GLABEL_FONT = "Dialog-13"; + +static double dsq(double x0, double y0, double x1, double y1); + +double GObject::getX() const { + return x; +} + +double GObject::getY() const { + return y; +} + +GPoint GObject::getLocation() const { + return GPoint(x, y); +} + +void GObject::setLocation(const GPoint & pt) { + setLocation(pt.getX(), pt.getY()); +} + +void GObject::setLocation(double x, double y) { + this->x = x; + this->y = y; + pp->setLocation(this, x, y); +} + +void GObject::move(double dx, double dy) { + setLocation(x + dx, y + dy); +} + +double GObject::getWidth() const { + return getBounds().getWidth(); +} + +double GObject::getHeight() const { + return getBounds().getHeight(); +} + +GDimension GObject::getSize() const { + GRectangle bounds = getBounds(); + return GDimension(bounds.getWidth(), bounds.getHeight()); +} + +void GObject::setLineWidth(double lineWidth) { + this->lineWidth = lineWidth; + pp->setLineWidth(this, lineWidth); +} + +double GObject::getLineWidth() const { + return lineWidth; +} + +void GObject::setColor(string color) { + setColor(convertColorToRGB(color)); +} + +void GObject::setColor(int rgb) { + this->color = convertRGBToColor(rgb); + pp->setColor(this, this->color); +} + +string GObject::getColor() const { + return color; +} + +void GObject::scale(double sf) { + scale(sf, sf); +} + +void GObject::scale(double sx, double sy) { + // Apply local transform + transformed = true; + pp->scale(this, sx, sy); +} + +void GObject::rotate(double theta) { + // Apply local transform + transformed = true; + pp->rotate(this, theta); +} + +void GObject::setVisible(bool flag) { + visible = flag; + pp->setVisible(this, flag); +} + +bool GObject::isVisible() const { + return visible; +} + +void GObject::sendForward() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendForward(this); +} + +void GObject::sendToFront() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendToFront(this); +} + +void GObject::sendBackward() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendBackward(this); +} + +void GObject::sendToBack() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendToBack(this); +} + +bool GObject::contains(GPoint pt) const { + return contains(pt.getX(), pt.getY()); +} + +bool GObject::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + return getBounds().contains(x, y); +} + +GCompound *GObject::getParent() const { + return parent; +} + +GObject::GObject() { + x = 0; + y = 0; + color = ""; + lineWidth = 1.0; + transformed = false; + visible = true; +} + +GObject::~GObject() { + pp->deleteGObject(this); +} + +/* + * Implementation notes: GRect class + * --------------------------------- + * The GRect class is the most straightforward of the shape classes. + */ + +GRect::GRect(double width, double height) { + createGRect(width, height); +} + +GRect::GRect(double x, double y, double width, double height) { + createGRect(width, height); + setLocation(x, y); +} + +GRect::~GRect() { + /* Empty */ +} + +void GRect::setSize(const GDimension & size) { + setSize(size.getWidth(), size.getHeight()); +} + +void GRect::setSize(double width, double height) { + if (transformed) error("setSize: Object has been transformed"); + this->width = width; + this->height = height; + pp->setSize(this, width, height); +} + +void GRect::setBounds(const GRectangle & bounds) { + setLocation(bounds.getX(), bounds.getY()); + setSize(bounds.getWidth(), bounds.getHeight()); +} + +void GRect::setBounds(double x, double y, double width, double height) { + setLocation(x, y); + setSize(width, height); +} + +GRectangle GRect::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y, width, height); +} + +void GRect::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GRect::isFilled() const { + return fillFlag; +} + +void GRect::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GRect::setFillColor(int rgb) { + fillColor = convertRGBToColor(rgb); + pp->setFillColor(this, fillColor); +} + +string GRect::getFillColor() const { + return fillColor; +} + +string GRect::getType() const { + return "GRect"; +} + +string GRect::toString() const { + ostringstream oss; + oss << "GRect(" << x << ", " << y << ", " + << width << ", " << height << ")"; + return oss.str(); +} + +GRect::GRect() { + /* Called only by the GRoundRect and G3DRect subclasses */ +} + +void GRect::createGRect(double width, double height) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + fillFlag = false; + fillColor = ""; + pp->createGRect(this, width, height); +} + +/* + * Implementation notes: GRoundRect class + * -------------------------------------- + * Most of the GRoundRect class is inherited from the GRect class. + */ + +GRoundRect::GRoundRect(double width, double height) { + createGRoundRect(width, height, DEFAULT_CORNER); +} + +GRoundRect::GRoundRect(double width, double height, double corner) { + createGRoundRect(width, height, corner); +} + +GRoundRect::GRoundRect(double x, double y, double width, double height) { + createGRoundRect(width, height, DEFAULT_CORNER); + setLocation(x, y); +} + +GRoundRect::GRoundRect(double x, double y, double width, double height, + double corner) { + createGRoundRect(width, height, corner); + setLocation(x, y); +} + +GRoundRect::~GRoundRect() { + /* Empty */ +} + +string GRoundRect::getType() const { + return "GRoundRect"; +} + +string GRoundRect::toString() const { + ostringstream oss; + oss << "GRoundRect(" << x << ", " << y << ", " + << width << ", " << height << ", " << corner << ")"; + return oss.str(); +} + +void GRoundRect::createGRoundRect(double width, double height, double corner) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + this->corner = corner; + fillFlag = false; + fillColor = ""; + pp->createGRoundRect(this, width, height, corner); +} + +/* + * Implementation notes: G3DRect class + * ----------------------------------- + * Most of the G3DRect class is inherited from the GRect class. + */ + +G3DRect::G3DRect(double width, double height) { + createG3DRect(width, height, false); +} + +G3DRect::G3DRect(double width, double height, bool raised) { + createG3DRect(width, height, raised); +} + +G3DRect::G3DRect(double x, double y, double width, double height) { + createG3DRect(width, height, false); + setLocation(x, y); +} + +G3DRect::G3DRect(double x, double y, double width, double height, + bool raised) { + createG3DRect(width, height, raised); + setLocation(x, y); +} + +G3DRect::~G3DRect() { + /* Empty */ +} + +void G3DRect::setRaised(bool raised) { + this->raised = raised; + pp->setRaised(this, raised); +} + +bool G3DRect::isRaised() const { + return raised; +} + +string G3DRect::getType() const { + return "G3DRect"; +} + +string G3DRect::toString() const { + ostringstream oss; + oss << boolalpha << "G3DRect(" << x << ", " << y << ", " + << width << ", " << height << ", " << raised << ")"; + return oss.str(); +} + +void G3DRect::createG3DRect(double width, double height, bool raised) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + this->raised = raised; + fillFlag = false; + fillColor = ""; + pp->createG3DRect(this, width, height, raised); +} + +GOval::GOval(double width, double height) { + createGOval(width, height); +} + +GOval::GOval(double x, double y, double width, double height) { + createGOval(width, height); + setLocation(x, y); +} + +GOval::~GOval() { + /* Empty */ +} + +void GOval::setSize(const GDimension & size) { + setSize(size.getWidth(), size.getHeight()); +} + +void GOval::setSize(double width, double height) { + if (transformed) error("setSize: Object has been transformed"); + this->width = width; + this->height = height; + pp->setSize(this, width, height); +} + +void GOval::setBounds(const GRectangle & bounds) { + setLocation(bounds.getX(), bounds.getY()); + setSize(bounds.getWidth(), bounds.getHeight()); +} + +void GOval::setBounds(double x, double y, double width, double height) { + setLocation(x, y); + setSize(width, height); +} + +GRectangle GOval::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y, width, height); +} + +bool GOval::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + double rx = width / 2; + double ry = height / 2; + if (rx == 0 || ry == 0) return false; + double dx = x - (this->x + rx); + double dy = y - (this->y + ry); + return (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry) <= 1.0; +} + +void GOval::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GOval::isFilled() const { + return fillFlag; +} + +void GOval::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GOval::setFillColor(int color) { + fillColor = convertRGBToColor(color); + pp->setFillColor(this, fillColor); +} + +string GOval::getFillColor() const { + return fillColor; +} + +string GOval::getType() const { + return "GOval"; +} + +string GOval::toString() const { + ostringstream oss; + oss << "GOval(" << x << ", " << y << ", " + << width << ", " << height << ")"; + return oss.str(); +} + +void GOval::createGOval(double width, double height) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + fillFlag = false; + fillColor = ""; + pp->createGOval(this, width, height); +} + +/* GArc class */ + +GArc::GArc(double width, double height, double start, double sweep) { + createGArc(width, height, start, sweep); +} + +GArc::GArc(double x, double y, double width, double height, + double start, double sweep) { + createGArc(width, height, start, sweep); + setLocation(x, y); +} + +void GArc::setStartAngle(double start) { + this->start = start; + pp->setStartAngle(this, start); +} + +double GArc::getStartAngle() const { + return start; +} + +void GArc::setSweepAngle(double sweep) { + this->sweep = sweep; + pp->setSweepAngle(this, sweep); +} + +double GArc::getSweepAngle() const { + return sweep; +} + +GPoint GArc::getStartPoint() const { + return getArcPoint(start); +} + +GPoint GArc::getEndPoint() const { + return getArcPoint(start + sweep); +} + +void GArc::setFrameRectangle(const GRectangle & rect) { + setFrameRectangle(rect.getX(), rect.getY(), rect.getWidth(), + rect.getHeight()); +} + +void GArc::setFrameRectangle(double x, double y, double width, double height) { + this->x = x; + this->y = y; + frameWidth = width; + frameHeight = height; + pp->setFrameRectangle(this, x, y, width, height); +} + +GRectangle GArc::getFrameRectangle() const { + return GRectangle(0, 0, 0, 0); +} + +void GArc::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GArc::isFilled() const { + return fillFlag; +} + +void GArc::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GArc::setFillColor(int color) { + fillColor = convertRGBToColor(color); + pp->setFillColor(this, fillColor); +} + +string GArc::getFillColor() const { + return fillColor; +} + +GRectangle GArc::getBounds() const { + if (transformed) return pp->getBounds(this); + double rx = frameWidth / 2; + double ry = frameHeight / 2; + double cx = x + rx; + double cy = y + ry; + double startRadians = start * PI / 180; + double sweepRadians = sweep * PI / 180; + double p1x = cx + cos(startRadians) * rx; + double p1y = cy - sin(startRadians) * ry; + double p2x = cx + cos(startRadians + sweepRadians) * rx; + double p2y = cy - sin(startRadians + sweepRadians) * ry; + double xMin = min(p1x, p2x); + double xMax = max(p1x, p2x); + double yMin = min(p1y, p2y); + double yMax = max(p1y, p2y); + if (containsAngle(0)) xMax = cx + rx; + if (containsAngle(90)) yMin = cy - ry; + if (containsAngle(180)) xMin = cx - rx; + if (containsAngle(270)) yMax = cy + ry; + if (isFilled()) { + xMin = min(xMin, cx); + yMin = min(yMin, cy); + xMax = max(xMax, cx); + yMax = max(yMax, cy); + } + return GRectangle(xMin, yMin, xMax - xMin, yMax - yMin); +} + +bool GArc::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + double rx = frameWidth / 2; + double ry = frameHeight / 2; + if (rx == 0 || ry == 0) return false; + double dx = x - (this->x + rx); + double dy = y - (this->y + ry); + double r = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry); + if (fillFlag) { + if (r > 1.0) return false; + } else { + double t = ARC_TOLERANCE / ((rx + ry) / 2); + if (abs(1.0 - r) > t) return false; + } + return containsAngle(atan2(-dy, dx) * 180 / PI); +} + +string GArc::getType() const { + return "GArc"; +} + +string GArc::toString() const { + ostringstream oss; + oss << "GArc(" << x << ", " << y << ", " << frameWidth << ", " + << frameHeight << ", " << start << ", " << sweep << ")"; + return oss.str(); +} + +GPoint GArc::getArcPoint(double theta) const { + double rx = frameWidth / 2; + double ry = frameHeight / 2; + double cx = x + rx; + double cy = y + ry; + double radians = theta * PI / 180; + return GPoint(cx + rx * cos(radians), cy - ry * sin(radians)); +} + +bool GArc::containsAngle(double theta) const { + double start = min(this->start, this->start + this->sweep); + double sweep = abs(this->sweep); + if (sweep >= 360) return true; + theta = (theta < 0) ? 360 - fmod(-theta, 360) : fmod(theta, 360); + start = (start < 0) ? 360 - fmod(-start, 360) : fmod(start, 360); + if (start + sweep > 360) { + return theta >= start || theta <= start + sweep - 360; + } else { + return theta >= start && theta <= start + sweep; + } +} + +void GArc::createGArc(double width, double height, double start, double sweep) { + this->x = 0; + this->y = 0; + frameWidth = width; + frameHeight = height; + this->start = start; + this->sweep = sweep; + fillFlag = false; + fillColor = ""; + pp->createGArc(this, width, height, start, sweep); +} + +GCompound::GCompound() { + pp->createGCompound(this); +} + +void GCompound::add(GObject *gobj) { + pp->add(this, gobj); + contents.add(gobj); + gobj->parent = this; +} + +void GCompound::add(GObject *gobj, double x, double y) { + gobj->setLocation(x, y); + add(gobj); +} + +void GCompound::remove(GObject *gobj) { + int index = findGObject(gobj); + if (index != -1) removeAt(index); +} + +void GCompound::removeAll() { + while (!contents.isEmpty()) { + removeAt(0); + } +} + +int GCompound::getElementCount() { + return contents.size(); +} + +GObject *GCompound::getElement(int index) { + return contents.get(index); +} + +GRectangle GCompound::getBounds() const { + if (transformed) return pp->getBounds(this); + double xMin = +1E20; + double yMin = +1E20; + double xMax = -1E20; + double yMax = -1E20; + for (int i = 0; i < contents.size(); i++) { + GRectangle bounds = contents.get(i)->getBounds(); + xMin = min(xMin, bounds.getX()); + yMin = min(yMin, bounds.getY()); + xMax = max(xMax, bounds.getX()); + yMin = min(yMax, bounds.getY()); + } + return GRectangle(xMin, yMin, xMax - xMin, yMax - yMin); +} + +bool GCompound::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + for (int i = 0; i < contents.size(); i++) { + if (contents.get(i)->contains(x, y)) return true; + } + return false; +} + +string GCompound::getType() const { + return "GCompound"; +} + +string GCompound::toString() const { + return "GCompound(...)"; +} + +void GCompound::sendForward(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != contents.size() - 1) { + contents.remove(index); + contents.insert(index + 1, gobj); + pp->sendForward(gobj); + } +} + +void GCompound::sendToFront(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != contents.size() - 1) { + contents.remove(index); + contents.add(gobj); + pp->sendToFront(gobj); + } +} + +void GCompound::sendBackward(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != 0) { + contents.remove(index); + contents.insert(index - 1, gobj); + pp->sendBackward(gobj); + } +} + +void GCompound::sendToBack(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != 0) { + contents.remove(index); + contents.insert(0, gobj); + pp->sendToBack(gobj); + } +} + +int GCompound::findGObject(GObject *gobj) { + int n = contents.size(); + for (int i = 0; i < n; i++) { + if (contents.get(i) == gobj) return i; + } + return -1; +} + +void GCompound::removeAt(int index) { + GObject *gobj = contents[index]; + contents.remove(index); + pp->remove(gobj); + gobj->parent = NULL; +} + +GImage::GImage(string filename) { + createGImage(filename); +} + +GImage::GImage(string filename, double x, double y) { + createGImage(filename); + setLocation(x, y); +} + +GRectangle GImage::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y, width, height); +} + +string GImage::getType() const { + return "GImage"; +} + +string GImage::toString() const { + return "GImage(\"" + filename + "\")"; +} + +void GImage::createGImage(string filename) { + this->filename = filename; + GDimension size = pp->createGImage(this, filename); + width = size.getWidth(); + height = size.getHeight(); +} + +/* + * Implementation notes: GLabel class + * ---------------------------------- + */ + +GLabel::GLabel(string str) { + createGLabel(str); +} + +GLabel::GLabel(string str, double x, double y) { + createGLabel(str); + setLocation(x, y); +} + +void GLabel::createGLabel(const string & str) { + this->str = str; + pp->createGLabel(this, str); + setFont(DEFAULT_GLABEL_FONT); + GDimension size = pp->getGLabelSize(this); + width = size.getWidth(); + height = size.getHeight(); + ascent = pp->getFontAscent(this); + descent = pp->getFontDescent(this); +} + +void GLabel::setFont(string font) { + this->font = font; + pp->setFont(this, font); + GDimension size = pp->getGLabelSize(this); + width = size.getWidth(); + height = size.getHeight(); + ascent = pp->getFontAscent(this); + descent = pp->getFontDescent(this); +} + +string GLabel::getFont() const { + return font; +} + +void GLabel::setLabel(string str) { + this->str = str; + pp->setLabel(this, str); + GDimension size = pp->getGLabelSize(this); + width = size.getWidth(); + height = size.getHeight(); +} + +string GLabel::getLabel() const { + return str; +} + +double GLabel::getFontAscent() const { + return ascent; +} + +double GLabel::getFontDescent() const { + return descent; +} + +GRectangle GLabel::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y - ascent, width, height); +} + +string GLabel::getType() const { + return "GLabel"; +} + +string GLabel::toString() const { + return "GLabel(\"" + str + "\")"; +} + +/* + * Implementation notes: GLine class + * --------------------------------- + */ + +GLine::GLine(double x0, double y0, double x1, double y1) { + pp->createGLine(this, x0, y0, x1, y1); + x = x0; + y = y0; + dx = x1 - x0; + dy = y1 - y0; +} + +void GLine::setStartPoint(double x, double y) { + dx += this->x - x; + dy += this->y - y; + this->x = x; + this->y = y; + pp->setStartPoint(this, x, y); +} + +GPoint GLine::getStartPoint() const { + return GPoint(x, y); +} + +void GLine::setEndPoint(double x, double y) { + dx = x - this->x; + dy = y - this->y; + pp->setEndPoint(this, x, y); +} + +GPoint GLine::getEndPoint() const { + return GPoint(x + dx, y + dy); +} + +GRectangle GLine::getBounds() const { + if (transformed) return pp->getBounds(this); + double x0 = (dx < 0) ? x + dx : x; + double y0 = (dy < 0) ? y + dy : y; + return GRectangle(x0, y0, abs(dx), abs(dy)); +} + +bool GLine::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + double x0 = getX(); + double y0 = getY(); + double x1 = x0 + dx; + double y1 = y0 + dy; + double tSquared = LINE_TOLERANCE * LINE_TOLERANCE; + if (dsq(x, y, x0, y0) < tSquared) return true; + if (dsq(x, y, x1, y1) < tSquared) return true; + if (x < min(x0, x1) - LINE_TOLERANCE) return false; + if (x > max(x0, x1) + LINE_TOLERANCE) return false; + if (y < min(y0, y1) - LINE_TOLERANCE) return false; + if (y > max(y0, y1) + LINE_TOLERANCE) return false; + if ((float) (x0 - x1) == 0 && (float) (y0 - y1) == 0) return false; + double u = ((x - x0) * (x1 - x0) + (y - y0) * (y1 - y0)) + / dsq(x0, y0, x1, y1); + return dsq(x, y, x0 + u * (x1 - x0), y0 + u * (y1 - y0)) < tSquared; +} + +string GLine::getType() const { + return "GLine"; +} + +string GLine::toString() const { + ostringstream oss; + oss << "GLine(" << x << ", " << y << ", " + << (x + dx) << ", " << (y + dy) << ")"; + return oss.str(); +} + +/* + * Implementation notes: GPolygon class + * ------------------------------------ + */ + +GPolygon::GPolygon() { + fillFlag = false; + fillColor = ""; + pp->createGPolygon(this); +} + +void GPolygon::addVertex(double x, double y) { + cx = x; + cy = y; + vertices.add(GPoint(cx, cy)); + pp->addVertex(this, cx, cy); +} + +void GPolygon::addEdge(double dx, double dy) { + addVertex(cx + dx, cy + dy); +} + +void GPolygon::addPolarEdge(double r, double theta) { + addEdge(r * cos(theta * PI / 180), -r * sin(theta * PI / 180)); +} + +Vector<GPoint> GPolygon::getVertices() const { + return vertices; +} + +void GPolygon::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GPolygon::isFilled() const { + return fillFlag; +} + +void GPolygon::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GPolygon::setFillColor(int rgb) { + fillColor = convertRGBToColor(rgb); + pp->setFillColor(this, fillColor); +} + +string GPolygon::getFillColor() const { + return fillColor; +} + +GRectangle GPolygon::getBounds() const { + if (transformed) return pp->getBounds(this); + double xMin = 0; + double yMin = 0; + double xMax = 0; + double yMax = 0; + for (int i = 0; i < vertices.size(); i++) { + double x = vertices[i].getX(); + double y = vertices[i].getY(); + if (i == 0 || x < xMin) xMin = x; + if (i == 0 || y < yMin) yMin = y; + if (i == 0 || x > xMax) xMax = x; + if (i == 0 || y > yMax) yMax = y; + } + return GRectangle(xMin, yMin, xMax - xMin, yMax - yMin); +} + +bool GPolygon::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + int crossings = 0; + int n = vertices.size(); + if (n < 2) return false; + if (vertices[0] == vertices[n - 1]) n--; + double x0 = vertices[0].getX(); + double y0 = vertices[0].getY(); + for (int i = 1; i <= n; i++) { + double x1 = vertices[i % n].getX(); + double y1 = vertices[i % n].getY(); + if ((y0 > y) != (y1 > y) && x - x0 < (x1 - x0) * (y - y0) / (y1 - y0)) { + crossings++; + } + x0 = x1; + y0 = y1; + } + return (crossings % 2 == 1); +} + +string GPolygon::getType() const { + return "GPolygon"; +} + +string GPolygon::toString() const { + ostringstream oss; + oss << "GPolygon(" << vertices.size() << " vertices)"; + return oss.str(); +} + +static double dsq(double x0, double y0, double x1, double y1) { + return (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); +} diff --git a/labb8/lib/StanfordCPPLib/gobjects.h b/labb8/lib/StanfordCPPLib/gobjects.h new file mode 100755 index 0000000..f0637bf --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gobjects.h @@ -0,0 +1,1491 @@ +/* + * File: gobjects.h + * ---------------- + * This file exports a hierarchy of graphical shapes based on + * the model developed for the ACM Java Graphics. + * <include src="pictures/ClassHierarchies/GObjectHierarchy-h.html"> + */ + +#ifndef _gobjects_h +#define _gobjects_h + +#include "gtypes.h" +#include "gwindow.h" +#include "vector.h" + +class GCompound; + +/* + * Class: GObject + * -------------- + * This class is the common superclass of all graphical objects that can + * be displayed on a graphical window. The class <code>GObject</code> + * itself is an <b><i>abstract class</i></b>, which means that you are not + * allowed to construct a <code>GObject</code> directly but must instead + * construct one of the concrete subclasses. + * <include src="pictures/ClassHierarchies/GObjectHierarchy.html"> + * + * <p>Most methods used for graphics take a pointer to a <code>GObject</code> + * rather than the <code>GObject</code> itself. Applications that use + * <code>GObject</code> pointers therefore use the arrow operator + * (<code>-></code>) to apply methods to the object pointer. + * For examples illustrating the use of the <code>GObject</code> class, see + * the descriptions of the individual subclasses. + */ + +class GObject { + +public: + +/* + * Destructor: ~GObject + * -------------------- + * Frees the storage for the object. + */ + + virtual ~GObject(); + +/* + * Method: getX + * Usage: double x = gobj->getX(); + * ------------------------------- + * Returns the <i>x</i>-coordinate of the object. + */ + + double getX() const; + +/* + * Method: getY + * Usage: double y = gobj->getY(); + * ------------------------------- + * Returns the <i>y</i>-coordinate of the object. + */ + + double getY() const; + +/* + * Method: getLocation + * Usage: GPoint pt = gobj->getLocation(); + * --------------------------------------- + * Returns the location of this object as a <code>GPoint</code>. + */ + + GPoint getLocation() const; + +/* + * Method: setLocation + * Usage: gobj->setLocation(pt); + * gobj->setLocation(x, y); + * ------------------------------- + * Sets the location of this object to the specified point. + */ + + void setLocation(const GPoint & pt); + void setLocation(double x, double y); + +/* + * Method: move + * Usage: gobj->move(dx, dy); + * -------------------------- + * Moves the object on the screen using the displacements + * <code>dx</code> and <code>dy</code>. + */ + + void move(double dx, double dy); + +/* + * Method: getWidth + * Usage: double width = gobj->getWidth(); + * --------------------------------------- + * Returns the width of this object, which is defined to be the width of + * the bounding box. + */ + + double getWidth() const; + +/* + * Method: getHeight + * Usage: double height = gobj->getHeight(); + * ----------------------------------------- + * Returns the height of this object, which is defined to be the height + * of the bounding box. + */ + + double getHeight() const; + +/* + * Method: getSize + * Usage: GDimension size = gobj->getSize(); + * ----------------------------------------- + * Returns the size of the object as a <code>GDimension</code>. + */ + + GDimension getSize() const; + +/* + * Method: getBounds + * Usage: GRectangle rect = gobj->getBounds(); + * ------------------------------------------- + * Returns the bounding box of this object, which is defined to be the + * smallest rectangle that covers everything drawn by the figure. The + * coordinates of this rectangle do not necessarily match the location + * returned by <code>getLocation</code>. Given a <code>GLabel</code> + * object, for example, <code>getLocation</code> returns the coordinates + * of the point on the baseline at which the string begins; the + * <code>getBounds</code> method, by contrast, returns a rectangle that + * covers the entire window area occupied by the string. + */ + + virtual GRectangle getBounds() const = 0; + +/* + * Method: setLineWidth + * Usage: gobj->setLineWidth(lineWidth); + * ------------------------------------- + * Sets the width of the line used to draw this object. + */ + + void setLineWidth(double lineWidth); + +/* + * Method: getLineWidth + * Usage: double lineWidth = gobj->getLineWidth(); + * ----------------------------------------------- + * Returns the width of the line used to draw this object. + */ + + double getLineWidth() const; + +/* + * Method: setColor + * Usage: gobj->setColor(color); + * ----------------------------- + * Sets the color used to display this object. The <code>color</code> + * string is usually one of the predefined color names: + * + * <code>BLACK</code>, + * <code>BLUE</code>, + * <code>CYAN</code>, + * <code>DARK_GRAY</code>, + * <code>GRAY</code>, + * <code>GREEN</code>, + * <code>LIGHT_GRAY</code>, + * <code>MAGENTA</code>, + * <code>ORANGE</code>, + * <code>PINK</code>, + * <code>RED</code>, + * <code>WHITE</code>, and + * <code>YELLOW</code>. + * + * The case of the individual letters in the color name is ignored, as + * are spaces and underscores, so that the color <code>DARK_GRAY</code> + * can be written as <code>"Dark Gray"</code>. + * + * <p>The color can also be specified as a string in the form + * <code>"#rrggbb"</code> where <code>rr</code>, <code>gg</code>, and + * <code>bb</code> are pairs of hexadecimal digits indicating the + * red, green, and blue components of the color, respectively. + */ + + void setColor(std::string color); + void setColor(int rgb); + +/* + * Method: getColor + * Usage: string color = gobj->getColor(); + * --------------------------------------- + * Returns the color used to display this object. This color is + * always returned as a string in the form <code>"#rrggbb"</code>, + * where <code>rr</code>, <code>gg</code>, and <code>bb</code> are + * the red, green, and blue components of the color, expressed as + * two-digit hexadecimal values. + */ + + std::string getColor() const; + +/* + * Method: scale + * Usage: gobj->scale(sf); + * gobj->scale(sx, sy); + * --------------------------- + * Scales the object by the specified scale factors. Most clients will use + * the first form, which scales the object by <code>sf</code> in both + * dimensions, so that invoking <code>gobj->scale(2)</code> doubles the + * size of the object. The second form applies independent scale factors + * to the <i>x</i> and <i>y</i> dimensions. + */ + + void scale(double sf); + void scale(double sx, double sy); + +/* + * Method: rotate + * Usage: gobj->rotate(theta); + * --------------------------- + * Transforms the object by rotating it <code>theta</code> degrees + * counterclockwise around its origin. + */ + + void rotate(double theta); + +/* + * Method: setVisible + * Usage: gobj->setVisible(flag); + * ------------------------------ + * Sets whether this object is visible. + */ + + void setVisible(bool flag); + +/* + * Method: isVisible + * Usage: if (gobj->isVisible()) ... + * --------------------------------- + * Returns <code>true</code> if this object is visible. + */ + + bool isVisible() const; + +/* + * Method: sendForward + * Usage: gobj->sendForward(); + * --------------------------- + * Moves this object one step toward the front in the <i>z</i> dimension. + * If it was already at the front of the stack, nothing happens. + */ + + void sendForward(); + +/* + * Method: sendToFront + * Usage: gobj->sendToFront(); + * --------------------------- + * Moves this object to the front of the display in the <i>z</i> dimension. + * By moving it to the front, this object will appear to be on top of the + * other graphical objects on the display and may hide any objects that + * are further back. + */ + + void sendToFront(); + +/* + * Method: sendBackward + * Usage: gobj->sendBackward(); + * ---------------------------- + * Moves this object one step toward the back in the <i>z</i> dimension. + * If it was already at the back of the stack, nothing happens. + */ + + void sendBackward(); + +/* + * Method: sendToBack + * Usage: gobj->sendToBack(); + * -------------------------- + * Moves this object to the back of the display in the <i>z</i> dimension. + * By moving it to the back, this object will appear to be behind the other + * graphical objects on the display and may be obscured by other objects + * in front. + */ + + void sendToBack(); + +/* + * Method: contains + * Usage: if (gobj->contains(pt)) ... + * if (gobj->contains(x, y)) ... + * ------------------------------------ + * Returns <code>true</code> if the specified point is inside the object. + */ + + bool contains(GPoint pt) const; + virtual bool contains(double x, double y) const; + +/* + * Method: getType + * Usage: string type = gobj->getType(); + * ------------------------------------- + * Returns the concrete type of the object as a string, as in + * <code>"GOval"</code> or <code>"GRect"</code>. + */ + + virtual std::string getType() const = 0; + +/* + * Method: toString + * Usage: gobj->toString(); + * ------------------------ + * Returns a printable representation of the object. + */ + + virtual std::string toString() const = 0; + +/* + * Method: getParent + * Usage: GCompound *parent = gobj->getParent(); + * --------------------------------------------- + * Returns a pointer to the <code>GCompound</code> that contains this + * object. Every <code>GWindow</code> is initialized to contain a single + * <code>GCompound</code> that is aligned with the window. Adding + * objects to the window adds them to that <code>GCompound</code>, + * which means that every object you add to the window has a parent. + * Calling <code>getParent</code> on the top-level <code>GCompound</code> + * returns <code>NULL</code>. + */ + + GCompound *getParent() const; + +/* Private section */ + +private: + const GObject & operator=(const GObject &) { return *this; } + GObject(const GObject &) { } + +/* Instance variables */ + +protected: + double x; /* The x coordinate of the origin */ + double y; /* The y coordinate of the origin */ + double lineWidth; /* The width of the line in pixels */ + std::string color; /* The color of the object */ + bool visible; /* Indicates if object is visible */ + bool transformed; /* Indicates if object is transformed */ + GCompound *parent; /* Pointer to the parent */ + +protected: + GObject(); + + friend class GArc; + friend class GButton; + friend class GCheckBox; + friend class GChooser; + friend class GCompound; + friend class GImage; + friend class GInteractor; + friend class GLabel; + friend class GLine; + friend class GOval; + friend class GPolygon; + friend class GRect; + friend class GRoundRect; + friend class GSlider; + friend class GTextField; + friend class G3DRect; + +}; + +/* + * Class: GRect + * ------------ + * This class represents a graphical object whose appearance consists of + * a rectangular box. For example, the following code adds a filled, red + * <nobr>200<font class=sansserif size=-1>x</font>100</nobr> rectangle + * at the upper left corner of the graphics window: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws a red rectangle at (0, 0)." << endl; + * GRect *rect = new GRect(0, 0, 200, 100); + * rect->setFilled(true); + * rect->setColor("RED"); + * gw.add(rect); + * return 0; + * } + *</pre> + */ + +class GRect : public GObject { + +public: + +/* + * Constructor: GRect + * Usage: GRect *rect = new GRect(width, height); + * GRect *rect = new GRect(x, y, width, height); + * ---------------------------------------------------- + * Constructs a rectangle with the specified width and height. The first + * form is positioned at the origin; the second at the coordinates + * given by <code>x</code> and <code>y</code>. + */ + + GRect(double width, double height); + GRect(double x, double y, double width, double height); + +/* + * Destructor: ~GRect + * ------------------ + * Frees any resources maintained by this object. + */ + + virtual ~GRect(); + +/* + * Method: setSize + * Usage: rect->setSize(size); + * rect->setSize(width, height); + * ------------------------------------ + * Changes the size of this rectangle to the specified width and height. + */ + + void setSize(const GDimension & size); + void setSize(double width, double height); + +/* + * Method: setBounds + * Usage: rect->setBounds(rect); + * rect->setBounds(x, y, width, height); + * -------------------------------------------- + * Changes the bounds of this rectangle to the specified values. + */ + + void setBounds(const GRectangle & size); + void setBounds(double x, double y, double width, double height); + +/* + * Method: setFilled + * Usage: rect->setFilled(flag); + * ----------------------------- + * Sets the fill status for the rectangle, where <code>false</code> is + * outlined and <code>true</code> is filled. + */ + + void setFilled(bool flag); + +/* + * Method: isFilled + * Usage: if (rect->isFilled()) ... + * -------------------------------- + * Returns <code>true</code> if the rectangle is filled. + */ + + bool isFilled() const; + +/* + * Method: setFillColor + * Usage: rect->setFillColor(color); + * --------------------------------- + * Sets the color used to display the filled region of this rectangle. + */ + + void setFillColor(std::string color); + void setFillColor(int rgb); + +/* + * Method: getFillColor + * Usage: string color = rect->getFillColor(); + * ------------------------------------------- + * Returns the color used to display the filled region of this rectangle. If + * none has been set, <code>getFillColor</code> returns the empty string. + */ + + std::string getFillColor() const; + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual std::string getType() const; + virtual std::string toString() const; + +protected: + +/* Instance variables */ + + double width; /* The width of the rectangle */ + double height; /* The height of the rectangle */ + bool fillFlag; /* Indicates whether the object is filled */ + std::string fillColor; /* Color used to fill the object */ + +/* Protected methods */ + + GRect(); + virtual void createGRect(double width, double height); + +}; + +/* + * Class: GRoundRect + * ----------------- + * This class represents a graphical object whose appearance consists + * of a rectangular box with rounded corners. + */ + +class GRoundRect : public GRect { + +public: + +/* + * Constructor: GRoundRect + * Usage: GRoundRect *rect = new GRoundRect(width, height); + * GRoundRect *rect = new GRoundRect(width, height, corner); + * GRoundRect *rect = new GRoundRect(x, y, width, height); + * GRoundRect *rect = new GRoundRect(x, y, width, height, corner); + * ---------------------------------------------------------------------- + * Constructs a new rectangle with the specified width and height. If + * the <code>x</code> and <code>y</code> parameters are specified, they + * are used to specify the origin. The <code>corner</code> parameter + * specifies the diameter of the arc forming the corner. + */ + + GRoundRect(double width, double height); + GRoundRect(double width, double height, double corner); + GRoundRect(double x, double y, double width, double height); + GRoundRect(double x, double y, double width, double height, double corner); + +/* + * Destructor: ~GRoundRect + * ----------------------- + * Frees any resources maintained by this object. + */ + + virtual ~GRoundRect(); + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +protected: + + double corner; + +/* Protected methods */ + + void createGRoundRect(double width, double height, double corner); + +}; + +/* + * Class: G3DRect + * -------------- + * This graphical object subclass represents a rectangular box that can + * be raised or lowered. + */ + +class G3DRect : public GRect { + +public: + +/* + * Constructor: G3DRect + * Usage: G3DRect *rect = new G3DRect(width, height); + * G3DRect *rect = new G3DRect(width, height, raised); + * G3DRect *rect = new G3DRect(x, y, width, height); + * G3DRect *rect = new G3DRect(x, y, width, height, raised); + * ---------------------------------------------------------------- + * Constructs a new 3D rectangle with the specified width and height. If + * the <code>x</code> and <code>y</code> parameters are specified, they + * are used to specify the origin. The <code>raised</code> parameter + * determines whether the rectangle should be drawn with highlights that + * suggest that it is raised about the background. + */ + + G3DRect(double width, double height); + G3DRect(double width, double height, bool raised); + G3DRect(double x, double y, double width, double height); + G3DRect(double x, double y, double width, double height, bool raised); + +/* + * Destructor: ~G3DRect + * -------------------- + * Frees any resources maintained by this object. + */ + + virtual ~G3DRect(); + +/* + * Method: setRaised + * Usage: rect->setRaised(raised); + * ------------------------------- + * Indicates whether this object appears raised. + */ + + void setRaised(bool raised); + +/* + * Method: isRaised + * Usage: if (rect->isRaised()) ... + * -------------------------------- + * Returns <code>true</code> if this object appears raised. + */ + + bool isRaised() const; + +/* Prototypes for the virtual methods */ + + virtual std::string getType() const; + virtual std::string toString() const; + +protected: + + bool raised; + +/* Protected methods */ + + void createG3DRect(double width, double height, bool raised); + +}; + +/* + * Class: GOval + * ------------ + * This graphical object subclass represents an oval inscribed in + * a rectangular box. For example, the following code displays a + * filled green oval inscribed in the graphics window: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws a green oval filling the window." << endl; + * GOval *oval = new GOval(gw.getWidth(), gw.getHeight()); + * oval->setFilled(true); + * oval->setColor("GREEN"); + * gw.add(oval); + * return 0; + * } + *</pre> + */ + +class GOval : public GObject { + +public: + +/* + * Constructor: GOval + * Usage: GOval *oval = new GOval(width, height); + * GOval *oval = new GOval(x, y, width, height); + * ---------------------------------------------------- + * Constructs a new oval inscribed in the specified rectangle. The + * first form is positioned at the origin; the second at the coordinates + * given by <code>x</code> and <code>y</code>. + */ + + GOval(double width, double height); + GOval(double x, double y, double width, double height); + +/* + * Destructor: ~GOval + * ------------------ + * Frees any resources maintained by this object. + */ + + virtual ~GOval(); + +/* + * Method: setSize + * Usage: oval->setSize(size); + * oval->setSize(width, height); + * ------------------------------------ + * Changes the size of the bounding rectangle to the specified width + * and height. + */ + + void setSize(const GDimension & size); + void setSize(double width, double height); + +/* + * Method: setBounds + * Usage: oval->setBounds(rect); + * oval->setBounds(x, y, width, height); + * -------------------------------------------- + * Changes the bounds of the oval to the specified values. + */ + + void setBounds(const GRectangle & size); + void setBounds(double x, double y, double width, double height); + +/* + * Method: setFilled + * Usage: oval->setFilled(flag); + * ----------------------------- + * Sets the fill status for the oval, where <code>false</code> is + * outlined and <code>true</code> is filled. + */ + + void setFilled(bool flag); + +/* + * Method: isFilled + * Usage: if (oval->isFilled()) ... + * -------------------------------- + * Returns <code>true</code> if the oval is filled. + */ + + bool isFilled() const; + +/* + * Method: setFillColor + * Usage: oval->setFillColor(color); + * --------------------------------- + * Sets the color used to display the filled region of this oval. + */ + + void setFillColor(std::string color); + void setFillColor(int rgb); + +/* + * Method: getFillColor + * Usage: string color = oval->getFillColor(); + * ------------------------------------------- + * Returns the color used to display the filled region of this oval. If + * none has been set, <code>getFillColor</code> returns the empty string. + */ + + std::string getFillColor() const; + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual bool contains(double x, double y) const; + virtual std::string getType() const; + virtual std::string toString() const; + +protected: + +/* Instance variables */ + + double width; /* The width of the bounding rectangle */ + double height; /* The height of the bounding rectangle */ + bool fillFlag; /* Indicates whether the object is filled */ + std::string fillColor; /* Color used to fill the object */ + +/* Protected methods */ + + void createGOval(double width, double height); + +}; + +/* + * Class: GArc + * ----------- + * This graphical object subclass represents an elliptical arc. The + * arc is specified by the following parameters: + * + * <p><ul> + * <li>The coordinates of the bounding rectangle (<code>x</code>, + * <code>y</code>, <code>width</code>, <code>height</code>) + * <li>The angle at which the arc starts (<code>start</code>) + * <li>The number of degrees that the arc covers (<code>sweep</code>) + * </ul> + * + * <p>All angles in a <code>GArc</code> description are measured in + * degrees moving counterclockwise from the +<i>x</i> axis. Negative + * values for either <code>start</code> or <code>sweep</code> indicate + * motion in a clockwise direction. + * <include src="pictures/GObjectDiagrams/GArcGeometry.html"> + */ + +class GArc : public GObject { + +public: + +/* + * Constructor: GArc + * Usage: GArc *arc = new GArc(width, height, start, sweep); + * GArc *arc = new GArc(x, y, width, height, start, sweep); + * --------------------------------------------------------------- + * Creates a new <code>GArc</code> object consisting of an elliptical arc. + * The first form creates a <code>GArc</code> whose origin is the point + * (0, 0); the second form positions the <code>GArc</code> at the + * point (<code>x</code>, <code>y</code>). + * <include src="pictures/GObjectDiagrams/GArcExamples.html"> + */ + + GArc(double width, double height, double start, double sweep); + GArc(double x, double y, double width, double height, + double start, double sweep); + +/* + * Method: setStartAngle + * Usage: arc->setStartAngle(start); + * --------------------------------- + * Sets the starting angle for this <code>GArc</code> object. + */ + + void setStartAngle(double start); + +/* + * Method: getStartAngle + * Usage: double angle = arc->getStartAngle(); + * ------------------------------------------- + * Returns the starting angle for this <code>GArc</code> object. + */ + + double getStartAngle() const; + +/* + * Method: setSweepAngle + * Usage: arc->setSweepAngle(start); + * --------------------------------- + * Sets the sweep angle for this <code>GArc</code> object. + */ + + void setSweepAngle(double start); + +/* + * Method: getSweepAngle + * Usage: double angle = arc->getSweepAngle(); + * ------------------------------------------- + * Returns the sweep angle for this <code>GArc</code> object. + */ + + double getSweepAngle() const; + +/* + * Method: getStartPoint + * Usage: GPoint pt = arc->getStartPoint(); + * ---------------------------------------- + * Returns the point at which the arc starts. + */ + + GPoint getStartPoint() const; + +/* + * Method: getEndPoint + * Usage: GPoint pt = arc->getEndPoint(); + * -------------------------------------- + * Returns the point at which the arc ends. + */ + + GPoint getEndPoint() const; + +/* + * Method: setFrameRectangle + * Usage: arc->setFrameRectangle(rect); + * arc->setFrameRectangle(x, y, width, height); + * --------------------------------------------------- + * Changes the boundaries of the rectangle used to frame the arc. + */ + + void setFrameRectangle(const GRectangle & rect); + void setFrameRectangle(double x, double y, double width, double height); + +/* + * Method: getFrameRectangle + * Usage: GRectangle rect = arc->getFrameRectangle(); + * -------------------------------------------------- + * Returns the boundaries of the rectangle used to frame the arc. + */ + + GRectangle getFrameRectangle() const; + +/* + * Method: setFilled + * Usage: arc->setFilled(flag); + * ---------------------------- + * Sets the fill status for the arc, where <code>false</code> is + * outlined and <code>true</code> is filled. If a <code>GArc</code> is + * unfilled, the figure consists only of the arc itself. If a + * <code>GArc</code> is filled, the figure consists of the + * pie-shaped wedge formed by connecting the endpoints of the arc to + * the center. As an example, the following program draws a 270-degree + * arc starting at 45 degrees, filled in yellow, much like the character + * in the PacMan video game: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws the PacMan character." << endl; + * double cx = gw.getWidth() / 2; + * double cy = gw.getHeight() / 2; + * double r = 25; + * GArc *pacman = new GArc(cx - r, cy - r, 2 * r, 2 * r, 45, 270); + * pacman->setFilled(true); + * pacman->setFillColor("YELLOW"); + * gw.add(pacman); + * return 0; + * } + *</pre> + * <include src="pictures/GObjectDiagrams/PacMan.html"> + */ + + void setFilled(bool flag); + +/* + * Method: isFilled + * Usage: if (arc->isFilled()) ... + * ------------------------------- + * Returns <code>true</code> if the arc is filled. + */ + + bool isFilled() const; + +/* + * Method: setFillColor + * Usage: arc->setFillColor(color); + * -------------------------------- + * Sets the color used to display the filled region of this arc. + * Colors are specified as strings as described in the notes for the + * <a href="GObject-class.html#Method:setColor"><code>setColor</code></a> + * method. + */ + + void setFillColor(std::string color); + void setFillColor(int rgb); + +/* + * Method: getFillColor + * Usage: string color = arc->getFillColor(); + * ------------------------------------------ + * Returns the color used to display the filled region of this arc. If + * none has been set, <code>getFillColor</code> returns the empty string. + */ + + std::string getFillColor() const; + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual bool contains(double x, double y) const; + virtual std::string getType() const; + virtual std::string toString() const; + +private: + + GPoint getArcPoint(double theta) const; + bool containsAngle(double theta) const; + void createGArc(double width, double height, double start, double sweep); + +/* Instance variables */ + + double start; /* Starting angle of the arc */ + double sweep; /* How many degrees the arc runs */ + double frameWidth; /* The width of the bounding box */ + double frameHeight; /* The height of the bounding box */ + std::string fillColor; /* The color of the interior */ + bool fillFlag; /* Indicates if the arc is filled */ + +}; + +/* + * Class: GCompound + * ---------------- + * This graphical object subclass consists of a collection + * of other graphical objects. Once assembled, the internal objects + * can be manipulated as a unit. The <code>GCompound</code> keeps + * track of its own position, and all items within it are drawn + * relative to that location. + */ + +class GCompound : public GObject { + +public: + +/* + * Constructor: GCompound + * Usage: GCompound *comp = new GCompound(); + * ----------------------------------------- + * Creates a <code>GCompound</code> object with no internal components. + */ + + GCompound(); + +/* + * Method: add + * Usage: comp->add(gobj); + * comp->add(gobj, x, y); + * ----------------------------- + * Adds a new graphical object to the <code>GCompound</code>. The second + * form moves the object to the point (<code>x</code>, <code>y</code>) first. + */ + + void add(GObject *gobj); + void add(GObject *gobj, double x, double y); + +/* + * Method: remove + * Usage: comp->remove(gobj); + * -------------------------- + * Removes the specified object from the <code>GCompound</code>. + */ + + void remove(GObject *gobj); + +/* + * Method: removeAll + * Usage: comp->removeAll(); + * ------------------------- + * Removes all graphical objects from the <code>GCompound</code>. + */ + + void removeAll(); + +/* + * Method: getElementCount + * Usage: int n = comp->getElementCount(); + * --------------------------------------- + * Returns the number of graphical objects stored in the + * <code>GCompound</code>. + */ + + int getElementCount(); + +/* + * Method: getElement + * Usage: GObject *gobj = comp->getElement(index); + * ----------------------------------------------- + * Returns a pointer to the graphical object at the specified index, + * numbering from back to front in the the <i>z</i> dimension. + */ + + GObject *getElement(int index); + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual bool contains(double x, double y) const; + virtual std::string getType() const; + virtual std::string toString() const; + +private: + void sendForward(GObject *gobj); + void sendToFront(GObject *gobj); + void sendBackward(GObject *gobj); + void sendToBack(GObject *gobj); + int findGObject(GObject *gobj); + void removeAt(int index); + +/* Instance variables */ + + Vector<GObject *> contents; + +/* Friend declarations */ + + friend class GObject; + +}; + +/* + * Class: GImage + * ------------- + * This graphical object subclass represents an image from a file. + * For example, the following code displays a <code>GImage</code> + * containing the Stanford tree at the center of the window, assuming + * that the image file <code>StanfordTree.png</code> exists, either in + * the current directory or an <code>images</code> subdirectory: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws the Stanford tree." << endl; + * GImage *tree = new GImage("StanfordTree.png"); + * double x = (gw.getWidth() - tree->getWidth()) / 2; + * double y = (gw.getHeight() - tree->getHeight()) / 2; + * gw.add(tree, x, y); + * return 0; + * } + *</pre> + */ + +class GImage : public GObject { + +public: + +/* + * Constructor: GImage + * Usage: GImage *image = new GImage(filename); + * GImage *image = new GImage(filename, x, y); + * -------------------------------------------------- + * Constructs a new image by loading the image from the specified + * file, which is either in the current directory or a subdirectory named + * <code>images</code>. By default, the upper left corner of the image + * appears at the origin; the second form automatically sets the location + * to the point (<code>x</code>, <code>y</code>). + */ + + GImage(std::string filename); + GImage(std::string filename, double x, double y); + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual std::string getType() const; + virtual std::string toString() const; + +private: + std::string filename; + double width; + double height; + + void createGImage(std::string filename); + +}; + +/* + * Class: GLabel + * ------------- + * This graphical object subclass represents a text string. For + * example, the following code adds a <code>GLabel</code> containing + * the string <code>"hello, world"</code> to the center of the window: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws the 'hello, world' message." << endl; + * GLabel *label = new GLabel("hello, world"); + * label->setFont("SansSerif-18"); + * double x = (gw.getWidth() - label->getWidth()) / 2; + * double y = (gw.getHeight() + label->getFontAscent()) / 2; + * gw.add(label, x, y); + * return 0; + * } + *</pre> + * + * Controlling the appearance and positioning of a <code>GLabel</code> + * depends on understanding the following terms: + * + * <p><ul> + * <li>The <b><i>baseline</i></b> is the horizontal line on which the + * characters rest. + * <li>The <b><i>origin</i></b> is the point on the baseline at which + * the label begins. + * <li>The <b><i>height</i></b> is the distance that separate two + * successive lines. + * <li>The <b><i>ascent</i></b> is the maximum distance a character + * in this font extends above the baseline. + * <li>The <b><i>descent</i></b> is the maximum distance a character + * in this font extends below the baseline. + * </ul> + * <include src="pictures/GObjectDiagrams/GLabelGeometry.html"> + */ + +class GLabel : public GObject { + +public: + +/* + * Constructor: GLabel + * Usage: GLabel *label = new GLabel(str); + * GLabel *label = new GLabel(str, x, y); + * --------------------------------------------- + * Creates a <code>GLabel</code> object containing the specified string. + * By default, the baseline of the first character appears at the origin; + * the second form automatically resets the location of the + * <code>GLabel</code> to the point (<code>x</code>, <code>y</code>). + */ + + GLabel(std::string str); + GLabel(std::string str, double x, double y); + +/* + * Method: setFont + * Usage: label->setFont(font); + * ---------------------------- + * Changes the font used to display the <code>GLabel</code> as specified by + * the string <code>font</code>, which has the following format: + * + *<pre> + * family-style-size + *</pre> + * + * where both <code>style</code> and <code>size</code> are optional. + * If any of these elements are missing or specified as an asterisk, + * the existing value is retained. + */ + + void setFont(std::string font); + +/* + * Method: getFont + * Usage: string font = label->getFont(); + * -------------------------------------- + * Returns the current font for the <code>GLabel</code>. + */ + + std::string getFont() const; + +/* + * Method: setLabel + * Usage: label->setLabel(str); + * ---------------------------- + * Changes the string stored within the <code>GLabel</code> object, so that + * a new text string appears on the display. + */ + + void setLabel(std::string str); + +/* + * Method: getLabel + * Usage: string str = label->getLabel(); + * -------------------------------------- + * Returns the string displayed by this object. + */ + + std::string getLabel() const; + +/* + * Method: getFontAscent + * Usage: double ascent = label->getFontAscent(); + * ---------------------------------------------- + * Returns the maximum distance strings in this font extend above + * the baseline. + */ + + double getFontAscent() const; + +/* + * Method: getFontDescent + * Usage: double descent = label->getFontDescent(); + * ------------------------------------------------ + * Returns the maximum distance strings in this font descend below + * the baseline. + */ + + double getFontDescent() const; + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual std::string getType() const; + virtual std::string toString() const; + +private: + +/* Instance variables */ + + std::string str; /* The string displayed by the label */ + std::string font; /* The font string of the label */ + double width; /* Width of the bounding box */ + double height; /* Height of the bounding box */ + double ascent; /* Font ascent */ + double descent; /* Font descent */ + + void createGLabel(const std::string & str); + +}; + +/* + * Class: GLine + * ------------ + * This graphical object subclass represents a line segment. For + * example, the following code adds lines that mark the diagonals + * of the graphics window: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws the diagonals on the window." << endl; + * gw.add(new GLine(0, 0, gw.getWidth(), gw.getHeight())); + * gw.add(new GLine(0, gw.getHeight(), gw.getWidth(), 0)); + * return 0; + * } + *</pre> + */ + +class GLine : public GObject { + +public: + +/* + * Constructor: GLine + * Usage: GLine *gline = new GLine(x0, y0, x1, y1); + * ------------------------------------------------ + * Constructs a line segment from its endpoints. The point + * (<code>x0</code>, <code>y0</code>) defines the start of the + * line and the point (<code>x1</code>, <code>y1</code>) defines + * the end. + */ + + GLine(double x0, double y0, double x1, double y1); + +/* + * Method: setStartPoint + * Usage: line->setStartPoint(x, y); + * --------------------------------- + * Sets the initial point in the line to (<code>x</code>, <code>y</code>), + * leaving the end point unchanged. This method is therefore different from + * <code>setLocation</code>, which moves both components of the line segment. + */ + + void setStartPoint(double x, double y); + +/* + * Method: getStartPoint + * Usage: GPoint pt = line->getStartPoint(); + * ----------------------------------------- + * Returns the point at which the line starts. + */ + + GPoint getStartPoint() const; + +/* + * Method: setEndPoint + * Usage: line->setEndPoint(x, y); + * ------------------------------- + * Sets the end point in the line to (<code>x</code>, <code>y</code>), + * leaving the start point unchanged. This method is therefore different from + * <code>setLocation</code>, which moves both components of the line segment. + */ + + void setEndPoint(double x, double y); + +/* + * Method: getEndPoint + * Usage: GPoint pt = line->getEndPoint(); + * --------------------------------------- + * Returns the point at which the line ends. + */ + + GPoint getEndPoint() const; + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual bool contains(double x, double y) const; + virtual std::string getType() const; + virtual std::string toString() const; + +protected: + +/* Instance variables */ + + double dx; /* The x displacement of the line */ + double dy; /* The y displacement of the line */ + +}; + +/* + * Class: GPolygon + * --------------- + * This graphical object subclass represents a polygon bounded by + * line segments. The <code>GPolygon</code> constructor creates an + * empty polygon. To complete the figure, you need to add vertices + * to the polygon using the methods + * <a href="#Method:addVertex"><code>addVertex</code></a>, + * <a href="#Method:addEdge"><code>addEdge</code></a>, + * and <a href="#Method:addPolarEdge"><code>addPolarEdge</code></a>. + * As an example, the following code adds a filled red octagon to + * the center of the window: + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws a red octagon." << endl; + * double edge = 75; + * GPolygon *stopSign = new GPolygon(); + * stopSign->addVertex(-edge / 2, edge / 2 + edge / sqrt(2.0)); + * for (int i = 0; i < 8; i++) { + * stopSign->addPolarEdge(edge, 45 * i); + * } + * stopSign->setFilled(true); + * stopSign->setColor("RED"); + * gw.add(stopSign, gw.getWidth() / 2, gw.getHeight() / 2); + * return 0; + * } + *</pre> + * <include src="pictures/GObjectDiagrams/StopSign.html"> + */ + +class GPolygon : public GObject { + +public: + +/* + * Constructor: GPolygon + * Usage: GPolygon *poly = new GPolygon(); + * --------------------------------------- + * Constructs a new empty polygon at the origin. + */ + + GPolygon(); + +/* + * Method: addVertex + * Usage: poly->addVertex(x, y); + * ----------------------------- + * Adds a vertex at (<code>x</code>, <code>y</code>) relative to the polygon + * origin. + */ + + void addVertex(double x, double y); + +/* + * Method: addEdge + * Usage: poly->addEdge(dx, dy); + * ----------------------------- + * Adds an edge to the polygon whose components are given by the displacements + * <code>dx</code> and <code>dy</code> from the last vertex. + */ + + void addEdge(double dx, double dy); + +/* + * Method: addPolarEdge + * Usage: poly->addPolarEdge(r, theta); + * ------------------------------------ + * Adds an edge to the polygon specified in polar coordinates. The length + * of the edge is given by <code>r</code>, and the edge extends in + * direction <code>theta</code>, measured in degrees counterclockwise + * from the +x axis. + */ + + void addPolarEdge(double r, double theta); + +/* + * Method: getVertices + * Usage: Vector<GPoint> vec = poly->getVertices(); + * ------------------------------------------------ + * Returns a vector of the points in the polygon. + */ + + Vector<GPoint> getVertices() const; + +/* + * Method: setFilled + * Usage: poly->setFilled(flag); + * ----------------------------- + * Sets the fill status for the polygon, where <code>false</code> is + * outlined and <code>true</code> is filled. + */ + + void setFilled(bool flag); + +/* + * Method: isFilled + * Usage: if (poly->isFilled()) ... + * -------------------------------- + * Returns <code>true</code> if the polygon is filled. + */ + + bool isFilled() const; + +/* + * Method: setFillColor + * Usage: poly->setFillColor(color); + * --------------------------------- + * Sets the color used to display the filled region of this polygon. + */ + + void setFillColor(std::string color); + void setFillColor(int rgb); + +/* + * Method: getFillColor + * Usage: string color = poly->getFillColor(); + * ------------------------------------------- + * Returns the color used to display the filled region of this polygon. If + * none has been set, <code>getFillColor</code> returns the empty string. + */ + + std::string getFillColor() const; + +/* Prototypes for the virtual methods */ + + virtual GRectangle getBounds() const; + virtual bool contains(double x, double y) const; + virtual std::string getType() const; + virtual std::string toString() const; + +private: + +/* Instance variables */ + + Vector<GPoint> vertices; /* The vertices of the polygon */ + double cx; /* The most recent x coordinate */ + double cy; /* The most recent y coordinate */ + std::string fillColor; /* The color of the interior */ + bool fillFlag; /* Indicates if object is filled */ + +}; + +#endif diff --git a/labb8/lib/StanfordCPPLib/graph.h b/labb8/lib/StanfordCPPLib/graph.h new file mode 100755 index 0000000..30fae8f --- /dev/null +++ b/labb8/lib/StanfordCPPLib/graph.h @@ -0,0 +1,836 @@ +/* + * File: graph.h + * ------------- + * This file exports a parameterized <code>Graph</code> class used + * to represent <b><i>graphs,</i></b> which consist of a set of + * <b><i>nodes</i></b> and a set of <b><i>arcs</i></b>. + */ + +#ifndef _graph_h +#define _graph_h + +#include <string> +#include "map.h" +#include "set.h" +#include "tokenscanner.h" + +/* + * Class: Graph<NodeType,ArcType> + * ------------------------------ + * This class represents a graph with the specified node and arc types. + * The <code>NodeType</code> and <code>ArcType</code> parameters indicate + * the structure type or class used for nodes and arcs, respectively. + * These types can contain any fields or methods required by the client, + * but must contain the following fields required by the <code>Graph</code> + * package itself: + * + * <p>The <code>NodeType</code> definition must include: + * <ul> + * <li>A <code>string</code> field called <code>name</code> + * <li>A <code>Set<ArcType *></code> field called <code>arcs</code> + * </ul> + * + * <p>The <code>ArcType</code> definition must include: + * <ul> + * <li>A <code>NodeType *</code> field called <code>start</code> + * <li>A <code>NodeType *</code> field called <code>finish</code> + * </ul> + */ + +template <typename NodeType,typename ArcType> +class Graph { + +public: + +/* + * Constructor: Graph + * Usage: Graph<NodeType,ArcType> g; + * --------------------------------- + * Creates an empty <code>Graph</code> object. + */ + + Graph(); + +/* + * Destructor: ~Graph + * ------------------ + * Frees the internal storage allocated to represent the graph. + */ + + virtual ~Graph(); + +/* + * Method: size + * Usage: int size = g.size(); + * --------------------------- + * Returns the number of nodes in the graph. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (g.isEmpty()) ... + * --------------------------- + * Returns <code>true</code> if the graph is empty. + */ + + bool isEmpty() const; + +/* + * Method: clear + * Usage: g.clear(); + * ----------------- + * Reinitializes the graph to be empty, freeing any heap storage. + */ + + void clear(); + +/* + * Method: addNode + * Usage: NodeType *node = g.addNode(name); + * NodeType *node = g.addNode(node); + * ---------------------------------------- + * Adds a node to the graph. The first version of this method + * creates a new node of the appropriate type and initializes its + * fields; the second assumes that the client has already created + * the node and simply adds it to the graph. Both versions of this + * method return a pointer to the node. + */ + + NodeType *addNode(std::string name); + NodeType *addNode(NodeType *node); + +/* + * Method: removeNode + * Usage: g.removeNode(name); + * g.removeNode(node); + * -------------------------- + * Removes a node from the graph, where the node can be specified + * either by its name or as a pointer value. Removing a node also + * removes all arcs that contain that node. + */ + + void removeNode(std::string name); + void removeNode(NodeType *node); + +/* + * Method: getNode + * Usage: NodeType *node = g.getNode(name); + * ---------------------------------------- + * Looks up a node in the name table attached to the graph and + * returns a pointer to that node. If no node with the specified + * name exists, <code>getNode</code> returns <code>NULL</code>. + */ + + NodeType *getNode(std::string name) const; + +/* + * Method: addArc + * Usage: g.addArc(s1, s2); + * g.addArc(n1, n2); + * g.addArc(arc); + * --------------------- + * Adds an arc to the graph. The endpoints of the arc can be specified + * either as strings indicating the names of the nodes or as pointers + * to the node structures. Alternatively, the client can create the arc + * structure explicitly and pass that pointer to the <code>addArc</code> + * method. All three of these versions return a pointer to the arc in + * case the client needs to capture this value. + */ + + ArcType *addArc(std::string s1, std::string s2); + ArcType *addArc(NodeType *n1, NodeType *n2); + ArcType *addArc(ArcType *arc); + +/* + * Method: removeArc + * Usage: g.removeArc(s1, s2); + * g.removeArc(n1, n2); + * g.removeArc(arc); + * ------------------------ + * Removes an arc from the graph, where the arc can be specified in any + * of three ways: by the names of its endpoints, by the node pointers + * at its endpoints, or as an arc pointer. If more than one arc + * connects the specified endpoints, all of them are removed. + */ + + void removeArc(std::string s1, std::string s2); + void removeArc(NodeType *n1, NodeType *n2); + void removeArc(ArcType *arc); + +/* + * Method: isConnected + * Usage: if (g.isConnected(n1, n2)) ... + * if (g.isConnected(s1, s2)) ... + * ------------------------------------- + * Returns <code>true</code> if the graph contains an arc from + * <code>n1</code> to <code>n2</code>. As in the <code>addArc</code> + * method, nodes can be specified either as node pointers or by name. + */ + + bool isConnected(NodeType *n1, NodeType *n2) const; + bool isConnected(std::string s1, std::string s2) const; + +/* + * Method: getNodeSet + * Usage: foreach (NodeType *node in g.getNodeSet()) ... + * ----------------------------------------------------- + * Returns the set of all nodes in the graph. + */ + + const Set<NodeType *> & getNodeSet() const; + +/* + * Method: getArcSet + * Usage: foreach (ArcType *arc in g.getArcSet()) ... + * foreach (ArcType *arc in g.getArcSet(node)) ... + * foreach (ArcType *arc in g.getArcSet(name)) ... + * ------------------------------------------------------ + * Returns the set of all arcs in the graph or, in the second and + * third forms, the arcs that start at the specified node, which + * can be indicated either as a pointer or by name. + */ + + const Set<ArcType *> & getArcSet() const; + const Set<ArcType *> & getArcSet(NodeType *node) const; + const Set<ArcType *> & getArcSet(std::string name) const; + +/* + * Method: getNeighbors + * Usage: foreach (NodeType *node in g.getNeighbors(node)) ... + * foreach (NodeType *node in g.getNeighbors(name)) ... + * ----------------------------------------------------------- + * Returns the set of nodes that are neighbors of the specified + * node, which can be indicated either as a pointer or by name. + */ + + const Set<NodeType *> getNeighbors(NodeType *node) const; + const Set<NodeType *> getNeighbors(std::string node) const; + +/* + * Method: toString + * Usage: string str = g.toString(); + * --------------------------------- + * Converts the graph to a printable string representation. + */ + + std::string toString(); + +/* + * Friend method: writeNodeData + * Usage: writeNodeData(os, NodeType *node); + * ----------------------------------------- + * Writes the data for the node to the output stream. The default + * implementation of this method is empty. Clients that want to store + * other fields from the node must override this method so that it + * writes that data in a form that scanNodeData can read. + */ + + virtual void writeNodeData(std::ostream &, NodeType *) const { + /* Empty */ + } + +/* + * Friend method: writeArcData + * Usage: writeArcData(os, ArcType *arc); + * -------------------------------------- + * Writes the data for the arc to the output stream. The default + * implementation of this method is empty. Clients that want to store + * other fields from the arc must override this method so that it writes + * that data in a form that scanArcData can read. + */ + + virtual void writeArcData(std::ostream &, ArcType *) const { + /* Empty */ + } + +/* + * Friend method: scanGraphEntry + * Usage: while (g.scanGraphEntry(scanner)) { } + * -------------------------------------------- + * This method reads one "entry" for the graph, which is either a node + * description or an arc description. The <code>scanGraphEntry</code> + * method returns <code>true</code> if it reads an entry, and + * <code>false</code> at the end of file or at text that cannot be + * recognized as a graph entry. + * + * <p>Node entries consist of the name of a node (which may be quoted + * if it contains special characters), optionally followed by data for + * the node. Arc descriptions have one of the following forms: + * + *<pre> + * n1 -> n2 + * n1 - n2 + *</pre> + * + * either of which can be followed by data for the arc. The first form + * creates a single directed arc; the second creates two arcs, one in + * each direction. + * + * <p>Clients who want to read node or arc data must override the empty + * versions of <code>scanNodeData</code> and <code>scanArcData</code> + * included in this interface. + */ + + virtual bool scanGraphEntry(TokenScanner & scanner); + +/* + * Friend method: scanNodeData + * Usage: scanNodeData(scanner, NodeType *node); + * --------------------------------------------- + * Reads the data for the specified node from the scanner. The default + * implementation of this method is empty. Clients that want to initialize + * other fields in the node from the token stream must override this method. + */ + + virtual void scanNodeData(TokenScanner &, NodeType *) { + /* Empty */ + } + +/* + * Friend method: scanArcData + * Usage: scanArcData(scanner, ArcType *forward, ArcType *backward); + * ----------------------------------------------------------------- + * Reads the data for an arc from the scanner. The <code>forward</code> + * argument points to the arc in the forward direction. If the arc is + * undirected, <code>backward</code> points to the reverse arc; for + * directed arcs, the <code>backward</code> pointer is <code>NULL</code>. + * The default implementation of this method is empty. Clients that want + * to initialize other fields in the arc must override this method so + * that it initializes one or both arc, as appropriate. + */ + + virtual void scanArcData(TokenScanner &, ArcType *, ArcType *) { + /* Empty */ + } + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Private class: GraphComparator + * ------------------------------ + * This template class establishes the ordering for nodes and arcs. + * Nodes are processed in alphabetical order by node name; arcs are + * compared in much the same way, looking first at the start node and + * then continuing on to look at the finish node if the start nodes + * match. These functions, however, indicate equality only if the + * arguments are identical, in the sense that they are at the same + * address. If two distinct arcs, for example, connect the same pair + * of nodes (which is perfectly legal in the graph abstraction and can + * be used, for example, to represent multiple modes of travel between + * two nodes), those arcs are not the same. + */ + + class GraphComparator { + public: + + bool operator()(NodeType *n1, NodeType *n2) { + return compare(n1, n2) < 0; + } + + bool operator()(ArcType *a1, ArcType *a2) { + return compare(a1, a2) < 0; + } + + }; + +private: + +/* Instance variables */ + + Set<NodeType *> nodes; /* The set of nodes in the graph */ + Set<ArcType *> arcs; /* The set of arcs in the graph */ + Map<std::string, NodeType *> nodeMap; /* A map from names to nodes */ + GraphComparator comparator; /* The comparator for this graph */ + +/* + * Functions: operator=, copy constructor + * -------------------------------------- + * These functions are part of the public interface of the class but are + * defined here to avoid adding confusion to the Graph class. + */ + +public: + + Graph & operator=(const Graph & src); + Graph(const Graph & src); + + static int compare(NodeType *n1, NodeType *n2) { + if (n1 == n2) return 0; + if (n1->name < n2->name) return -1; + if (n1->name > n2->name) return +1; + return (n1 < n2) ? -1 : +1; + } + + static int compare(ArcType *a1, ArcType *a2) { + if (a1 == a2) return 0; + NodeType *n1 = a1->start; + NodeType *n2 = a2->start; + if (n1 != n2) return compare(n1, n2); + n1 = a1->finish; + n2 = a2->finish; + if (n1 != n2) return compare(n1, n2); + return (a1 < a2) ? -1 : +1; + } + +private: + + void deepCopy(const Graph & src); + NodeType *getExistingNode(std::string name) const; + NodeType *scanNode(TokenScanner & scanner); + +}; + +extern void error(std::string msg); + +/* + * Implementation notes: Graph constructor + * --------------------------------------- + * Even though the body of the Graph constructor is empty, important + * work is done by the initializers, which ensure that the nodes and + * arcs set are given the correct comparison functions. + */ + +template <typename NodeType,typename ArcType> +Graph<NodeType,ArcType>::Graph() { + comparator = GraphComparator(); + nodes = Set<NodeType *>(comparator); + arcs = Set<ArcType *>(comparator); +} + +/* + * Implementation notes: Graph destructor + * -------------------------------------- + * The destructor must free all heap storage used by this graph to + * represent the nodes and arcs. The clear metho must also reclaim + * this memory, which means that the destructor can simply call + * clear to do the work. + */ + +template <typename NodeType,typename ArcType> +Graph<NodeType,ArcType>::~Graph() { + clear(); +} + +/* + * Implementation notes: size, isEmpty + * ----------------------------------- + * These methods are defined in terms of the node set, so the implementation + * simply forwards the request there. Note that it is impossible for a + * graph to have arcs if it has no nodes. + */ + +template <typename NodeType,typename ArcType> +int Graph<NodeType,ArcType>::size() const { + return nodes.size(); +} + +template <typename NodeType,typename ArcType> +bool Graph<NodeType,ArcType>::isEmpty() const { + return nodes.isEmpty(); +} + +/* + * Implementation notes: clear + * --------------------------- + * The implementation of clear first frees the nodes and arcs in + * their respective sets and then uses the Set class clear method + * to ensure that these sets are empty. + */ + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::clear() { + foreach (NodeType *node in nodes) { + delete node; + } + foreach (ArcType *arc in arcs) { + delete arc; + } + arcs.clear(); + nodes.clear(); + nodeMap.clear(); +} + +/* + * Implementation notes: addNode + * ----------------------------- + * The addNode method appears in two forms: one that creates a node + * from its name and one that assumes that the client has created + * the new node. In each case, the implementation must add the node + * the set of nodes for the graph and add the name-to-node association + * to the node map. + */ + +template <typename NodeType,typename ArcType> +NodeType *Graph<NodeType,ArcType>::addNode(std::string name) { + NodeType *node = new NodeType(); + node->arcs = Set<ArcType *>(comparator); + node->name = name; + return addNode(node); +} + +template <typename NodeType,typename ArcType> +NodeType *Graph<NodeType,ArcType>::addNode(NodeType *node) { + if (nodeMap.containsKey(node->name)) { + error("addNode: node " + node->name + " already exists"); + } + nodes.add(node); + nodeMap[node->name] = node; + return node; +} + +/* + * Implementation notes: removeNode + * -------------------------------- + * The removeNode method must remove the specified node but must + * also remove any arcs in the graph containing the node. To avoid + * changing the node set during iteration, this implementation creates + * a vector of arcs that require deletion. + */ + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::removeNode(std::string name) { + removeNode(getExistingNode(name)); +} + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::removeNode(NodeType *node) { + Vector<ArcType *> toRemove; + foreach (ArcType *arc in arcs) { + if (arc->start == node || arc->finish == node) { + toRemove.add(arc); + } + } + foreach (ArcType *arc in toRemove) { + removeArc(arc); + } + nodes.remove(node); +} + +/* + * Implementation notes: getNode, getExistingNode + * ---------------------------------------------- + * The getNode method simply looks up the name in the map, which correctly + * returns NULL if the name is not found. Other methods in the + * implementation call the private method getExistingNode instead, + * which checks for a NULL value and signals an error. + */ + +template <typename NodeType,typename ArcType> +NodeType *Graph<NodeType,ArcType>::getNode(std::string name) const { + return nodeMap.get(name); +} + +template <typename NodeType,typename ArcType> +NodeType *Graph<NodeType,ArcType>::getExistingNode(std::string name) const { + NodeType *node = nodeMap.get(name); + if (node == NULL) error("Graph class: No node named " + name); + return node; +} + +/* + * Implementation notes: addArc + * ---------------------------- + * The addArc method appears in three forms, as described in the + * interface. The code for each form of the method, however, is + * quite straightforward. + */ + +template <typename NodeType,typename ArcType> +ArcType *Graph<NodeType,ArcType>::addArc(std::string s1, std::string s2) { + return addArc(getExistingNode(s1), getExistingNode(s2)); +} + +template <typename NodeType,typename ArcType> +ArcType *Graph<NodeType,ArcType>::addArc(NodeType *n1, NodeType *n2) { + ArcType *arc = new ArcType(); + arc->start = n1; + arc->finish = n2; + return addArc(arc); +} + +template <typename NodeType,typename ArcType> +ArcType *Graph<NodeType,ArcType>::addArc(ArcType *arc) { + arc->start->arcs.add(arc); + arcs.add(arc); + return arc; +} + +/* + * Implementation notes: removeArc + * ------------------------------- + * These methods remove arcs from the graph, which is ordinarily simply + * a matter of removing the arc from two sets: the set of arcs in the + * graph as a whole and the set of arcs in the starting node. The + * methods that remove an arc specified by its endpoints, however, + * must take account of the fact that there might be more than one + * such arc and delete all of them. + */ + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::removeArc(std::string s1, std::string s2) { + removeArc(getExistingNode(s1), getExistingNode(s2)); +} + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::removeArc(NodeType *n1, NodeType *n2) { + Vector<ArcType *> toRemove; + foreach (ArcType *arc in arcs) { + if (arc->start == n1 && arc->finish == n2) { + toRemove.add(arc); + } + } + foreach (ArcType *arc in toRemove) { + removeArc(arc); + } +} + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::removeArc(ArcType *arc) { + arc->start->arcs.remove(arc); + arcs.remove(arc); +} + +/* + * Implementation notes: isConnected + * --------------------------------- + * Node n1 is connected to n2 if any of the arcs leaving n1 finish at n2. + * The two versions of this method allow nodes to be specified either as + * node pointers or by name. + */ + +template <typename NodeType,typename ArcType> +bool Graph<NodeType,ArcType>::isConnected(NodeType *n1, NodeType *n2) const { + foreach (ArcType *arc in n1->arcs) { + if (arc->finish == n2) return true; + } + return false; +} + +template <typename NodeType,typename ArcType> +bool Graph<NodeType,ArcType>::isConnected(std::string s1, + std::string s2) const { + return isConnected(getExistingNode(s1), getExistingNode(s2)); +} + +/* + * Implementation notes: getNodeSet, getArcSet + * ------------------------------------------- + * These methods simply return the set requested by the client. The + * sets are returned by reference for efficiency, because doing so + * eliminates the need to copy the set. + */ + +template <typename NodeType,typename ArcType> +const Set<NodeType *> & Graph<NodeType,ArcType>::getNodeSet() const { + return nodes; +} + +template <typename NodeType,typename ArcType> +const Set<ArcType *> & Graph<NodeType,ArcType>::getArcSet() const { + return arcs; +} + +template <typename NodeType,typename ArcType> +const Set<ArcType *> & + Graph<NodeType,ArcType>::getArcSet(NodeType *node) const { + return node->arcs; +} + +template <typename NodeType,typename ArcType> +const Set<ArcType *> & + Graph<NodeType,ArcType>::getArcSet(std::string name) const { + return getArcSet(getExistingNode(name)); +} + +/* + * Implementation notes: getNeighbors + * ---------------------------------- + * This implementation recomputes the set each time, which is reasonably + * efficient if the degree of the node is small. + */ + +template <typename NodeType,typename ArcType> +const Set<NodeType *> + Graph<NodeType,ArcType>::getNeighbors(NodeType *node) const { + Set<NodeType *> nodes = Set<NodeType *>(comparator); + foreach (ArcType *arc in node->arcs) { + nodes.add(arc->finish); + } + return nodes; +} + +template <typename NodeType,typename ArcType> +const Set<NodeType *> + Graph<NodeType,ArcType>::getNeighbors(std::string name) const { + return getNeighbors(getExistingNode(name)); +} + +/* + * Implementation notes: operator=, copy constructor + * ------------------------------------------------- + * These methods ensure that copying a graph creates an entirely new + * parallel structure of nodes and arcs. + */ + +template <typename NodeType,typename ArcType> +Graph<NodeType,ArcType> + & Graph<NodeType,ArcType>::operator=(const Graph & src) { + if (this != &src) { + clear(); + deepCopy(src); + } + return *this; +} + +template <typename NodeType,typename ArcType> +Graph<NodeType,ArcType>::Graph(const Graph & src) { + nodes = Set<NodeType *>(comparator); + arcs = Set<ArcType *>(comparator); + deepCopy(src); +} + +/* + * Private method: deepCopy + * ------------------------ + * Common code factored out of the copy constructor and operator= to + * copy the contents from the other graph. + */ + +template <typename NodeType,typename ArcType> +void Graph<NodeType,ArcType>::deepCopy(const Graph & src) { + foreach (NodeType *oldNode in src.nodes) { + NodeType *newNode = new NodeType(); + *newNode = *oldNode; + newNode->arcs.clear(); + addNode(newNode); + } + foreach (ArcType *oldArc in src.arcs) { + ArcType *newArc = new ArcType(); + *newArc = *oldArc; + newArc->start = getExistingNode(oldArc->start->name); + newArc->finish = getExistingNode(oldArc->finish->name); + addArc(newArc); + } +} + +template <typename NodeType,typename ArcType> +std::string Graph<NodeType,ArcType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +/* + * Implementation notes: scanGraphEntry + * ------------------------------------ + * The scanGraphEntry and its helper methods take a scanner that is + * initialized to the input stream and has the options ignoreWhitespace, + * scanStrings, and scanNumbers set. + */ + +template <typename NodeType,typename ArcType> +bool Graph<NodeType,ArcType>::scanGraphEntry(TokenScanner & scanner) { + NodeType *n1 = scanNode(scanner); + if (n1 == NULL) return false; + std::string op = scanner.nextToken(); + if (op != "-" && op != "->") { + scanner.saveToken(op); + return true; + } + NodeType *n2 = scanNode(scanner); + if (n2 == NULL) error("scanGraphEntry: Missing node after " + op); + ArcType *forward = new ArcType(); + forward->start = n1; + forward->finish = n2; + addArc(forward); + ArcType *backward = NULL; + if (op == "-") { + backward = new ArcType(); + backward->start = n2; + backward->finish = n1; + addArc(backward); + } + scanArcData(scanner, forward, backward); + return true; +} + +template <typename NodeType,typename ArcType> +NodeType *Graph<NodeType,ArcType>::scanNode(TokenScanner & scanner) { + std::string token = scanner.nextToken(); + switch (scanner.getTokenType(token)) { + case WORD: break; + case STRING: token = scanner.getStringValue(token); break; + default: scanner.saveToken(token); return NULL; + } + NodeType *node = getNode(token); + if (node == NULL) { + node = new NodeType(); + node->name = token; + scanNodeData(scanner, node); + addNode(node); + } + return node; +} + +/* + * Implementation notes: << and >> + * ------------------------------- + * The insertion and extraction operators for graphs are more complicated + * than for the standard collection types because the nodes and arcs can + * contain client-specific data. To ensure that this information is + * correctly written and read by these operators, clients must override + * the methods writeNodeData, writeArcData, scanNodeData, and scanArcData. + */ + +template <typename NodeType,typename ArcType> +std::ostream & operator<<(std::ostream & os, + const Graph<NodeType,ArcType> & g) { + os << "{"; + bool started = false; + foreach (NodeType *node in g.getNodeSet()) { + if (started) os << ", "; + writeGenericValue(os, node->name, false); + g.writeNodeData(os, node); + started = true; + } + foreach (ArcType *arc in g.getArcSet()) { + os << ", "; + writeGenericValue(os, arc->start->name, false); + os << " -> "; + writeGenericValue(os, arc->finish->name, false); + g.writeArcData(os, arc); + } + return os << "}"; +} + +template <typename NodeType,typename ArcType> +std::istream & operator>>(std::istream & is, Graph<NodeType,ArcType> & g) { + TokenScanner scanner(is); + scanner.ignoreWhitespace(); + scanner.scanNumbers(); + scanner.scanStrings(); + scanner.addOperator("->"); + std::string token = scanner.nextToken(); + if (token != "{") error("operator >>: Missing {"); + g.clear(); + while (g.scanGraphEntry(scanner)) { + token = scanner.nextToken(); + if (token == "}") { + scanner.saveToken(token); + } else if (token != ",") { + error("operator >>: Unexpected token " + token); + } + } + token = scanner.nextToken(); + if (token != "}") error("operator >>: Missing }"); + return is; +} + +#endif diff --git a/labb8/lib/StanfordCPPLib/grid.h b/labb8/lib/StanfordCPPLib/grid.h new file mode 100755 index 0000000..0a23b46 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/grid.h @@ -0,0 +1,516 @@ +/* + * File: grid.h + * ------------ + * This file exports the <code>Grid</code> class, which offers a + * convenient abstraction for representing a two-dimensional array. + */ + +#ifndef _grid_h +#define _grid_h + +#include "foreach.h" +#include "strlib.h" +#include "vector.h" + +/* + * Class: Grid<ValueType> + * ---------------------- + * This class stores an indexed, two-dimensional array. The following code, + * for example, creates an identity matrix of size <code>n</code>, in which + * the elements are 1.0 along the main diagonal and 0.0 everywhere else: + * + *<pre> + * Grid<double> createIdentityMatrix(int n) { + * Grid<double> matrix(n, n); + * for (int i = 0; i < n; i++) { + * matrix[i][i] = 1.0; + * } + * return matrix; + * } + *</pre> + */ + +template <typename ValueType> +class Grid { + +public: + +/* Forward reference */ + class GridRow; + +/* + * Constructor: Grid + * Usage: Grid<ValueType> grid; + * Grid<ValueType> grid(nRows, nCols); + * ------------------------------------------ + * Initializes a new grid. The second form of the constructor is + * more common and creates a grid with the specified number of rows + * and columns. Each element of the grid is initialized to the + * default value for the type. The default constructor creates an + * empty grid for which the client must call <code>resize</code> to + * set the dimensions. + */ + + Grid(); + Grid(int nRows, int nCols); + +/* + * Destructor: ~Grid + * ----------------- + * Frees any heap storage associated with this grid. + */ + + virtual ~Grid(); + +/* + * Method: numRows + * Usage: int nRows = grid.numRows(); + * ---------------------------------- + * Returns the number of rows in the grid. + */ + + int numRows() const; + +/* + * Method: numCols + * Usage: int nCols = grid.numCols(); + * ---------------------------------- + * Returns the number of columns in the grid. + */ + + int numCols() const; + +/* + * Method: resize + * Usage: grid.resize(nRows, nCols); + * --------------------------------- + * Reinitializes the grid to have the specified number of rows + * and columns. Any previous grid contents are discarded. + */ + + void resize(int nRows, int nCols); + +/* + * Method: inBounds + * Usage: if (grid.inBounds(row, col)) ... + * --------------------------------------- + * Returns <code>true</code> if the specified row and column position + * is inside the bounds of the grid. + */ + + bool inBounds(int row, int col) const; + +/* + * Method: get + * Usage: ValueType value = grid.get(row, col); + * -------------------------------------------- + * Returns the element at the specified <code>row</code>/<code>col</code> + * position in this grid. This method signals an error if the + * <code>row</code> and <code>col</code> arguments are outside + * the grid boundaries. + */ + + ValueType get(int row, int col); + const ValueType & get(int row, int col) const; + +/* + * Method: set + * Usage: grid.set(row, col, value); + * --------------------------------- + * Replaces the element at the specified <code>row</code>/<code>col</code> + * location in this grid with a new value. This method signals an error + * if the <code>row</code> and <code>col</code> arguments are outside + * the grid boundaries. + */ + + void set(int row, int col, ValueType value); + +/* + * Operator: [] + * Usage: grid[row][col] + * ---------------------- + * Overloads <code>[]</code> to select elements from this grid. + * This extension enables the use of traditional array notation to + * get or set individual elements. This method signals an error if + * the <code>row</code> and <code>col</code> arguments are outside + * the grid boundaries. + */ + + GridRow operator[](int row); + const GridRow operator[](int row) const; + +/* + * Method: toString + * Usage: string str = grid.toString(); + * ------------------------------------ + * Converts the grid to a printable string representation. + */ + + std::string toString(); + +/* + * Method: mapAll + * Usage: grid.mapAll(fn); + * ----------------------- + * Calls the specified function on each element of the grid. The + * elements are processed in <b><i>row-major order,</i></b> in which + * all the elements of row 0 are processed, followed by the elements + * in row 1, and so on. + */ + + void mapAll(void (*fn)(ValueType value)) const; + void mapAll(void (*fn)(const ValueType & value)) const; + + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional Grid operations + * -------------------------- + * In addition to the methods listed in this interface, the Grid + * class supports the following operations: + * + * - Stream I/O using the << and >> operators + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement and STL iterators + * + * The iteration forms process the grid in row-major order. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Implementation notes: Grid data structure + * ----------------------------------------- + * The Grid is internally managed as a dynamic array of elements. + * The array itself is one-dimensional, the logical separation into + * rows and columns is done by arithmetic computation. The layout + * is in row-major order, which is to say that the entire first row + * is laid out contiguously, followed by the entire second row, + * and so on. + */ + +/* Instance variables */ + + ValueType *elements; /* A dynamic array of the elements */ + int nRows; /* The number of rows in the grid */ + int nCols; /* The number of columns in the grid */ + +/* Private method prototypes */ + + void checkRange(int row, int col); + +/* + * Hidden features + * --------------- + * The remainder of this file consists of the code required to + * support deep copying and iteration. Including these methods + * in the public interface would make that interface more + * difficult to understand for the average client. + */ + +/* + * Deep copying support + * -------------------- + * This copy constructor and operator= are defined to make a + * deep copy, making it possible to pass/return grids by value + * and assign from one grid to another. The entire contents of + * the grid, including all elements, are copied. Each grid + * element is copied from the original grid to the copy using + * assignment (operator=). Making copies is generally avoided + * because of the expense and thus, grids are typically passed + * by reference, however, when a copy is needed, these operations + * are supported. + */ + + void deepCopy(const Grid & grid) { + int n = grid.nRows * grid.nCols; + elements = new ValueType[n]; + for (int i = 0; i < n; i++) { + elements[i] = grid.elements[i]; + } + nRows = grid.nRows; + nCols = grid.nCols; + } + +public: + + Grid & operator=(const Grid & src) { + if (this != &src) { + delete[] elements; + deepCopy(src); + } + return *this; + } + + Grid(const Grid & src) { + deepCopy(src); + } + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : public std::iterator<std::input_iterator_tag, ValueType> { + + public: + + iterator(const Grid *gp, int index) { + this->gp = gp; + this->index = index; + } + + iterator(const iterator & it) { + this->gp = it.gp; + this->index = it.index; + } + + iterator & operator++() { + index++; + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + bool operator==(const iterator & rhs) { + return gp == rhs.gp && index == rhs.index; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + ValueType & operator*() { + return gp->elements[index]; + } + + ValueType *operator->() { + return &gp->elements[index]; + } + + private: + const Grid *gp; + int index; + }; + + iterator begin() const { + return iterator(this, 0); + } + + iterator end() const { + return iterator(this, nRows * nCols); + } + +/* + * Private class: Grid<ValType>::GridRow + * ------------------------------------- + * This section of the code defines a nested class within the Grid template + * that makes it possible to use traditional subscripting on Grid values. + */ + + class GridRow { + public: + GridRow() { + /* Empty */ + } + + ValueType & operator[](int col) { + extern void error(std::string msg); + if (!gp->inBounds(row, col)) { + error("Grid index values out of range"); + } + return gp->elements[(row * gp->nCols) + col]; + } + + ValueType operator[](int col) const { + extern void error(std::string msg); + if (!gp->inBounds(row, col)) { + error("Grid index values out of range"); + } + return gp->elements[(row * gp->nCols) + col]; + } + + private: + GridRow(Grid *gridRef, int index) { + gp = gridRef; + row = index; + } + + Grid *gp; + int row; + friend class Grid; + }; + friend class GridRow; + +}; + +extern void error(std::string msg); + +template <typename ValueType> +Grid<ValueType>::Grid() { + elements = NULL; + nRows = 0; + nCols = 0; +} + +template <typename ValueType> +Grid<ValueType>::Grid(int nRows, int nCols) { + elements = NULL; + resize(nRows, nCols); +} + +template <typename ValueType> +Grid<ValueType>::~Grid() { + if (elements != NULL) delete[] elements; +} + +template <typename ValueType> +int Grid<ValueType>::numRows() const { + return nRows; +} + +template <typename ValueType> +int Grid<ValueType>::numCols() const { + return nCols; +} + +template <typename ValueType> +void Grid<ValueType>::resize(int nRows, int nCols) { + if (nRows < 0 || nCols < 0) { + error("Attempt to resize grid to invalid size (" + + integerToString(nRows) + ", " + + integerToString(nCols) + ")"); + } + if (elements != NULL) delete[] elements; + this->nRows = nRows; + this->nCols = nCols; + elements = new ValueType[nRows * nCols]; + ValueType value = ValueType(); + for (int i = 0; i < nRows * nCols; i++) { + elements[i] = value; + } +} + +template <typename ValueType> +bool Grid<ValueType>::inBounds(int row, int col) const { + return row >= 0 && col >= 0 && row < nRows && col < nCols; +} + +template <typename ValueType> +ValueType Grid<ValueType>::get(int row, int col) { + if (!inBounds(row, col)) error("get: Grid indices out of bounds"); + return elements[(row * nCols) + col]; +} + +template <typename ValueType> +const ValueType & Grid<ValueType>::get(int row, int col) const { + if (!inBounds(row, col)) error("get: Grid indices out of bounds"); + return elements[(row * nCols) + col]; +} + +template <typename ValueType> +void Grid<ValueType>::set(int row, int col, ValueType value) { + if (!inBounds(row, col)) error("set: Grid indices out of bounds"); + elements[(row * nCols) + col] = value; +} + +template <typename ValueType> +typename Grid<ValueType>::GridRow Grid<ValueType>::operator[](int row) { + return GridRow(this, row); +} + +template <typename ValueType> +const typename Grid<ValueType>::GridRow + Grid<ValueType>::operator[](int row) const { + return GridRow(this, row); +} + +template <typename ValueType> +void Grid<ValueType>::mapAll(void (*fn)(ValueType value)) const { + for (int i = 0; i < nRows; i++) { + for (int j = 0; j < nCols; j++) { + fn(get(i, j)); + } + } +} + +template <typename ValueType> +void Grid<ValueType>::mapAll(void (*fn)(const ValueType & value)) const { + for (int i = 0; i < nRows; i++) { + for (int j = 0; j < nCols; j++) { + fn(get(i, j)); + } + } +} + +template <typename ValueType> +template <typename FunctorType> +void Grid<ValueType>::mapAll(FunctorType fn) const { + for (int i = 0; i < nRows; i++) { + for (int j = 0; j < nCols; j++) { + fn(get(i, j)); + } + } +} + +template <typename ValueType> +std::string Grid<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +/* + * Implementation notes: << and >> + * ------------------------------- + * The insertion and extraction operators use the template facilities in + * strlib.h to read and write generic values in a way that treats strings + * specially. + */ + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, const Grid<ValueType> & grid) { + os << "{"; + int nRows = grid.numRows(); + int nCols = grid.numCols(); + for (int i = 0; i < nRows; i++) { + if (i > 0) os << ", "; + os << "{"; + for (int j = 0; j < nCols; j++) { + if (j > 0) os << ", "; + writeGenericValue(os, grid.get(i, j), true); + } + os << "}"; + } + return os << "}"; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, Grid<ValueType> & grid) { + Vector< Vector<ValueType> > vec2d; + is >> vec2d; + int nRows = vec2d.size(); + int nCols = (nRows == 0) ? 0 : vec2d[0].size(); + grid.resize(nRows, nCols); + for (int i = 0; i < nRows; i++) { + for (int j = 0; j < nCols; j++) { + grid[i][j] = vec2d[i][j]; + } + } + return is; +} + +#endif diff --git a/labb8/lib/StanfordCPPLib/gtimer.cpp b/labb8/lib/StanfordCPPLib/gtimer.cpp new file mode 100755 index 0000000..20e160d --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gtimer.cpp @@ -0,0 +1,62 @@ +/* + * File: gtimer.cpp + * ---------------- + * This file implements the gtimer.h interface. + */ + +#include <iostream> +#include <sstream> +#include <string> +#include "platform.h" +using namespace std; + +/* Global variables */ + +static Platform *pp = getPlatform(); + +/* Implementation of the GTimer class */ + +GTimer::GTimer(double milliseconds) { + gtd = new GTimerData(); + gtd->refCount = 1; + pp->createTimer(*this, milliseconds); +} + +GTimer::~GTimer() { + if (--gtd->refCount == 0) delete gtd; +} + +void GTimer::start() { + pp->startTimer(*this); +} + +void GTimer::stop() { + pp->stopTimer(*this); +} + +bool GTimer::operator==(GTimer t2) { + return gtd == t2.gtd; +} + +bool GTimer::operator!=(GTimer t2) { + return gtd != t2.gtd; +} + +GTimer::GTimer(GTimerData *gtd) { + this->gtd = gtd; + gtd->refCount++; +} + +GTimer::GTimer(const GTimer & src) { + this->gtd = src.gtd; + this->gtd->refCount++; +} + +GTimer & GTimer::operator=(const GTimer & src) { + if (this != &src) { + if (--gtd->refCount == 0) delete gtd; + this->gtd = src.gtd; + this->gtd->refCount++; + } + return *this; +} diff --git a/labb8/lib/StanfordCPPLib/gtimer.h b/labb8/lib/StanfordCPPLib/gtimer.h new file mode 100755 index 0000000..a0a6d7d --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gtimer.h @@ -0,0 +1,116 @@ +/* + * File: gtimer.h + * -------------- + * This file defines the <code>GTimer</code> class, which implements a + * general interval timer. + */ + +#ifndef _gtimer_h +#define _gtimer_h + +#include <string> + +/* + * Friend type: GTimerData + * ----------------------- + * This type maintains a reference count to determine when it is + * possible to free the timer. The address of this block is used + * as the timer id. + */ + +struct GTimerData { + int refCount; +}; + +/* + * Class: GTimer + * ------------- + * This class implements a simple interval timer that generates a + * <code>GTimerEvent</code> with a specified frequency. Copying + * a <code>GTimer</code> object is legal and creates an object that + * refers to the same internal timer. + */ + +class GTimer { + +public: + +/* + * Constructor: GTimer + * Usage: GTimer timer(milliseconds); + * ---------------------------------- + * Creates a timer object that generates a <code>GTimerEvent</code> + * each time the specified number of milliseconds has elapsed. No + * events are generated until the client calls <code>start</code> + * on the timer. For more details on using timers, see the documentation + * for the <a href="GTimerEvent-class.html"><code>GTimerEvent</code></a> + * class. + */ + + GTimer(double milliseconds); + +/* + * Destructor: ~GTimer + * ------------------- + * Frees the resources associated with the timer. + */ + + virtual ~GTimer(); + +/* + * Method: start + * Usage: timer.start(); + * --------------------- + * Starts the timer. A timer continues to generate timer events until it + * is stopped; to achieve the effect of a one-shot timer, the simplest + * approach is to call the <code>stop</code> method inside the event + * handler. + */ + + void start(); + +/* + * Method: stop + * Usage: timer.stop(); + * -------------------- + * Stops the timer so that it stops generating events until it is restarted. + */ + + void stop(); + +/* + * Friend operator: == + * Usage: if (t1 == t2) ... + * ------------------------ + * Checks whether the two objects refer to the same timer. + */ + + bool operator==(GTimer t2); + +/* + * Friend operator: != + * Usage: if (t1 != t2) ... + * ------------------------ + * Checks whether the two objects refer to the different timers. + */ + + bool operator!=(GTimer t2); + +/* Private section */ + + GTimer(GTimerData *gtd); + GTimer(const GTimer & src); + GTimer & operator=(const GTimer & src); + +private: + +/* Instance variables */ + + GTimerData *gtd; + + friend class Platform; + friend class GTimerEvent; + +}; + +#endif diff --git a/labb8/lib/StanfordCPPLib/gtypes.cpp b/labb8/lib/StanfordCPPLib/gtypes.cpp new file mode 100755 index 0000000..2e5c678 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gtypes.cpp @@ -0,0 +1,196 @@ +/* + * File: gtypes.cpp + * ---------------- + * This file implements the classes in the gtypes.h interface. + */ + +#include <string> +#include <cmath> +#include "error.h" +#include "gtypes.h" +#include "strlib.h" +using namespace std; + +static const double PI = 3.14159265358979; +static const int HASH_MASK = int(unsigned(-1) >> 1); + +/* + * Implementation notes: GPoint class + * ---------------------------------- + * The GPoint class itself is entirely straightforward. The relational + * operators compare the x components first, followed by the y component. + * The hashCode function computes the exclusive-or of the individual words. + */ + +GPoint::GPoint() { + x = 0; + y = 0; +} + +GPoint::GPoint(double x, double y) { + this->x = x; + this->y = y; +} + +double GPoint::getX() const { + return x; +} + +double GPoint::getY() const { + return y; +} + +string GPoint::toString() const { + return "(" + realToString(x) + ", " + realToString(y) + ")"; +} + +ostream & operator<<(ostream & os, const GPoint & pt) { + return os << pt.toString(); +} + +bool operator==(const GPoint & p1, const GPoint & p2) { + return p1.x == p2.x && p1.y == p2.y; +} + +bool operator!=(const GPoint & p1, const GPoint & p2) { + return !(p1 == p2); +} + +int hashCode(const GPoint & pt) { + int hash = 0; + for (size_t i = 0; i < sizeof(double) / sizeof(int); i++) { + hash ^= ((int *) &pt.x)[i] ^ ((int *) &pt.y)[i]; + } + return HASH_MASK & hash; +} + +/* + * Implementation notes: GDimension class + * -------------------------------------- + * The GDimension class itself is entirely straightforward. The + * relational operators compare the width first, followed by the height. + * The hashCode function computes the exclusive-or of the individual words. + */ + +GDimension::GDimension() { + width = 0; + height = 0; +} + +GDimension::GDimension(double width, double height) { + this->width = width; + this->height = height; +} + +double GDimension::getWidth() const { + return width; +} + +double GDimension::getHeight() const { + return height; +} + +string GDimension::toString() const { + return "(" + realToString(width) + ", " + realToString(height) + ")"; +} + +ostream & operator<<(ostream & os, const GDimension & dim) { + return os << dim.toString(); +} + +bool operator==(const GDimension & d1, const GDimension & d2) { + return d1.width == d2.width && d1.height == d2.height; +} + +bool operator!=(const GDimension & d1, const GDimension & d2) { + return !(d1 == d2); +} + +int hashCode(const GDimension & dim) { + int hash = 0; + for (size_t i = 0; i < sizeof(double) / sizeof(int); i++) { + hash ^= ((int *) &dim.width)[i] ^ ((int *) &dim.height)[i]; + } + return HASH_MASK & hash; +} + +/* + * Implementation notes: GRectangle class + * -------------------------------------- + * The GRectangle class itself is entirely straightforward. The + * relational operators compare the components in the following order: + * x, y, width, height. The hashCode function computes the exclusive-or + * of the individual words. + */ + +GRectangle::GRectangle() { + x = 0; + y = 0; + width = 0; + height = 0; +} + +GRectangle::GRectangle(double x, double y, double width, double height) { + this->x = x; + this->y = y; + this->width = width; + this->height = height; +} + +double GRectangle::getX() const { + return x; +} + +double GRectangle::getY() const { + return y; +} + +double GRectangle::getWidth() const { + return width; +} + +double GRectangle::getHeight() const { + return height; +} + +bool GRectangle::isEmpty() const { + return width <= 0 || height <= 0; +} + +bool GRectangle::contains(double x, double y) const { + return x >= this->x && y >= this->y + && x < this->x + width + && y < this->y + height; +} + +bool GRectangle::contains(GPoint pt) const { + return contains(pt.getX(), pt.getY()); +} + +string GRectangle::toString() const { + return "(" + realToString(x) + ", " + realToString(y) + ", " + + realToString(width) + ", " + realToString(height) + ")"; +} + +ostream & operator<<(ostream & os, const GRectangle & rect) { + return os << rect.toString(); +} + +bool operator==(const GRectangle & r1, const GRectangle & r2) { + return r1.x == r2.x && r1.y == r2.y + && r1.width == r2.width + && r1.height == r2.height; +} + +bool operator!=(const GRectangle & r1, const GRectangle & r2) { + return !(r1 == r2); +} + +int hashCode(const GRectangle & r) { + int hash = 0; + for (size_t i = 0; i < sizeof(double) / sizeof(int); i++) { + hash ^= ((int *) &r.x)[i] ^ ((int *) &r.y)[i]; + hash ^= ((int *) &r.width)[i] ^ ((int *) &r.height)[i]; + } + return HASH_MASK & hash; +} diff --git a/labb8/lib/StanfordCPPLib/gtypes.h b/labb8/lib/StanfordCPPLib/gtypes.h new file mode 100755 index 0000000..cd55b53 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gtypes.h @@ -0,0 +1,301 @@ +/* + * File: gtypes.h + * -------------- + * This file defines classes for representing points, dimensions, and + * rectangles. + */ + +#ifndef _gtypes_h +#define _gtypes_h + +#include <iostream> +#include <string> + +/* + * Class: GPoint + * ------------- + * This class contains real-valued x and y fields. It is used to + * represent a location on the graphics plane. + */ + +class GPoint { + +public: + +/* + * Constructor: GPoint + * Usage: GPoint origin; + * GPoint pt(x, y); + * ----------------------- + * Creates a <code>GPoint</code> object with the specified <code>x</code> + * and <code>y</code> coordinates. If the coordinates are not supplied, + * the default constructor sets these fields to 0. + */ + + GPoint(); + GPoint(double x, double y); + +/* + * Method: getX + * Usage: double x = pt.getX(); + * ---------------------------- + * Returns the x component of the point. + */ + + double getX() const; + +/* + * Method: getY + * Usage: double y = pt.getY(); + * ---------------------------- + * Returns the y component of the point. + */ + + double getY() const; + +/* + * Method: toString + * Usage: string str = pt.toString(); + * ---------------------------------- + * Converts the <code>GPoint</code> to a string in the form + * <code>"(</code><i>x</i><code>,</code> <i>y</i><code>)"</code>. + */ + + std::string toString() const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in this class is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +/* Instance variables */ + + double x; /* The x-coordinate of the point */ + double y; /* The y-coordinate of the point */ + +/* Friend declarations */ + + friend bool operator==(const GPoint & p1, const GPoint & p2); + friend bool operator!=(const GPoint & p1, const GPoint & p2); + friend int hashCode(const GPoint & pt); + +}; + +/* + * Class: GDimension + * ----------------- + * This class contains real-valued width and height fields. It is used + * to indicate the size of a graphical object. + */ + +class GDimension { + +public: + +/* + * Constructor: GDimension + * Usage: GDimension empty; + * GDimension dim(width, height); + * ------------------------------------- + * Creates a <code>GDimension</code> object with the specified + * <code>width</code> and <code>height</code> coordinates. If the + * coordinates are not supplied, the default constructor sets these + * fields to 0. + */ + + GDimension(); + GDimension(double width, double height); + +/* + * Method: getWidth + * Usage: double width = dim.getWidth(); + * ------------------------------------- + * Returns the width component of the <code>GDimension</code> object. + */ + + double getWidth() const; + +/* + * Method: getHeight + * Usage: double height = dim.getHeight(); + * --------------------------------------- + * Returns the height component of the <code>GDimension</code> object. + */ + + double getHeight() const; + +/* + * Method: toString + * Usage: string str = dim.toString(); + * ----------------------------------- + * Converts the <code>GDimension</code> to a string in the form + * <code>"(</code><i>width</i><code>,</code> <i>height</i><code>)"</code>. + */ + + std::string toString() const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in this class is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +/* Instance variables */ + + double width; /* The width of the GDimension */ + double height; /* The height of the GDimension */ + +/* Friend declarations */ + + friend bool operator==(const GDimension & d1, const GDimension & d2); + friend bool operator!=(const GDimension & d1, const GDimension & d2); + friend int hashCode(const GDimension & dim); + +}; + +/* + * Class: GRectangle + * ----------------- + * This type contains real-valued x, y, width, and height fields. It is + * used to represent the bounding box of a graphical object. + */ + +class GRectangle { + +public: + +/* + * Constructor: GRectangle + * Usage: GRectangle empty; + * GRectangle r(x, y, width, height); + * ----------------------------------------- + * Creates a <code>GRectangle</code> object with the specified components. + * If these parameters are not supplied, the default constructor sets + * these fields to 0. + */ + + GRectangle(); + GRectangle(double x, double y, double width, double height); + +/* + * Method: getX + * Usage: double x = r.getX(); + * --------------------------- + * Returns the x component of the rectangle. + */ + + double getX() const; + +/* + * Method: getY + * Usage: double y = pt.getY(); + * ---------------------------- + * Returns the y component of the rectangle. + */ + + double getY() const; + +/* + * Method: getWidth + * Usage: double width = r.getWidth(); + * ----------------------------------- + * Returns the width component of the rectangle. + */ + + double getWidth() const; + +/* + * Method: getHeight + * Usage: double height = pt.getHeight(); + * -------------------------------------- + * Returns the height component of the rectangle. + */ + + double getHeight() const; + +/* + * Method: isEmpty + * Usage: if (r.isEmpty()) ... + * --------------------------- + * Returns <code>true</code> if the rectangle is empty. + */ + + bool isEmpty() const; + +/* + * Method: contains + * Usage: if (r.contains(pt)) ... + * if (r.contains(x, y)) ... + * -------------------------------- + * Returns <code>true</code> if the rectangle contains the given point, + * which may be specified either as a point or as distinct coordinates. + */ + + bool contains(GPoint pt) const; + bool contains(double x, double y) const; + +/* + * Method: toString + * Usage: string str = r.toString(); + * --------------------------------- + * Converts the <code>GRectangle</code> to a string in the form + * <code>"(</code><i>x</i><code>,</code> <i>y</i><code>,</code> + * <i>width</i><code>,</code> <i>height</i><code>)"</code>. + */ + + std::string toString() const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in this class is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +/* Instance variables */ + + double x; /* The x-coordinate of the rectangle */ + double y; /* The y-coordinate of the rectangle */ + double width; /* The width of the rectangle */ + double height; /* The height of the rectangle */ + +/* Friend declarations */ + + friend bool operator==(const GRectangle & r1, const GRectangle & r2); + friend bool operator!=(const GRectangle & r1, const GRectangle & r2); + friend int hashCode(const GRectangle & r); + +}; + +/* + * Free functions + * -------------- + * This section of the interface defines the insertion, comparison, + * and hashCode functions for the geometric types. + */ + +std::ostream & operator<<(std::ostream & os, const GPoint & pt); +bool operator==(const GPoint & p1, const GPoint & p2); +bool operator!=(const GPoint & p1, const GPoint & p2); +int hashCode(const GPoint & pt); + +std::ostream & operator<<(std::ostream & os, const GDimension & dim); +bool operator==(const GDimension & d1, const GDimension & d2); +bool operator!=(const GDimension & d1, const GDimension & d2); +int hashCode(const GDimension & dim); + +std::ostream & operator<<(std::ostream & os, const GRectangle & rect); +bool operator==(const GRectangle & r1, const GRectangle & r2); +bool operator!=(const GRectangle & r1, const GRectangle & r2); +int hashCode(const GRectangle & r); + +#endif diff --git a/labb8/lib/StanfordCPPLib/gwindow.cpp b/labb8/lib/StanfordCPPLib/gwindow.cpp new file mode 100755 index 0000000..30c9ea8 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gwindow.cpp @@ -0,0 +1,355 @@ +/* + * File: gwindow.cpp + * ----------------- + * This file implements the GWindow class, passing most calls directly + * to the appropriate methods in the Platform class, which is implemented + * separately for each architecture. + */ + +#include <iostream> +#include <iomanip> +#include <sstream> +#include <string> +#include "gevents.h" +#include "gobjects.h" +#include "gmath.h" +#include "gtypes.h" +#include "gwindow.h" +#include "map.h" +#include "strlib.h" +#include "vector.h" +#include "platform.h" +using namespace std; + +/* Constants */ + +static const int DEFAULT_WIDTH = 500; +static const int DEFAULT_HEIGHT = 300; + +/* Private function prototypes */ + +static void initColorTable(); +static string canonicalColorName(string str); + +/* + * Global variable: pp + * ------------------- + * This variable points to a singleton of the Platform class. + */ + +static Platform *pp = getPlatform(); + +/* + * Global variable: colorTable + * --------------------------- + * This variable holds the translation table that maps colors into + * their RGB values. This color table is shared throughout the + * application and cannot be manipulated by any clients. If you + * need to define color names for application specific colors, you + * should do so by defining string constants with the appropriate + * hexadecimal values, as in + * + * const string MAGENTA = "0xFF00FF"; + */ + +static Map<string,int> colorTable; + +GWindow::GWindow() { + initGWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, true); +} + +GWindow::GWindow(bool visible) { + initGWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, visible); +} + +GWindow::GWindow(double width, double height) { + initGWindow(width, height, true); +} + +GWindow::GWindow(double width, double height, bool visible) { + initGWindow(width, height, visible); +} + +void GWindow::initGWindow(double width, double height, bool visible) { + gwd = new GWindowData(); + gwd->windowWidth = width; + gwd->windowHeight = height; + gwd->top = new GCompound(); + pp->createGWindow(*this, width, height, gwd->top); + setColor("BLACK"); + setVisible(visible); + pause(1000); // Temporary fix for race condition in back-end. +} + +GWindow::~GWindow() { + /* Empty */ +} + +void GWindow::close() { + pp->close(*this); + pp->deleteGWindow(*this); +} + +void GWindow::requestFocus() { + pp->requestFocus(*this); +} + +void GWindow::clear() { + gwd->top->removeAll(); + pp->clear(*this); +} + +void GWindow::repaint() { + pp->repaint(*this); +} + +void GWindow::setVisible(bool flag) { + gwd->visible = flag; + pp->setVisible(*this, flag); +} + +bool GWindow::isVisible() { + return gwd->visible; +} + +void GWindow::drawLine(const GPoint & p0, const GPoint & p1) { + drawLine(p0.getX(), p0.getY(), p1.getX(), p1.getY()); +} + +void GWindow::drawLine(double x0, double y0, double x1, double y1) { + GLine line(x0, y0, x1, y1); + line.setColor(gwd->color); + draw(line); +} + +GPoint GWindow::drawPolarLine(const GPoint & p0, double r, double theta) { + return drawPolarLine(p0.getX(), p0.getY(), r, theta); +} + +GPoint GWindow::drawPolarLine(double x0, double y0, double r, double theta) { + double x1 = x0 + r * cosDegrees(theta); + double y1 = y0 - r * sinDegrees(theta); + drawLine(x0, y0, x1, y1); + return GPoint(x1, y1); +} + +void GWindow::drawRect(const GRectangle & bounds) { + drawRect(bounds.getX(), bounds.getY(), bounds.getWidth(), + bounds.getHeight()); +} + +void GWindow::drawRect(double x, double y, double width, double height) { + GRect rect(x, y, width, height); + rect.setColor(gwd->color); + draw(rect); +} + +void GWindow::fillRect(const GRectangle & bounds) { + fillRect(bounds.getX(), bounds.getY(), bounds.getWidth(), + bounds.getHeight()); +} + +void GWindow::fillRect(double x, double y, double width, double height) { + GRect rect(x, y, width, height); + rect.setColor(gwd->color); + rect.setFilled(true); + draw(rect); +} + +void GWindow::drawOval(const GRectangle & bounds) { + drawOval(bounds.getX(), bounds.getY(), bounds.getWidth(), + bounds.getHeight()); +} + +void GWindow::drawOval(double x, double y, double width, double height) { + GOval oval(x, y, width, height); + oval.setColor(gwd->color); + draw(oval); +} + +void GWindow::fillOval(const GRectangle & bounds) { + fillOval(bounds.getX(), bounds.getY(), bounds.getWidth(), + bounds.getHeight()); +} + +void GWindow::fillOval(double x, double y, double width, double height) { + GOval oval(x, y, width, height); + oval.setColor(gwd->color); + oval.setFilled(true); + draw(oval); +} + +void GWindow::setColor(string color) { + setColor(convertColorToRGB(color)); +} + +void GWindow::setColor(int rgb) { + gwd->color = convertRGBToColor(rgb); +} + +string GWindow::getColor() { + return gwd->color; +} + +double GWindow::getWidth() { + return gwd->windowWidth; +} + +double GWindow::getHeight() { + return gwd->windowHeight; +} + +void GWindow::setWindowTitle(string title) { + gwd->windowTitle = title; + pp->setWindowTitle(*this, title); +} + +string GWindow::getWindowTitle() { + return gwd->windowTitle; +} + +void GWindow::draw(const GObject & gobj) { + draw(&gobj); +} + +void GWindow::draw(GObject *gobj) { + pp->draw(*this, gobj); +} + +void GWindow::draw(const GObject *gobj) { + pp->draw(*this, gobj); +} + +void GWindow::draw(GObject & gobj, double x, double y) { + draw(&gobj, x, y); +} + +void GWindow::draw(GObject *gobj, double x, double y) { + gobj->setLocation(x, y); + pp->draw(*this, gobj); +} + +void GWindow::add(GObject *gobj) { + gwd->top->add(gobj); +} + +void GWindow::add(GObject *gobj, double x, double y) { + gobj->setLocation(x, y); + add(gobj); +} + +void GWindow::addToRegion(GInteractor *gobj, string region) { + pp->addToRegion(*this, (GObject *) gobj, region); +} + +void GWindow::addToRegion(GLabel *gobj, string region) { + pp->addToRegion(*this, (GObject *) gobj, region); +} + +void GWindow::removeFromRegion(GInteractor *gobj, string region) { + pp->removeFromRegion(*this, (GObject *) gobj, region); +} + +void GWindow::removeFromRegion(GLabel *gobj, string region) { + pp->removeFromRegion(*this, (GObject *) gobj, region); +} + +void GWindow::remove(GObject *gobj) { + gwd->top->remove(gobj); +} + +GObject *GWindow::getGObjectAt(double x, double y) { + int n = gwd->top->getElementCount(); + for (int i = n - 1; i >= 0; i--) { + GObject *gobj = gwd->top->getElement(i); + if (gobj->contains(x, y)) return gobj; + } + return NULL; +} + +void GWindow::setRegionAlignment(string region, string align) { + pp->setRegionAlignment(*this, region, align); +} + +bool GWindow::operator==(GWindow w2) { + return gwd == w2.gwd; +} + +bool GWindow::operator!=(GWindow w2) { + return gwd != w2.gwd; +} + +GWindow::GWindow(GWindowData *gwd) { + this->gwd = gwd; +} + +void pause(double milliseconds) { + pp->pause(milliseconds); +} + +double getScreenWidth() { + return pp->getScreenWidth(); +} + +double getScreenHeight() { + return pp->getScreenWidth(); +} + +int convertColorToRGB(string colorName) { + if (colorName == "") return -1; + if (colorName[0] == '#') { + istringstream is(colorName.substr(1) + "@"); + int rgb; + char terminator = '\0'; + is >> hex >> rgb >> terminator; + if (terminator != '@') error("setColor: Illegal color - " + colorName); + return rgb; + } + string name = canonicalColorName(colorName); + if (colorTable.size() == 0) initColorTable(); + if (!colorTable.containsKey(name)) { + error("setColor: Undefined color - " + colorName); + } + return colorTable[name]; +} + +string convertRGBToColor(int rgb) { + if (rgb == -1) return ""; + ostringstream os; + os << hex << setfill('0') << uppercase << "#"; + os << setw(2) << (rgb >> 16 & 0xFF); + os << setw(2) << (rgb >> 8 & 0xFF); + os << setw(2) << (rgb & 0xFF); + return os.str(); +} + +void exitGraphics() { + pp->exitGraphics(); + exit(0); +} + +static void initColorTable() { + colorTable["black"] = 0x000000; + colorTable["darkgray"] = 0x595959; + colorTable["gray"] = 0x999999; + colorTable["lightgray"] = 0xBFBFBF; + colorTable["white"] = 0xFFFFFF; + colorTable["red"] = 0xFF0000; + colorTable["yellow"] = 0xFFFF00; + colorTable["green"] = 0x00FF00; + colorTable["cyan"] = 0x00FFFF; + colorTable["blue"] = 0x0000FF; + colorTable["magenta"] = 0xFF00FF; + colorTable["orange"] = 0xFFC800; + colorTable["pink"] = 0xFFAFAF; +} + +static string canonicalColorName(string str) { + string result = ""; + int nChars = str.length(); + for (int i = 0; i < nChars; i++) { + char ch = str[i]; + if (!isspace(ch) && ch != '_') result += tolower(ch); + } + return result; +} diff --git a/labb8/lib/StanfordCPPLib/gwindow.h b/labb8/lib/StanfordCPPLib/gwindow.h new file mode 100755 index 0000000..e6dbfbd --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gwindow.h @@ -0,0 +1,536 @@ +/* + * File: gwindow.h + * --------------- + * This file defines the <code>GWindow</code> class which supports + * drawing graphical objects on the screen. + */ + +#ifndef _gwindow_h +#define _gwindow_h + +#include <string> +#include "gtypes.h" +#include "vector.h" + +class GCompound; +class GInteractor; +class GLabel; +class GObject; + +/* + * Friend type: GWindowData + * ------------------------ + * This block contains all data pertaining to the window. Shallow copying + * of the <code>GWindow</code> object ensures that all copies refer to the + * same onscreen window. + */ + +struct GWindowData { + double windowWidth; + double windowHeight; + std::string windowTitle; + std::string color; + bool visible; + GCompound *top; +}; + +/* + * Class: GWindow + * -------------- + * This class represents a graphics window that supports simple graphics. + * Each <code>GWindow</code> consists of two layers. The background layer + * provides a surface for drawing static pictures that involve no animation. + * Graphical objects drawn in the background layer are persistent and do + * not require the client to update the contents of the window. The + * foreground layer contains graphical objects that are redrawn as necessary. + * + * <p>The <code>GWindow</code> class includes several methods that draw + * lines, rectangles, and ovals on the background layer without making + * use of the facilities of the <code>gobjects.h</code> interface. For + * example, the following program draws a diamond, rectangle, and oval + * at the center of the window. + * + *<pre> + * int main() { + * GWindow gw; + * cout << "This program draws a diamond, rectangle, and oval." << endl; + * double width = gw.getWidth(); + * double height = gw.getHeight(); + * gw.drawLine(0, height / 2, width / 2, 0); + * gw.drawLine(width / 2, 0, width, height / 2); + * gw.drawLine(width, height / 2, width / 2, height); + * gw.drawLine(width / 2, height, 0, height / 2); + * gw.setColor("BLUE"); + * gw.fillRect(width / 4, height / 4, width / 2, height / 2); + * gw.setColor("GRAY"); + * gw.fillOval(width / 4, height / 4, width / 2, height / 2); + * return 0; + * } + *</pre> + * + * A <code>GWindow</code> object may be freely copied, after which all + * copies refer to the same window. + */ + +class GWindow { + +public: + +/* + * Constructor: GWindow + * Usage: GWindow gw; + * GWindow gw(width, height); + * --------------------------------- + * Creates a window, either of the specified size or a default size. + */ + + GWindow(); + GWindow(double width, double height); + +/* + * Destructor: ~GWindow + * -------------------- + * Reclaims the internal storage for the window. Note that the window + * is not closed by this operation, but persists until it is closed by + * the client or the user exits the program. + */ + + virtual ~GWindow(); + +/* + * Method: close + * Usage: gw.close(); + * ------------------ + * Deletes the window from the screen. + */ + + void close(); + +/* + * Method: requestFocus + * Usage: gw.requestFocus(); + * ------------------------- + * Asks the system to assign the keyboard focus to the window, which + * brings it to the top and ensures that key events are delivered to + * the window. Clicking in the window automatically requests the focus. + */ + + void requestFocus(); + +/* + * Method: clear + * Usage: gw.clear(); + * ------------------ + * Clears the contents of the window. + */ + + void clear(); + +/* + * Method: setVisible + * Usage: gw.setVisible(flag); + * --------------------------- + * Determines whether the window is visible on the screen. + */ + + void setVisible(bool flag); + +/* + * Method: isVisible + * Usage: if (gw.isVisible()) ... + * ------------------------------ + * Tests whether the window is visible. + */ + + bool isVisible(); + +/* + * Method: drawLine + * Usage: gw.drawLine(p0, p1); + * gw.drawLine(x0, y0, x1, y1); + * ----------------------------------- + * Draws a line connecting the specified points. + */ + + void drawLine(const GPoint & p0, const GPoint & p1); + void drawLine(double x0, double y0, double x1, double y1); + +/* + * Method: drawPolarLine + * Usage: GPoint p1 = gw.drawPolarLine(p0, r, theta); + * GPoint p1 = gw.drawPolarLine(x0, y0, r, theta); + * ------------------------------------------------------ + * Draws a line of length <code>r</code> in the direction <code>theta</code> + * from the initial point. The angle <code>theta</code> is measured in + * degrees counterclockwise from the +<i>x</i> axis. The method returns + * the end point of the line. + */ + + GPoint drawPolarLine(const GPoint & p0, double r, double theta); + GPoint drawPolarLine(double x0, double y0, double r, double theta); + +/* + * Method: drawOval + * Usage: gw.drawOval(bounds); + * gw.drawOval(x, y, width, height); + * ---------------------------------------- + * Draws the frame of a oval with the specified bounds. + */ + + void drawOval(const GRectangle & bounds); + void drawOval(double x, double y, double width, double height); + +/* + * Method: fillOval + * Usage: gw.fillOval(bounds); + * gw.fillOval(x, y, width, height); + * ---------------------------------------- + * Fills the frame of a oval with the specified bounds. + */ + + void fillOval(const GRectangle & bounds); + void fillOval(double x, double y, double width, double height); + +/* + * Method: drawRect + * Usage: gw.drawRect(bounds); + * gw.drawRect(x, y, width, height); + * ---------------------------------------- + * Draws the frame of a rectangle with the specified bounds. + */ + + void drawRect(const GRectangle & bounds); + void drawRect(double x, double y, double width, double height); + +/* + * Method: fillRect + * Usage: gw.fillRect(bounds); + * gw.fillRect(x, y, width, height); + * ---------------------------------------- + * Fills the frame of a rectangle with the specified bounds. + */ + + void fillRect(const GRectangle & bounds); + void fillRect(double x, double y, double width, double height); + +/* + * Method: setColor + * Usage: gw.setColor(color); + * -------------------------- + * Sets the color used for drawing. The <code>color</code> parameter is + * usually one of the predefined color names: + * + * <code>BLACK</code>, + * <code>BLUE</code>, + * <code>CYAN</code>, + * <code>DARK_GRAY</code>, + * <code>GRAY</code>, + * <code>GREEN</code>, + * <code>LIGHT_GRAY</code>, + * <code>MAGENTA</code>, + * <code>ORANGE</code>, + * <code>PINK</code>, + * <code>RED</code>, + * <code>WHITE</code>, and + * <code>YELLOW</code>. + * + * The case of the individual letters in the color name is ignored, as + * are spaces and underscores, so that the color <code>DARK_GRAY</code> + * can be written as <code>"Dark Gray"</code>. + * + * <p>The color can also be specified as a string in the form + * <code>"#rrggbb"</code> where <code>rr</code>, <code>gg</code>, and + * <code>bb</code> are pairs of hexadecimal digits indicating the + * red, green, and blue components of the color. + */ + + void setColor(std::string color); + void setColor(int color); + +/* + * Method: getColor + * Usage: string color = gw.getColor(); + * ------------------------------------ + * Returns the current color as a string in the form <code>"#rrggbb"</code>. + * In this string, the values <code>rr</code>, <code>gg</code>, + * and <code>bb</code> are two-digit hexadecimal values representing + * the red, green, and blue components of the color, respectively. + */ + + std::string getColor(); + +/* + * Method: getWidth + * Usage: double width = gw.getWidth(); + * ------------------------------------ + * Returns the width of the graphics window in pixels. + */ + + double getWidth(); + +/* + * Method: getHeight + * Usage: double height = gw.getHeight(); + * -------------------------------------- + * Returns the height of the graphics window in pixels. + */ + + double getHeight(); + +/* + * Method: repaint + * Usage: gw.repaint(); + * -------------------- + * Schedule a repaint on this window. + */ + + void repaint(); + +/* + * Method: setWindowTitle + * Usage: gw.setWindowTitle(title); + * -------------------------------- + * Sets the title of the graphics window. + */ + + void setWindowTitle(std::string title); + +/* + * Method: getWindowTitle + * Usage: string title = gw.getWindowTitle(); + * ------------------------------------------ + * Returns the title of the graphics window. + */ + + std::string getWindowTitle(); + +/* + * Method: draw + * Usage: gw.draw(gobj); + * gw.draw(gobj, x, y); + * --------------------------- + * Draws the <code>GObject</code> on the background layer. For convenience, + * the <code>gobj</code> parameter may be passed either as a constant + * reference or as a pointer. If the <code>x</code> and <code>y</code> + * parameters are included, the object is moved to that location before + * drawing. + */ + + void draw(const GObject & gobj); + void draw(GObject *gobj); + void draw(const GObject *gobj); + void draw(GObject & gobj, double x, double y); + void draw(GObject *gobj, double x, double y); + +/* + * Method: add + * Usage: gw.add(gobj); + * gw.add(gobj, x, y); + * -------------------------- + * Adds the <code>GObject</code> to the foreground layer of the window. + * The second form of the call sets the location of the object to + * (<code>x</code>, <code>y</code>) first. + * + * <p>In terms of memory management, adding a <code>GObject</code> pointer to + * a <code>GWindow</code> transfers control of that object from the client to + * the window manager. Deleting a <code>GWindow</code> automatically deletes + * any <nobr><code>GObject</code><font size=-1>s</font></nobr> it contains. + */ + + void add(GObject *gobj); + void add(GObject *gobj, double x, double y); + +/* + * Method: remove + * Usage: gw.remove(gobj); + * ----------------------- + * Removes the object from the window. + */ + + void remove(GObject *gobj); + +/* + * Method: addToRegion + * Usage: gw.addToRegion(interactor, region); + * ------------------------------------------ + * Adds the interactor (which can also be a <code>GLabel</code>) to + * the control strip specified by the <code>region</code> parameter. + * The <code>region</code> parameter must be one of the strings + * <code>"NORTH"</code>, <code>"EAST"</code>, <code>"SOUTH"</code>, + * or <code>"WEST"</code>. + */ + + void addToRegion(GInteractor *gobj, std::string region); + void addToRegion(GLabel *gobj, std::string region); + +/* + * Method: removeFromRegion + * Usage: gw.removeFromRegion(interactor, region); + * ----------------------------------------------- + * Adds the interactor (which can also be a <code>GLabel</code>) to + * the control strip specified by the <code>region</code> parameter. + * The <code>region</code> parameter must be one of the strings + * <code>"NORTH"</code>, <code>"EAST"</code>, <code>"SOUTH"</code>, + * or <code>"WEST"</code>. + */ + + void removeFromRegion(GInteractor *gobj, std::string region); + void removeFromRegion(GLabel *gobj, std::string region); + +/* + * Method: getGObjectAt + * Usage: GObject *gobj = getGObjectAt(x, y); + * ------------------------------------------ + * Returns a pointer to the topmost <code>GObject</code> containing the + * point (<code>x</code>, <code>y</code>), or <code>NULL</code> if no such + * object exists. + */ + + GObject *getGObjectAt(double x, double y); + +/* + * Method: setRegionAlignment + * Usage: gw.setRegionAlignment(region, align); + * -------------------------------------------- + * Sets the alignment of the specified side region as specified by the + * string <code>align</code>. The <code>region</code> parameter must be + * one of the strings <code>"NORTH"</code>, <code>"EAST"</code>, + * <code>"SOUTH"</code>, or <code>"WEST"</code> and the <code>align</code> + * parameter must be <code>"LEFT"</code>, <code>"RIGHT"</code>, or + * <code>"CENTER"</code>. By default, side panels use + * <code>CENTER</code> alignment. + */ + + void setRegionAlignment(std::string region, std::string align); + +/* + * Operator: == + * Usage: if (w1 == w2) ... + * ------------------------ + * Checks whether the two objects refer to the same window. + */ + + bool operator==(GWindow w2); + +/* + * Operator: != + * Usage: if (w1 != w2) ... + * ------------------------ + * Checks whether the two objects refer to different windows. + */ + + bool operator!=(GWindow w2); + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + + explicit GWindow(bool visible); + GWindow(double width, double height, bool visible); + GWindow(GWindowData *gwd); + +private: + +/* Instance variables */ + + GWindowData *gwd; + +/* Private methods */ + + void initGWindow(double width, double height, bool visible); + + friend class Platform; + friend class GKeyEvent; + friend class GMouseEvent; + friend class GWindowEvent; + +}; + +/* + * Function: repaint + * Usage: repaint(); + * ----------------- + * Issues a request to update all graphics windows. This function + * is called automatically when the program pauses, waits for an + * event, waits for user input on the console, or terminates. As + * a result, most clients never need to call repaint explicitly. + */ + +void repaint(); + +/* + * Function: pause + * Usage: pause(milliseconds); + * --------------------------- + * Pauses for the indicated number of milliseconds. This function is + * useful for animation where the motion would otherwise be too fast. + */ + +void pause(double milliseconds); + +/* + * Function: getScreenWidth + * Usage: width = getScreenWidth(); + * -------------------------------- + * Returns the width of the entire display screen. + */ + +double getScreenWidth(); + +/* + * Function: getScreenHeight + * Usage: height = getScreenHeight(); + * ---------------------------------- + * Returns the height of the entire display screen. + */ + +double getScreenHeight(); + +/* + * Function: convertColorToRGB + * Usage: int rgb = convertColorToRGB(colorName); + * ---------------------------------------------- + * Converts a color name into an integer that encodes the + * red, green, and blue components of the color. + */ + +int convertColorToRGB(std::string colorName); + +/* + * Function: convertRGBToColor + * Usage: int colorName = convertRGBToColor(rgb); + * ---------------------------------------------- + * Converts an <code>rgb</code> value into a color name in the + * form <code>"#rrggbb"</code>. Each of the <code>rr</code>, + * <code>gg</code>, and <code>bb</code> values are two-digit + * hexadecimal numbers indicating the intensity of that component. + */ + +std::string convertRGBToColor(int rgb); + +/* + * Function: waitForClick + * Usage: waitForClick(); + * ---------------------- + * Waits for a mouse click to occur anywhere in any window. + */ + +void waitForClick(); + +/* + * Function: exitGraphics + * Usage: exitGraphics(); + * ---------------------- + * Closes all graphics windows and exits from the application without + * waiting for any additional user interaction. + */ + +void exitGraphics(); + +#include "console.h" +#include "private/main.h" + +#endif diff --git a/labb8/lib/StanfordCPPLib/hashmap.cpp b/labb8/lib/StanfordCPPLib/hashmap.cpp new file mode 100755 index 0000000..4dd77d6 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/hashmap.cpp @@ -0,0 +1,296 @@ +/* + * File: hashmap.cpp + * ----------------- + * This file contains the hash functions that are used in conjunction + * with the HashMap class. + */ + +#include <iostream> +#include <string> +#include "hashmap.h" +#include "hashset.h" +#include "lexicon.h" +#include "queue.h" +#include "set.h" +#include "stack.h" +#include "vector.h" +using namespace std; + +/* + * Implementation notes: hashCode + * ------------------------------ + * This function takes a string key and uses it to derive a hash code, + * which is a nonnegative integer related to the key by a deterministic + * function that distributes keys well across the space of integers. + * The general method is called linear congruence, which is also used + * in random-number generators. The specific algorithm used here is + * called djb2 after the initials of its inventor, Daniel J. Bernstein, + * Professor of Mathematics at the University of Illinois at Chicago. + */ + +const int HASH_SEED = 5381; /* Starting point for first cycle */ +const int HASH_MULTIPLIER = 33; /* Multiplier for each cycle */ +const int HASH_MASK = unsigned(-1) >> 1; /* All 1 bits except the sign */ + +int hashCode(const string & str) { + unsigned hash = HASH_SEED; + int n = str.length(); + for (int i = 0; i < n; i++) { + hash = HASH_MULTIPLIER * hash + str[i]; + } + return int(hash & HASH_MASK); +} + +int hashCode(int key) { + return key & HASH_MASK; +} + +int hashCode(char key) { + return key; +} + +int hashCode(long key) { + return int(key) & HASH_MASK; +} + +int hashCode(double key) { + char* byte = (char*) &key; + unsigned hash = HASH_SEED; + for (int i = 0; i < (int) sizeof(double); i++) { + hash = HASH_MULTIPLIER * hash + (int) *byte++; + } + return hash & HASH_MASK; +} + + +// hashCode functions for various collections; +// added by Marty Stepp to allow compound collections. +// I'm a bit ashamed to have to rewrite so many prototypes, one for each +// element type; but I can't get it to compile with a template. +int hashCode(const HashSet<int>& s) { + int code = HASH_SEED; + foreach (int n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const HashSet<double>& s) { + int code = HASH_SEED; + foreach (double n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const HashSet<char>& s) { + int code = HASH_SEED; + foreach (char n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const HashSet<long>& s) { + int code = HASH_SEED; + foreach (long n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const HashSet<std::string>& s) { + int code = HASH_SEED; + foreach (std::string n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Lexicon& l) { + int code = HASH_SEED; + foreach (std::string n in l) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Queue<int>& q) { + int code = HASH_SEED; + Queue<int> backup = q; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.dequeue()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Queue<double>& q) { + int code = HASH_SEED; + Queue<double> backup = q; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.dequeue()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Queue<char>& q) { + int code = HASH_SEED; + Queue<char> backup = q; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.dequeue()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Queue<long>& q) { + int code = HASH_SEED; + Queue<long> backup = q; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.dequeue()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Queue<std::string>& q) { + int code = HASH_SEED; + Queue<std::string> backup = q; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.dequeue()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Set<int>& s) { + int code = HASH_SEED; + foreach (int n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Set<double>& s) { + int code = HASH_SEED; + foreach (double n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Set<char>& s) { + int code = HASH_SEED; + foreach (char n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Set<long>& s) { + int code = HASH_SEED; + foreach (long n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Set<std::string>& s) { + int code = HASH_SEED; + foreach (std::string n in s) { + code = HASH_MULTIPLIER * code + hashCode(n); + } + return int(code & HASH_MASK); +} + +int hashCode(const Stack<int>& s) { + int code = HASH_SEED; + Stack<int> backup = s; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.pop()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Stack<double>& s) { + int code = HASH_SEED; + Stack<double> backup = s; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.pop()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Stack<char>& s) { + int code = HASH_SEED; + Stack<char> backup = s; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.pop()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Stack<long>& s) { + int code = HASH_SEED; + Stack<long> backup = s; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.pop()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Stack<std::string>& s) { + int code = HASH_SEED; + Stack<std::string> backup = s; + while (!backup.isEmpty()) { + code = HASH_MULTIPLIER * code + hashCode(backup.pop()); + } + return int(code & HASH_MASK); +} + +int hashCode(const Vector<int>& v) { + int code = HASH_SEED; + for (int i = 0, size = v.size(); i < size; i++) { + code = HASH_MULTIPLIER * code + hashCode(v[i]); + } + return int(code & HASH_MASK); +} + +int hashCode(const Vector<double>& v) { + int code = HASH_SEED; + for (int i = 0, size = v.size(); i < size; i++) { + code = HASH_MULTIPLIER * code + hashCode(v[i]); + } + return int(code & HASH_MASK); +} + +int hashCode(const Vector<char>& v) { + int code = HASH_SEED; + for (int i = 0, size = v.size(); i < size; i++) { + code = HASH_MULTIPLIER * code + hashCode(v[i]); + } + return int(code & HASH_MASK); +} + +int hashCode(const Vector<long>& v) { + int code = HASH_SEED; + for (int i = 0, size = v.size(); i < size; i++) { + code = HASH_MULTIPLIER * code + hashCode(v[i]); + } + return int(code & HASH_MASK); +} + +int hashCode(const Vector<std::string>& v) { + int code = HASH_SEED; + for (int i = 0, size = v.size(); i < size; i++) { + code = HASH_MULTIPLIER * code + hashCode(v[i]); + } + return int(code & HASH_MASK); +} + +//template <typename ValueType> +//int hashCode(const Vector<ValueType>& v) { +// int code = 0; +// for (int i = 0, size = v.size(); i < size; i++) { +// code = 31 * code + hashCode(v[i]); +// } +// return code; +//} + diff --git a/labb8/lib/StanfordCPPLib/hashmap.h b/labb8/lib/StanfordCPPLib/hashmap.h new file mode 100755 index 0000000..5b76ae0 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/hashmap.h @@ -0,0 +1,663 @@ +/* + * File: hashmap.h + * --------------- + * This file exports the <code>HashMap</code> class, which stores + * a set of <i>key</i>-<i>value</i> pairs. + */ + +#ifndef _hashmap_h +#define _hashmap_h + +#include <cstdlib> +#include <string> +#include "foreach.h" +#include "vector.h" + +/* + * Function: hashCode + * Usage: int hash = hashCode(key); + * -------------------------------- + * Returns a hash code for the specified key, which is always a + * nonnegative integer. This function is overloaded to support + * all of the primitive types and the C++ <code>string</code> type. + */ + +int hashCode(const std::string & key); +int hashCode(int key); +int hashCode(char key); +int hashCode(long key); +int hashCode(double key); + +/* + * Class: HashMap<KeyType,ValueType> + * --------------------------------- + * This class implements an efficient association between + * <b><i>keys</i></b> and <b><i>values</i></b>. This class is + * identical to the <a href="Map-class.html"><code>Map</code></a> class + * except for the fact that it uses a hash table as its underlying + * representation. Although the <code>HashMap</code> class operates in + * constant time, the iterator for <code>HashMap</code> returns the + * values in a seemingly random order. + */ + +template <typename KeyType, typename ValueType> +class HashMap { + +public: + +/* + * Constructor: HashMap + * Usage: HashMap<KeyType,ValueType> map; + * -------------------------------------- + * Initializes a new empty map that associates keys and values of + * the specified types. The type used for the key must define + * the <code>==</code> operator, and there must be a free function + * with the following signature: + * + *<pre> + * int hashCode(KeyType key); + *</pre> + * + * that returns a positive integer determined by the key. This interface + * exports <code>hashCode</code> functions for <code>string</code> and + * the C++ primitive types. + */ + + HashMap(); + +/* + * Destructor: ~HashMap + * -------------------- + * Frees any heap storage associated with this map. + */ + + virtual ~HashMap(); + +/* + * Method: size + * Usage: int nEntries = map.size(); + * --------------------------------- + * Returns the number of entries in this map. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (map.isEmpty()) ... + * ----------------------------- + * Returns <code>true</code> if this map contains no entries. + */ + + bool isEmpty() const; + +/* + * Method: put + * Usage: map.put(key, value); + * --------------------------- + * Associates <code>key</code> with <code>value</code> in this map. + * Any previous value associated with <code>key</code> is replaced + * by the new value. + */ + + void put(KeyType key, ValueType value); + +/* + * Method: get + * Usage: ValueType value = map.get(key); + * -------------------------------------- + * Returns the value associated with <code>key</code> in this map. + * If <code>key</code> is not found, <code>get</code> returns the + * default value for <code>ValueType</code>. + */ + + ValueType get(KeyType key) const; + +/* + * Method: containsKey + * Usage: if (map.containsKey(key)) ... + * ------------------------------------ + * Returns <code>true</code> if there is an entry for <code>key</code> + * in this map. + */ + + bool containsKey(KeyType key) const; + +/* + * Method: remove + * Usage: map.remove(key); + * ----------------------- + * Removes any entry for <code>key</code> from this map. + */ + + void remove(KeyType key); + +/* + * Method: clear + * Usage: map.clear(); + * ------------------- + * Removes all entries from this map. + */ + + void clear(); + +/* + * Method: keys + * Usage: Vector<KeyType> keys = map.keys(); + * ------------------------------------------- + * Returns a collection containing all keys in this map. + */ + + Vector<KeyType> keys() const; + +/* + * Method: values + * Usage: Vector<ValueType> values = map.values(); + * ------------------------------------------- + * Returns a collection containing all values in this map. + */ + + Vector<ValueType> values() const; + +/* + * Operator: [] + * Usage: map[key] + * --------------- + * Selects the value associated with <code>key</code>. This syntax + * makes it easy to think of a map as an "associative array" + * indexed by the key type. If <code>key</code> is already present + * in the map, this function returns a reference to its associated + * value. If key is not present in the map, a new entry is created + * whose value is set to the default for the value type. + */ + + ValueType & operator[](KeyType key); + ValueType operator[](KeyType key) const; + +/* + * Method: toString + * Usage: string str = map.toString(); + * ----------------------------------- + * Converts the map to a printable string representation. + */ + + std::string toString(); + +/* + * Method: mapAll + * Usage: map.mapAll(fn); + * ---------------------- + * Iterates through the map entries and calls <code>fn(key, value)</code> + * for each one. The keys are processed in an undetermined order. + */ + + void mapAll(void (*fn)(KeyType, ValueType)) const; + void mapAll(void (*fn)(const KeyType &, const ValueType &)) const; + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional HashMap operations + * ----------------------------- + * In addition to the methods listed in this interface, the HashMap + * class supports the following operations: + * + * - Stream I/O using the << and >> operators + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement and STL iterators + * + * The HashMap class makes no guarantees about the order of iteration. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Implementation notes: + * --------------------- + * The HashMap class is represented using a hash table that uses + * bucket chaining to resolve collisions. + */ + +private: + +/* Constant definitions */ + + static const int INITIAL_BUCKET_COUNT = 101; + static const int MAX_LOAD_PERCENTAGE = 70; + +/* Type definition for cells in the bucket chain */ + + struct Cell { + KeyType key; + ValueType value; + Cell *next; + }; + +/* Instance variables */ + + Vector<Cell *> buckets; + int nBuckets; + int numEntries; + +/* Private methods */ + +/* + * Private method: createBuckets + * Usage: createBuckets(nBuckets); + * ------------------------------- + * Sets up the vector of buckets to have nBuckets entries, each NULL. + * If asked to make empty vector, makes one bucket just to simplify + * handling elsewhere. + */ + + void createBuckets(int nBuckets) { + if (nBuckets == 0) nBuckets = 1; + buckets = Vector<Cell *>(nBuckets, NULL); + this->nBuckets = nBuckets; + numEntries = 0; + } + +/* + * Private method: deleteBuckets + * Usage: deleteBuckets(buckets); + * ------------------------------ + * Deletes all the cells in the linked lists contained in vector. + */ + + void deleteBuckets(Vector <Cell *> & buckets) { + for (int i = 0; i < buckets.size(); i++) { + Cell *cp = buckets[i]; + while (cp != NULL) { + Cell *np = cp->next; + delete cp; + cp = np; + } + buckets[i] = NULL; + } + } + +/* + * Private method: expandAndRehash + * Usage: expandAndRehash(); + * ------------------------- + * This method is used to increase the number of buckets in the map + * and then rehashes all existing entries and adds them into new buckets. + * This operation is used when the load factor (i.e. the number of cells + * per bucket) has increased enough to warrant this O(N) operation to + * enlarge and redistribute the entries. + */ + + void expandAndRehash() { + Vector<Cell *>oldBuckets = buckets; + createBuckets(oldBuckets.size() * 2 + 1); + for (int i = 0; i < oldBuckets.size(); i++) { + for (Cell *cp = oldBuckets[i]; cp != NULL; cp = cp->next) { + put(cp->key, cp->value); + } + } + deleteBuckets(oldBuckets); + } + +/* + * Private method: findCell + * Usage: Cell *cp = findCell(bucket, key); + * Cell *cp = findCell(bucket, key, parent); + * ------------------------------------------------ + * Finds a cell in the chain for the specified bucket that matches key. + * If a match is found, the return value is a pointer to the cell containing + * the matching key. If no match is found, the function returns NULL. + * If the optional third argument is supplied, it is filled in with the + * cell preceding the matching cell to allow the client to splice out + * the target cell in the delete call. If parent is NULL, it indicates + * that the cell is the first cell in the bucket chain. + */ + + Cell *findCell(int bucket, KeyType key) const { + Cell *dummy; + return findCell(bucket, key, dummy); + } + + Cell *findCell(int bucket, KeyType key, Cell * & parent) const { + parent = NULL; + Cell *cp = buckets.get(bucket); + while (cp != NULL && key != cp->key) { + parent = cp; + cp = cp->next; + } + return cp; + } + + void deepCopy(const HashMap & src) { + createBuckets(src.nBuckets); + for (int i = 0; i < src.nBuckets; i++) { + for (Cell *cp = src.buckets.get(i); cp != NULL; cp = cp->next) { + put(cp->key, cp->value); + } + } + } + +public: + +/* + * Hidden features + * --------------- + * The remainder of this file consists of the code required to + * support deep copying and iteration. Including these methods + * in the public interface would make that interface more + * difficult to understand for the average client. + */ + +/* + * Deep copying support + * -------------------- + * This copy constructor and operator= are defined to make a + * deep copy, making it possible to pass/return maps by value + * and assign from one map to another. + */ + + HashMap & operator=(const HashMap & src) { + if (this != &src) { + clear(); + deepCopy(src); + } + return *this; + } + + HashMap(const HashMap & src) { + deepCopy(src); + } + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : public std::iterator<std::input_iterator_tag,KeyType> { + + private: + + const HashMap *mp; /* Pointer to the map */ + int bucket; /* Index of current bucket */ + Cell *cp; /* Current cell in bucket chain */ + + public: + + iterator() { + /* Empty */ + } + + iterator(const HashMap *mp, bool end) { + this->mp = mp; + if (end) { + bucket = mp->nBuckets; + cp = NULL; + } else { + bucket = 0; + cp = mp->buckets.get(bucket); + while (cp == NULL && ++bucket < mp->nBuckets) { + cp = mp->buckets.get(bucket); + } + } + } + + iterator(const iterator & it) { + mp = it.mp; + bucket = it.bucket; + cp = it.cp; + } + + iterator & operator++() { + cp = cp->next; + while (cp == NULL && ++bucket < mp->nBuckets) { + cp = mp->buckets.get(bucket); + } + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + bool operator==(const iterator & rhs) { + return mp == rhs.mp && bucket == rhs.bucket && cp == rhs.cp; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + KeyType operator*() { + return cp->key; + } + + KeyType *operator->() { + return &cp->key; + } + + friend class HashMap; + + }; + + iterator begin() const { + return iterator(this, false); + } + + iterator end() const { + return iterator(this, true); + } + +}; + +/* + * Implementation notes: HashMap class + * ----------------------------------- + * In this map implementation, the entries are stored in a hashtable. + * The hashtable keeps a vector of "buckets", where each bucket is a + * linked list of elements that share the same hash code (i.e. hash + * collisions are resolved by chaining). The buckets are dynamically + * allocated so that we can change the the number of buckets (rehash) + * when the load factor becomes too high. The map should provide O(1) + * performance on the put/remove/get operations. + */ + +template <typename KeyType,typename ValueType> +HashMap<KeyType,ValueType>::HashMap() { + createBuckets(INITIAL_BUCKET_COUNT); +} + +template <typename KeyType,typename ValueType> +HashMap<KeyType,ValueType>::~HashMap() { + deleteBuckets(buckets); +} + +template <typename KeyType,typename ValueType> +int HashMap<KeyType,ValueType>::size() const { + return numEntries; +} + +template <typename KeyType,typename ValueType> +bool HashMap<KeyType,ValueType>::isEmpty() const { + return size() == 0; +} + +template <typename KeyType,typename ValueType> +void HashMap<KeyType,ValueType>::put(KeyType key, ValueType value) { + (*this)[key] = value; +} + +template <typename KeyType,typename ValueType> +ValueType HashMap<KeyType,ValueType>::get(KeyType key) const { + Cell *cp = findCell(hashCode(key) % nBuckets, key); + if (cp == NULL) return ValueType(); + return cp->value; +} + +template <typename KeyType,typename ValueType> +bool HashMap<KeyType,ValueType>::containsKey(KeyType key) const { + return findCell(hashCode(key) % nBuckets, key) != NULL; +} + +template <typename KeyType,typename ValueType> +void HashMap<KeyType,ValueType>::remove(KeyType key) { + int bucket = hashCode(key) % nBuckets; + Cell *parent; + Cell *cp = findCell(bucket, key, parent); + if (cp != NULL) { + if (parent == NULL) { + buckets[bucket] = cp->next; + } else { + parent->next = cp->next; + } + delete cp; + numEntries--; + } +} + +template <typename KeyType,typename ValueType> +void HashMap<KeyType,ValueType>::clear() { + deleteBuckets(buckets); + numEntries = 0; +} + +template <typename KeyType,typename ValueType> +Vector<KeyType> HashMap<KeyType,ValueType>::keys() const { + Vector<KeyType> keyset; + foreach (KeyType key in *this) { + keyset.add(key); + } + return keyset; +} + +template <typename KeyType,typename ValueType> +Vector<ValueType> HashMap<KeyType,ValueType>::values() const { + Vector<ValueType> values; + foreach (KeyType key in *this) { + values.add(this->get(key)); + } + return values; +} + +template <typename KeyType,typename ValueType> +ValueType & HashMap<KeyType,ValueType>::operator[](KeyType key) { + int bucket = hashCode(key) % nBuckets; + Cell *cp = findCell(bucket, key); + if (cp == NULL) { + if (numEntries > MAX_LOAD_PERCENTAGE * nBuckets / 100.0) { + expandAndRehash(); + bucket = hashCode(key) % nBuckets; + } + cp = new Cell; + cp->key = key; + cp->value = ValueType(); + cp->next = buckets[bucket]; + buckets[bucket] = cp; + numEntries++; + } + return cp->value; +} + +template <typename KeyType,typename ValueType> +void HashMap<KeyType,ValueType>::mapAll(void (*fn)(KeyType, ValueType)) const { + for (int i = 0 ; i < buckets.size(); i++) { + for (Cell *cp = buckets.get(i); cp != NULL; cp = cp->next) { + fn(cp->key, cp->value); + } + } +} + +template <typename KeyType,typename ValueType> +void HashMap<KeyType,ValueType>::mapAll(void (*fn)(const KeyType &, + const ValueType &)) const { + for (int i = 0 ; i < buckets.size(); i++) { + for (Cell *cp = buckets.get(i); cp != NULL; cp = cp->next) { + fn(cp->key, cp->value); + } + } +} + +template <typename KeyType,typename ValueType> +template <typename FunctorType> +void HashMap<KeyType,ValueType>::mapAll(FunctorType fn) const { + for (int i = 0 ; i < buckets.size(); i++) { + for (Cell *cp = buckets.get(i); cp != NULL; cp = cp->next) { + fn(cp->key, cp->value); + } + } +} + +template <typename KeyType, typename ValueType> +ValueType HashMap<KeyType,ValueType>::operator[](KeyType key) const { + return get(key); +} + +template <typename KeyType, typename ValueType> +std::string HashMap<KeyType,ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +/* + * Implementation notes: << and >> + * ------------------------------- + * The insertion and extraction operators use the template facilities in + * strlib.h to read and write generic values in a way that treats strings + * specially. + */ + +template <typename KeyType, typename ValueType> +std::ostream & operator<<(std::ostream & os, + const HashMap<KeyType,ValueType> & map) { + os << "{"; + typename HashMap<KeyType,ValueType>::iterator begin = map.begin(); + typename HashMap<KeyType,ValueType>::iterator end = map.end(); + typename HashMap<KeyType,ValueType>::iterator it = begin; + while (it != end) { + if (it != begin) os << ", "; + writeGenericValue(os, *it, false); + os << ":"; + writeGenericValue(os, map[*it], false); + ++it; + } + return os << "}"; +} + +template <typename KeyType, typename ValueType> +std::istream & operator>>(std::istream & is, + HashMap<KeyType,ValueType> & map) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + map.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + KeyType key; + readGenericValue(is, key); + is >> ch; + if (ch != ':') error("operator >>: Missing colon after key"); + ValueType value; + readGenericValue(is, value); + map[key] = value; + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +#endif diff --git a/labb8/lib/StanfordCPPLib/hashset.h b/labb8/lib/StanfordCPPLib/hashset.h new file mode 100755 index 0000000..f7551c5 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/hashset.h @@ -0,0 +1,613 @@ +/* + * File: hashset.h + * --------------- + * This file exports the <code>HashSet</code> class, which + * implements an efficient abstraction for storing sets of values. + */ + +#ifndef _hashset_h +#define _hashset_h + +#include <iostream> +#include "foreach.h" +#include "hashmap.h" +#include "vector.h" + +/* + * Class: HashSet<ValueType> + * ------------------------- + * This class implements an efficient abstraction for storing sets + * of distinct elements. This class is identical to the <code>Set</code> + * class except for the fact that it uses a hash table as its underlying + * representation. The advantage of the <code>HashSet</code> class is that + * it operates in constant time, as opposed to the <i>O</i>(log <i>N</i>) + * time for the <code>Set</code> class. The disadvantage of + * <code>HashSet</code> is that iterators return the values in a + * seemingly random order. + */ + +template <typename ValueType> +class HashSet { + +public: + +/* + * Constructor: HashSet + * Usage: HashSet<ValueType> set; + * ------------------------------ + * Initializes an empty set of the specified element type. + */ + + HashSet(); + +/* + * Destructor: ~HashSet + * -------------------- + * Frees any heap storage associated with this set. + */ + + virtual ~HashSet(); + +/* + * Method: size + * Usage: count = set.size(); + * -------------------------- + * Returns the number of elements in this set. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (set.isEmpty()) ... + * ----------------------------- + * Returns <code>true</code> if this set contains no elements. + */ + + bool isEmpty() const; + +/* + * Method: add + * Usage: set.add(value); + * ---------------------- + * Adds an element to this set, if it was not already there. For + * compatibility with the STL <code>set</code> class, this method + * is also exported as <code>insert</code>. + */ + + void add(const ValueType & value); + void insert(const ValueType & value); + +/* + * Method: remove + * Usage: set.remove(value); + * ------------------------- + * Removes an element from this set. If the value was not + * contained in the set, no error is generated and the set + * remains unchanged. + */ + + void remove(const ValueType & value); + +/* + * Method: contains + * Usage: if (set.contains(value)) ... + * ----------------------------------- + * Returns <code>true</code> if the specified value is in this set. + */ + + bool contains(const ValueType & value) const; + +/* + * Method: isSubsetOf + * Usage: if (set.isSubsetOf(set2)) ... + * ------------------------------------ + * Implements the subset relation on sets. It returns + * <code>true</code> if every element of this set is + * contained in <code>set2</code>. + */ + + bool isSubsetOf(const HashSet & set2) const; + +/* + * Method: clear + * Usage: set.clear(); + * ------------------- + * Removes all elements from this set. + */ + + void clear(); + +/* + * Operator: == + * Usage: set1 == set2 + * ------------------- + * Returns <code>true</code> if <code>set1</code> and <code>set2</code> + * contain the same elements. + */ + + bool operator==(const HashSet & set2) const; + +/* + * Operator: != + * Usage: set1 != set2 + * ------------------- + * Returns <code>true</code> if <code>set1</code> and <code>set2</code> + * are different. + */ + + bool operator!=(const HashSet & set2) const; + +/* + * Operator: + + * Usage: set1 + set2 + * set1 + element + * --------------------- + * Returns the union of sets <code>set1</code> and <code>set2</code>, which + * is the set of elements that appear in at least one of the two sets. The + * right hand set can be replaced by an element of the value type, in which + * case the operator returns a new set formed by adding that element. + */ + + HashSet operator+(const HashSet & set2) const; + HashSet operator+(const ValueType & element) const; + +/* + * Operator: * + * Usage: set1 * set2 + * ------------------ + * Returns the intersection of sets <code>set1</code> and <code>set2</code>, + * which is the set of all elements that appear in both. + */ + + HashSet operator*(const HashSet & set2) const; + +/* + * Operator: - + * Usage: set1 - set2 + * set1 - element + * --------------------- + * Returns the difference of sets <code>set1</code> and <code>set2</code>, + * which is all of the elements that appear in <code>set1</code> but + * not <code>set2</code>. The right hand set can be replaced by an + * element of the value type, in which case the operator returns a new + * set formed by removing that element. + */ + + HashSet operator-(const HashSet & set2) const; + HashSet operator-(const ValueType & element) const; + +/* + * Operator: += + * Usage: set1 += set2; + * set1 += value; + * --------------------- + * Adds all of the elements from <code>set2</code> (or the single + * specified value) to <code>set1</code>. As a convenience, the + * <code>HashSet</code> package also overloads the comma operator so + * that it is possible to initialize a set like this: + * + *<pre> + * HashSet<int< digits; + * digits += 0, 1, 2, 3, 4, 5, 6, 7, 8, 9; + *</pre> + */ + + HashSet & operator+=(const HashSet & set2); + HashSet & operator+=(const ValueType & value); + +/* + * Operator: *= + * Usage: set1 *= set2; + * -------------------- + * Removes any elements from <code>set1</code> that are not present in + * <code>set2</code>. + */ + + HashSet & operator*=(const HashSet & set2); + +/* + * Operator: -= + * Usage: set1 -= set2; + * set1 -= value; + * --------------------- + * Removes the elements from <code>set2</code> (or the single + * specified value) from <code>set1</code>. As a convenience, the + * <code>HashSet</code> package also overloads the comma operator so + * that it is possible to remove multiple elements from a set + * like this: + * + *<pre> + * digits -= 0, 2, 4, 6, 8; + *</pre> + * + * which removes the values 0, 2, 4, 6, and 8 from the set + * <code>digits</code>. + */ + + HashSet & operator-=(const HashSet & set2); + HashSet & operator-=(const ValueType & value); + +/* + * Method: first + * Usage: ValueType value = set.first(); + * ------------------------------------- + * Returns the first value in the set in the order established by the + * <code>foreach</code> macro. If the set is empty, <code>first</code> + * generates an error. + */ + + ValueType first() const; + +/* + * Method: toString + * Usage: string str = set.toString(); + * ----------------------------------- + * Converts the set to a printable string representation. + */ + + std::string toString(); + +/* + * Method: mapAll + * Usage: set.mapAll(fn); + * ---------------------- + * Iterates through the elements of the set and calls <code>fn(value)</code> + * for each one. The values are processed in ascending order, as defined + * by the comparison function. + */ + + void mapAll(void (*fn)(ValueType)) const; + void mapAll(void (*fn)(const ValueType &)) const; + + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional HashSet operations + * ----------------------------- + * In addition to the methods listed in this interface, the HashSet + * class supports the following operations: + * + * - Stream I/O using the << and >> operators + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement and STL iterators + * + * The iteration forms process the HashSet in an unspecified order. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + + HashMap<ValueType,bool> map; /* Map used to store the element */ + bool removeFlag; /* Flag to differentiate += and -= */ + +public: + +/* + * Hidden features + * --------------- + * The remainder of this file consists of the code required to + * support the comma operator, deep copying, and iteration. + * Including these methods in the public interface would make + * that interface more difficult to understand for the average client. + */ + + HashSet & operator,(const ValueType & value) { + if (this->removeFlag) { + this->remove(value); + } else { + this->add(value); + } + return *this; + } + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : public std::iterator<std::input_iterator_tag,ValueType> { + + private: + + typename HashMap<ValueType,bool>::iterator mapit; + + public: + + iterator() { + /* Empty */ + } + + iterator(typename HashMap<ValueType,bool>::iterator it) : mapit(it) { + /* Empty */ + } + + iterator(const iterator & it) { + mapit = it.mapit; + } + + iterator & operator++() { + ++mapit; + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + bool operator==(const iterator & rhs) { + return mapit == rhs.mapit; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + ValueType operator*() { + return *mapit; + } + + ValueType *operator->() { + return mapit; + } + }; + + iterator begin() const { + return iterator(map.begin()); + } + + iterator end() const { + return iterator(map.end()); + } + +}; + +extern void error(std::string msg); + +template <typename ValueType> +HashSet<ValueType>::HashSet() { + /* Empty */ +} + +template <typename ValueType> +HashSet<ValueType>::~HashSet() { + /* Empty */ +} + +template <typename ValueType> +int HashSet<ValueType>::size() const { + return map.size(); +} + +template <typename ValueType> +bool HashSet<ValueType>::isEmpty() const { + return map.isEmpty(); +} + +template <typename ValueType> +void HashSet<ValueType>::add(const ValueType & value) { + map.put(value, true); +} + +template <typename ValueType> +void HashSet<ValueType>::insert(const ValueType & value) { + map.put(value, true); +} + +template <typename ValueType> +void HashSet<ValueType>::remove(const ValueType & value) { + map.remove(value); +} + +template <typename ValueType> +bool HashSet<ValueType>::contains(const ValueType & value) const { + return map.containsKey(value); +} + +template <typename ValueType> +void HashSet<ValueType>::clear() { + map.clear(); +} + +template <typename ValueType> +bool HashSet<ValueType>::isSubsetOf(const HashSet & set2) const { + iterator it = begin(); + iterator end = this->end(); + while (it != end) { + if (!set2.map.containsKey(*it)) return false; + ++it; + } + return true; +} + +/* + * Implementation notes: set operators + * ----------------------------------- + * The implementations for the set operators use iteration to walk + * over the elements in one or both sets. + */ + +template <typename ValueType> +bool HashSet<ValueType>::operator==(const HashSet & set2) const { + return this->isSubsetOf(set2) && set2.isSubsetOf(*this); +} + +template <typename ValueType> +bool HashSet<ValueType>::operator!=(const HashSet & set2) const { + return !(*this == set2); +} + +template <typename ValueType> +HashSet<ValueType> HashSet<ValueType>::operator+(const HashSet & set2) const { + HashSet<ValueType> set = *this; + foreach (ValueType value in set2) { + set.add(value); + } + return set; +} + +template <typename ValueType> +HashSet<ValueType> +HashSet<ValueType>::operator+(const ValueType & element) const { + HashSet<ValueType> set = *this; + set.add(element); + return set; +} + +template <typename ValueType> +HashSet<ValueType> HashSet<ValueType>::operator*(const HashSet & set2) const { + HashSet<ValueType> set; + foreach (ValueType value in *this) { + if (set2.map.containsKey(value)) set.add(value); + } + return set; +} + +template <typename ValueType> +HashSet<ValueType> HashSet<ValueType>::operator-(const HashSet & set2) const { + HashSet<ValueType> set; + foreach (ValueType value in *this) { + if (!set2.map.containsKey(value)) set.add(value); + } + return set; +} + +template <typename ValueType> +HashSet<ValueType> +HashSet<ValueType>::operator-(const ValueType & element) const { + HashSet<ValueType> set = *this; + set.remove(element); + return set; +} + +template <typename ValueType> +HashSet<ValueType> & HashSet<ValueType>::operator+=(const HashSet & set2) { + foreach (ValueType value in set2) { + this->add(value); + } + return *this; +} + +template <typename ValueType> +HashSet<ValueType> & HashSet<ValueType>::operator+=(const ValueType & value) { + this->add(value); + this->removeFlag = false; + return *this; +} + +template <typename ValueType> +HashSet<ValueType> & HashSet<ValueType>::operator*=(const HashSet & set2) { + Vector<ValueType> toRemove; + foreach (ValueType value in *this) { + if (!set2.map.containsKey(value)) toRemove.add(value); + } + foreach (ValueType value in toRemove) { + this->remove(value); + } + return *this; +} + +template <typename ValueType> +HashSet<ValueType> & HashSet<ValueType>::operator-=(const HashSet & set2) { + Vector<ValueType> toRemove; + foreach (ValueType value in *this) { + if (set2.map.containsKey(value)) toRemove.add(value); + } + foreach (ValueType value in toRemove) { + this->remove(value); + } + return *this; +} + +template <typename ValueType> +HashSet<ValueType> & HashSet<ValueType>::operator-=(const ValueType & value) { + this->remove(value); + this->removeFlag = true; + return *this; +} + +template <typename ValueType> +ValueType HashSet<ValueType>::first() const { + if (isEmpty()) error("first: set is empty"); + return *begin(); +} + +template <typename ValueType> +std::string HashSet<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +template <typename ValueType> +void HashSet<ValueType>::mapAll(void (*fn)(ValueType)) const { + map.mapAll(fn); +} + +template <typename ValueType> +void HashSet<ValueType>::mapAll(void (*fn)(const ValueType &)) const { + map.mapAll(fn); +} + +template <typename ValueType> +template <typename FunctorType> +void HashSet<ValueType>::mapAll(FunctorType fn) const { + map.mapAll(fn); +} + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, const HashSet<ValueType> & set) { + os << "{"; + bool started = false; + foreach (ValueType value in set) { + if (started) os << ", "; + writeGenericValue(os, value, true); + started = true; + } + os << "}"; + return os; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, HashSet<ValueType> & set) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + set.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + ValueType value; + readGenericValue(is, value); + set += value; + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +// hashing functions for hash sets; defined in hashmap.cpp +int hashCode(const HashSet<std::string>& s); +int hashCode(const HashSet<int>& s); +int hashCode(const HashSet<char>& s); +int hashCode(const HashSet<long>& s); +int hashCode(const HashSet<double>& s); + +#endif diff --git a/labb8/lib/StanfordCPPLib/lexicon.cpp b/labb8/lib/StanfordCPPLib/lexicon.cpp new file mode 100755 index 0000000..140bef2 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/lexicon.cpp @@ -0,0 +1,316 @@ +/* + * File: lexicon.cpp + * ----------------- + * A lexicon is a word list. This lexicon is backed by two separate data + * structures for storing the words in the list: + * + * 1) a DAWG (directed acyclic word graph) + * 2) a Set<string> of other words. + * + * Typically the DAWG is used for a large list read from a file in binary + * format. The STL set is for words added piecemeal at runtime. + * + * The DAWG idea comes from an article by Appel & Jacobson, CACM May 1988. + * This lexicon implementation only has the code to load/search the DAWG. + * The DAWG builder code is quite a bit more intricate, see me (Julie) + * if you need it. + */ + +#include <fstream> +#include <string> +#include <cstring> +#include <algorithm> +#include <cstdlib> +#include <iostream> +#include <stdint.h> +#include "error.h" +#include "lexicon.h" +#include "strlib.h" +using namespace std; + +static void toLowerCaseInPlace(string & str); + +/* + * The DAWG is stored as an array of edges. Each edge is represented by + * one 32-bit struct. The 5 "letter" bits indicate the character on this + * transition (expressed as integer from 1 to 26), the "accept" bit indicates + * if you accept after appending that char (current path forms word), and the + * "lastEdge" bit marks this as the last edge in a sequence of childeren. + * The bulk of the bits (24) are used for the index within the edge array for + * the children of this node. The children are laid out contiguously in + * alphabetical order. Since we read edges as binary bits from a file in + * a big-endian format, we have to swap the struct order for little-endian + * machines. + */ + +Lexicon::Lexicon() { + edges = start = NULL; + numEdges = numDawgWords = 0; +} + +Lexicon::Lexicon(string filename) { + edges = start = NULL; + numEdges = numDawgWords = 0; + addWordsFromFile(filename); +} + +Lexicon::~Lexicon() { + if (edges) delete[] edges; +} + +/* + * Swaps a 4-byte long from big to little endian byte order + */ + +static uint32_t my_ntohl(uint32_t arg) { + uint32_t result = ((arg & 0xff000000) >> 24) | + ((arg & 0x00ff0000) >> 8) | + ((arg & 0x0000ff00) << 8) | + ((arg & 0x000000ff) << 24); + return result; +} + +/* + * Implementation notes: readBinaryFile + * ------------------------------------ + * The binary lexicon file format must follow this pattern: + * DAWG:<startnode index>:<num bytes>:<num bytes block of edge data> + */ + +void Lexicon::readBinaryFile(string filename) { + long startIndex, numBytes; + char firstFour[4], expected[] = "DAWG"; + ifstream istr(filename.c_str(), IOS_IN | IOS_BINARY); + if (false) my_ntohl(0); + if (istr.fail()) { + error("Couldn't open lexicon file " + filename); + } + istr.read(firstFour, 4); + istr.get(); + istr >> startIndex; + istr.get(); + istr >> numBytes; + istr.get(); + if (istr.fail() || strncmp(firstFour, expected, 4) != 0 + || startIndex < 0 || numBytes < 0) { + error("Improperly formed lexicon file " + filename); + } + numEdges = numBytes/sizeof(Edge); + edges = new Edge[numEdges]; + start = &edges[startIndex]; + istr.read((char *)edges, numBytes); + if (istr.fail() && !istr.eof()) { + error("Improperly formed lexicon file " + filename); + } + +#if defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN + uint32_t *cur = (uint32_t *) edges; + for (int i = 0; i < numEdges; i++, cur++) { + *cur = my_ntohl(*cur); + } +#endif + + istr.close(); + numDawgWords = countDawgWords(start); +} + +int Lexicon::countDawgWords(Edge *ep) const { + int count = 0; + while (true) { + if (ep->accept) count++; + if (ep->children != 0) { + count += countDawgWords(&edges[ep->children]); + } + if (ep->lastEdge) break; + ep++; + } + return count; +} + +/* + * Check for DAWG in first 4 to identify as special binary format, + * otherwise assume ASCII, one word per line + */ + +void Lexicon::addWordsFromFile(string filename) { + char firstFour[4], expected[] = "DAWG"; + ifstream istr(filename.c_str()); + if (istr.fail()) { + error("Couldn't open lexicon file " + filename); + } + istr.read(firstFour, 4); + if (strncmp(firstFour, expected, 4) == 0) { + if (otherWords.size() != 0) { + error("Binary files require an empty lexicon"); + } + readBinaryFile(filename); + return; + } + istr.seekg(0); + string line; + while (getline(istr, line)) { + add(line); + } + istr.close(); +} + +int Lexicon::size() const { + return numDawgWords + otherWords.size(); +} + +bool Lexicon::isEmpty() const { + return size() == 0; +} + +void Lexicon::clear() { + if (edges) delete[] edges; + edges = start = NULL; + numEdges = numDawgWords = 0; + otherWords.clear(); +} + +/* + * Implementation notes: findEdgeForChar + * ------------------------------------- + * Iterate over sequence of children to find one that + * matches the given char. Returns NULL if we get to + * last child without finding a match (thus no such + * child edge exists). + */ + +Lexicon::Edge *Lexicon::findEdgeForChar(Edge *children, char ch) const { + Edge *curEdge = children; + while (true) { + if (curEdge->letter == charToOrd(ch)) return curEdge; + if (curEdge->lastEdge) return NULL; + curEdge++; + } +} + +/* + * Implementation notes: traceToLastEdge + * ------------------------------------- + * Given a string, trace out path through the DAWG edge-by-edge. + * If a path exists, return last edge; otherwise return NULL. + */ + +Lexicon::Edge *Lexicon::traceToLastEdge(const string & s) const { + if (!start) return NULL; + Edge *curEdge = findEdgeForChar(start, s[0]); + int len = (int) s.length(); + for (int i = 1; i < len; i++) { + if (!curEdge || !curEdge->children) return NULL; + curEdge = findEdgeForChar(&edges[curEdge->children], s[i]); + } + return curEdge; +} + +bool Lexicon::containsPrefix(string prefix) const { + if (prefix.empty()) return true; + toLowerCaseInPlace(prefix); + if (traceToLastEdge(prefix)) return true; + foreach (string word in otherWords) { + if (startsWith(word, prefix)) return true; + if (prefix < word) return false; + } + return false; +} + +bool Lexicon::contains(string word) const { + toLowerCaseInPlace(word); + Edge *lastEdge = traceToLastEdge(word); + if (lastEdge && lastEdge->accept) return true; + return otherWords.contains(word); +} + +void Lexicon::add(string word) { + toLowerCaseInPlace(word); + if (!contains(word)) { + otherWords.add(word); + } +} + +Lexicon::Lexicon(const Lexicon & src) { + deepCopy(src); +} + +Lexicon & Lexicon::operator=(const Lexicon & src) { + if (this != &src) { + if (edges != NULL) delete[] edges; + deepCopy(src); + } + return *this; +} + +void Lexicon::deepCopy(const Lexicon & src) { + if (src.edges == NULL) { + edges = NULL; + start = NULL; + } else { + numEdges = src.numEdges; + edges = new Edge[src.numEdges]; + memcpy(edges, src.edges, sizeof(Edge)*src.numEdges); + start = edges + (src.start - src.edges); + } + numDawgWords = src.numDawgWords; + otherWords = src.otherWords; +} + +void Lexicon::mapAll(void (*fn)(string)) const { + foreach (string word in *this) { + fn(word); + } +} + +void Lexicon::mapAll(void (*fn)(const string &)) const { + foreach (string word in *this) { + fn(word); + } +} + +void Lexicon::iterator::advanceToNextWordInSet() { + if (setIterator == setEnd) { + currentSetWord = ""; + } else { + currentSetWord = *setIterator; + ++setIterator; + } +} + +void Lexicon::iterator::advanceToNextWordInDawg() { + if (edgePtr == NULL) { + edgePtr = lp->start; + } else { + advanceToNextEdge(); + } + while (edgePtr != NULL && !edgePtr->accept) { + advanceToNextEdge(); + } +} + +void Lexicon::iterator::advanceToNextEdge() { + Edge *ep = edgePtr; + if (ep->children == 0) { + while (ep != NULL && ep->lastEdge) { + if (stack.isEmpty()) { + edgePtr = NULL; + return; + } else { + ep = stack.pop(); + currentDawgPrefix.resize(currentDawgPrefix.length() - 1); + } + } + edgePtr = ep + 1; + } else { + stack.push(ep); + currentDawgPrefix.push_back(lp->ordToChar(ep->letter)); + edgePtr = &lp->edges[ep->children]; + } +}; + +static void toLowerCaseInPlace(string & str) { + int nChars = str.length(); + for (int i = 0; i < nChars; i++) { + str[i] = tolower(str[i]); + } +} diff --git a/labb8/lib/StanfordCPPLib/lexicon.h b/labb8/lib/StanfordCPPLib/lexicon.h new file mode 100755 index 0000000..fea4726 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/lexicon.h @@ -0,0 +1,366 @@ +/* + * File: lexicon.h + * --------------- + * This file exports the <code>Lexicon</code> class, which is a + * compact structure for storing a list of words. + */ + +#ifndef _lexicon_h +#define _lexicon_h + +#include <string> +#include "foreach.h" +#include "set.h" +#include "stack.h" + +/* + * Class: Lexicon + * -------------- + * This class is used to represent a <b><i>lexicon,</i></b> or word list. + * The main difference between a lexicon and a dictionary is that + * a lexicon does not provide any mechanism for storing definitions; + * the lexicon contains only words, with no associated information. + * It is therefore similar to a set of strings, but with a more + * space-efficient internal representation. The <code>Lexicon</code> + * class supports efficient lookup operations for words and prefixes. + * + * <p>As an example of the use of the <code>Lexicon</code> class, the + * following program lists all the two-letter words in the lexicon + * stored in <code>EnglishWords.dat</code>: + * + *<pre> + * int main() { + * Lexicon english("EnglishWords.dat"); + * foreach (string word in english) { + * if (word.length() == 2) { + * cout << word << endl; + * } + * } + * return 0; + * } + *</pre> + */ + +#include <cctype> + +class Lexicon { + +public: + +/* + * Constructor: Lexicon + * Usage: Lexicon lex; + * Lexicon lex(filename); + * ----------------------------- + * Initializes a new lexicon. The default constructor creates an empty + * lexicon. The second form reads in the contents of the lexicon from + * the specified data file. The data file must be in one of two formats: + * (1) a space-efficient precompiled binary format or (2) a text file + * containing one word per line. The Stanford library distribution + * includes a binary lexicon file named <code>English.dat</code> + * containing a list of words in English. The standard code pattern + * to initialize that lexicon looks like this: + * + *<pre> + * Lexicon english("English.dat"); + *</pre> + */ + + Lexicon(); + Lexicon(std::string filename); + +/* + * Destructor: ~Lexicon + * -------------------- + * The destructor deallocates any storage associated with the lexicon. + */ + + virtual ~Lexicon(); + +/* + * Method: size + * Usage: int n = lex.size(); + * -------------------------- + * Returns the number of words contained in the lexicon. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (lex.isEmpty()) ... + * ----------------------------- + * Returns <code>true</code> if the lexicon contains no words. + */ + + bool isEmpty() const; + +/* + * Method: clear + * Usage: lex.clear(); + * ------------------- + * Removes all words from the lexicon. + */ + + void clear(); + +/* + * Method: add + * Usage: lex.add(word); + * --------------------- + * Adds the specified word to the lexicon. + */ + + void add(std::string word); + +/* + * Method: addWordsFromFile + * Usage: lex.addWordsFromFile(filename); + * -------------------------------------- + * Reads the file and adds all of its words to the lexicon. + */ + + void addWordsFromFile(std::string filename); + +/* + * Method: contains + * Usage: if (lex.contains(word)) ... + * ---------------------------------- + * Returns <code>true</code> if <code>word</code> is contained in the + * lexicon. In the <code>Lexicon</code> class, the case of letters is + * ignored, so "Zoo" is the same as "ZOO" or "zoo". + */ + + bool contains(std::string word) const; + +/* + * Method: containsPrefix + * Usage: if (lex.containsPrefix(prefix)) ... + * ------------------------------------------ + * Returns true if any words in the lexicon begin with <code>prefix</code>. + * Like <code>containsWord</code>, this method ignores the case of letters + * so that "MO" is a prefix of "monkey" or "Monday". + */ + + bool containsPrefix(std::string prefix) const; + +/* + * Method: mapAll + * Usage: lexicon.mapAll(fn); + * -------------------------- + * Calls the specified function on each word in the lexicon. + */ + + void mapAll(void (*fn)(std::string)) const; + void mapAll(void (*fn)(const std::string &)) const; + + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional Lexicon operations + * ----------------------------- + * In addition to the methods listed in this interface, the Lexicon + * class supports the following operations: + * + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement and STL iterators + * + * All iteration is guaranteed to proceed in alphabetical order. All + * words in the lexicon are stored in lowercase. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +#ifdef _WIN32 +#define LITTLE_ENDIAN 1 +#define BYTE_ORDER LITTLE_ENDIAN +#endif + +#pragma pack(1) + struct Edge { +#if defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN + unsigned long letter:5; + unsigned long lastEdge:1; + unsigned long accept:1; + unsigned long unused:1; + unsigned long children:24; +#else + unsigned long children:24; + unsigned long unused:1; + unsigned long accept:1; + unsigned long lastEdge:1; + unsigned long letter:5; +#endif + }; +#pragma pack() + + Edge *edges, *start; + int numEdges, numDawgWords; + Set<std::string> otherWords; + +public: + +/* + * Deep copying support + * -------------------- + * This copy constructor and operator= are defined to make a + * deep copy, making it possible to pass/return lexicons by value + * and assign from one lexicon to another. The entire contents of + * the lexicon, including all words, are copied. Making copies is + * generally avoided because of the expense and thus, lexicons are + * typically passed by reference. When a copy is needed, these + * operations are supported. + */ + + Lexicon(const Lexicon & src); + Lexicon & operator=(const Lexicon & src); + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : public std::iterator<std::input_iterator_tag,std::string> { + private: + const Lexicon *lp; + int index; + std::string currentDawgPrefix; + std::string currentSetWord; + std::string tmpWord; + Edge *edgePtr; + Stack<Edge *> stack; + Set<std::string>::iterator setIterator; + Set<std::string>::iterator setEnd; + + void advanceToNextWordInDawg(); + void advanceToNextWordInSet(); + void advanceToNextEdge(); + + public: + iterator() { + this->lp = NULL; + } + + iterator(const Lexicon *lp, bool endFlag) { + this->lp = lp; + if (endFlag) { + index = lp->size(); + } else { + index = 0; + edgePtr = NULL; + setIterator = lp->otherWords.begin(); + setEnd = lp->otherWords.end(); + currentDawgPrefix = ""; + currentSetWord = ""; + advanceToNextWordInDawg(); + advanceToNextWordInSet(); + } + } + + iterator(const iterator & it) { + lp = it.lp; + index = it.index; + currentDawgPrefix = it.currentDawgPrefix; + currentSetWord = it.currentSetWord; + edgePtr = it.edgePtr; + stack = it.stack; + setIterator = it.setIterator; + } + + iterator & operator++() { + if (edgePtr == NULL) { + advanceToNextWordInSet(); + } else { + if (currentSetWord == "" || currentDawgPrefix < currentSetWord) { + advanceToNextWordInDawg(); + } else { + advanceToNextWordInSet(); + } + } + index++; + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + bool operator==(const iterator & rhs) { + return lp == rhs.lp && index == rhs.index; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + std::string operator*() { + if (edgePtr == NULL) return currentSetWord; + if (currentSetWord == "" || currentDawgPrefix < currentSetWord) { + return currentDawgPrefix + lp->ordToChar(edgePtr->letter); + } else { + return currentSetWord; + } + } + + std::string *operator->() { + if (edgePtr == NULL) return ¤tSetWord; + if (currentSetWord == "" || currentDawgPrefix < currentSetWord) { + tmpWord = currentDawgPrefix + lp->ordToChar(edgePtr->letter); + return &tmpWord; + } else { + return ¤tSetWord; + } + } + + }; + + iterator begin() const { + return iterator(this, false); + } + + iterator end() const { + return iterator(this, true); + } + +private: + + Edge *findEdgeForChar(Edge *children, char ch) const; + Edge *traceToLastEdge(const std::string & s) const; + void readBinaryFile(std::string filename); + void deepCopy(const Lexicon & src); + int countDawgWords(Edge *start) const; + + unsigned int charToOrd(char ch) const { + return ((unsigned int)(tolower(ch) - 'a' + 1)); + } + + char ordToChar(unsigned int ord) const { + return ((char)(ord - 1 + 'a')); + } + +}; + +template <typename FunctorType> +void Lexicon::mapAll(FunctorType fn) const { + foreach (std::string word in *this) { + fn(word); + } +} + +// hashing functions for lexicons; defined in hashmap.cpp +int hashCode(const Lexicon& l); + +#endif diff --git a/labb8/lib/StanfordCPPLib/main.cpp b/labb8/lib/StanfordCPPLib/main.cpp new file mode 100755 index 0000000..6feb8aa --- /dev/null +++ b/labb8/lib/StanfordCPPLib/main.cpp @@ -0,0 +1,108 @@ +/* + * File: main.cpp + * -------------- + * This file defines a default version of the Main function that takes + * the argc and argv arguments. This function must be defined in its + * own module to ensure that it is loaded only if the client doesn't + * supply one. + */ + +#include <exception> +#include <iostream> +#include <string> + +using namespace std; + +int Main(int, char *[]) { + extern int Main(); + return Main(); +} + +void __StanfordCPPLib_terminate_handler() { + try { + throw; // re-throws the exception that already occurred + } catch (const std::exception& ex) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** An exception occurred during program execution: \n"; + msg += " *** "; + msg += ex.what(); + msg += "\n ***\n\n"; + cerr << msg; + throw ex; + } catch (std::string str) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A string exception occurred during program execution: \n"; + msg += " *** \""; + msg += str; + msg += "\"\n ***\n"; + cerr << msg; + throw str; + } catch (char const* str) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A string exception occurred during program execution: \n"; + msg += " *** \""; + msg += str; + msg += "\"\n ***\n"; + cerr << msg; + throw str; + } catch (int n) { + char buf[128]; + snprintf(buf, 128, "%d", n); + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** An int exception occurred during program execution: \n"; + msg += " *** "; + msg += buf; + msg += "\n ***\n\n"; + cerr << msg; + throw n; + } catch (long l) { + char buf[128]; + snprintf(buf, 128, "%ld", l); + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A long exception occurred during program execution: \n"; + msg += " *** "; + msg += buf; + msg += "\n ***\n\n"; + cerr << msg; + throw l; + } catch (char c) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A char exception occurred during program execution: \n"; + msg += " *** '"; + msg += c; + msg += "'\n ***\n"; + cerr << msg; + throw c; + } catch (bool b) { + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A bool exception occurred during program execution: \n"; + msg += " *** "; + msg += (b ? "true" : "false"); + msg += "\n ***\n\n"; + cerr << msg; + throw b; + } catch (double d) { + char buf[128]; + snprintf(buf, 128, "%lf", d); + string msg = "\n ***\n"; + msg += " *** STANFORD C++ LIBRARY \n"; + msg += " *** A double exception occurred during program execution: \n"; + msg += " *** "; + msg += buf; + msg += "\n ***\n\n"; + cerr << msg; + throw d; + } + abort(); +} + +void __StanfordCPPLib_set_terminate() { + set_terminate(__StanfordCPPLib_terminate_handler); +} diff --git a/labb8/lib/StanfordCPPLib/map.h b/labb8/lib/StanfordCPPLib/map.h new file mode 100755 index 0000000..663de03 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/map.h @@ -0,0 +1,910 @@ +/* + * File: map.h + * ----------- + * This file exports the template class <code>Map</code>, which + * maintains a collection of <i>key</i>-<i>value</i> pairs. + */ + +#ifndef _map_h +#define _map_h + +#include <cstdlib> +#include "foreach.h" +#include "stack.h" +#include "vector.h" + +/* + * Class: Map<KeyType,ValueType> + * ----------------------------- + * This class maintains an association between <b><i>keys</i></b> and + * <b><i>values</i></b>. The types used for keys and values are + * specified using templates, which makes it possible to use + * this structure with any data type. + */ + +template <typename KeyType, typename ValueType> +class Map { + +public: + +/* + * Constructor: Map + * Usage: Map<KeyType,ValueType> map; + * ---------------------------------- + * Initializes a new empty map that associates keys and values of the + * specified types. + */ + + Map(); + +/* + * Destructor: ~Map + * ---------------- + * Frees any heap storage associated with this map. + */ + + virtual ~Map(); + +/* + * Method: size + * Usage: int nEntries = map.size(); + * --------------------------------- + * Returns the number of entries in this map. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (map.isEmpty()) ... + * ----------------------------- + * Returns <code>true</code> if this map contains no entries. + */ + + bool isEmpty() const; + +/* + * Method: put + * Usage: map.put(key, value); + * --------------------------- + * Associates <code>key</code> with <code>value</code> in this map. + * Any previous value associated with <code>key</code> is replaced + * by the new value. + */ + + void put(const KeyType & key, const ValueType & value); + +/* + * Method: get + * Usage: ValueType value = map.get(key); + * -------------------------------------- + * Returns the value associated with <code>key</code> in this map. + * If <code>key</code> is not found, <code>get</code> returns the + * default value for <code>ValueType</code>. + */ + + ValueType get(const KeyType & key) const; + +/* + * Method: containsKey + * Usage: if (map.containsKey(key)) ... + * ------------------------------------ + * Returns <code>true</code> if there is an entry for <code>key</code> + * in this map. + */ + + bool containsKey(const KeyType & key) const; + +/* + * Method: remove + * Usage: map.remove(key); + * ----------------------- + * Removes any entry for <code>key</code> from this map. + */ + + void remove(const KeyType & key); + +/* + * Method: clear + * Usage: map.clear(); + * ------------------- + * Removes all entries from this map. + */ + + void clear(); + +/* + * Method: keys + * Usage: Vector<KeyType> keys = map.keys(); + * ------------------------------------------- + * Returns a collection containing all keys in this map. + */ + + Vector<KeyType> keys() const; + +/* + * Method: values + * Usage: Vector<ValueType> values = map.values(); + * ------------------------------------------- + * Returns a collection containing all values in this map. + */ + + Vector<ValueType> values() const; + +/* + * Operator: [] + * Usage: map[key] + * --------------- + * Selects the value associated with <code>key</code>. This syntax + * makes it easy to think of a map as an "associative array" + * indexed by the key type. If <code>key</code> is already present + * in the map, this function returns a reference to its associated + * value. If key is not present in the map, a new entry is created + * whose value is set to the default for the value type. + */ + + ValueType & operator[](const KeyType & key); + ValueType operator[](const KeyType & key) const; + +/* + * Method: toString + * Usage: string str = map.toString(); + * ----------------------------------- + * Converts the map to a printable string representation. + */ + + std::string toString(); + +/* + * Method: mapAll + * Usage: map.mapAll(fn); + * ---------------------- + * Iterates through the map entries and calls <code>fn(key, value)</code> + * for each one. The keys are processed in ascending order, as defined + * by the comparison function. + */ + + void mapAll(void (*fn)(KeyType, ValueType)) const; + void mapAll(void (*fn)(const KeyType &, const ValueType &)) const; + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional Map operations + * ------------------------- + * In addition to the methods listed in this interface, the Map + * class supports the following operations: + * + * - Stream I/O using the << and >> operators + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement and STL iterators + * + * All iteration is guaranteed to proceed in the order established by + * the comparison function passed to the constructor, which ordinarily + * matches the order of the key type. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Implementation notes: + * --------------------- + * The map class is represented using a binary search tree. The + * specific implementation used here is the classic AVL algorithm + * developed by Georgii Adel'son-Vel'skii and Evgenii Landis in 1962. + */ + +private: + +/* Constant definitions */ + + static const int BST_LEFT_HEAVY = -1; + static const int BST_IN_BALANCE = 0; + static const int BST_RIGHT_HEAVY = +1; + +/* Type definition for nodes in the binary search tree */ + + struct BSTNode { + KeyType key; /* The key stored in this node */ + ValueType value; /* The corresponding value */ + BSTNode *left; /* Subtree containing all smaller keys */ + BSTNode *right; /* Subtree containing all larger keys */ + int bf; /* AVL balance factor */ + }; + +/* + * Implementation notes: Comparator + * -------------------------------- + * The Comparator class encapsulates a functor that compares two values + * of KeyType. In contrast to the classes in the STL, all of which embed + * the comparator in the type, the Map class and its derivatives pass an + * optional Comparator value. While this strategy results in a more + * complex implementation, it has the advantage of allowing maps and sets + * to carry their own comparators without forcing the client to include + * the comparator in the template declaration. This simplification is + * particularly important for the Graph class. + * + * The allocation is required in the TemplateComparator class because + * the type std::binary_function has subclasses but does not define a + * virtual destructor. + */ + + class Comparator { + public: + virtual ~Comparator() { } + virtual bool lessThan(const KeyType & k1, const KeyType & k2) = 0; + virtual Comparator *clone() = 0; + }; + + template <typename CompareType> + class TemplateComparator : public Comparator { + public: + TemplateComparator(CompareType cmp) { + this->cmp = new CompareType(cmp); + } + + virtual bool lessThan(const KeyType & k1, const KeyType & k2) { + return (*cmp)(k1, k2); + } + + virtual Comparator *clone() { + return new TemplateComparator<CompareType>(*cmp); + } + + private: + CompareType *cmp; + }; + + Comparator & getComparator() const { + return *cmpp; + } + +/* Instance variables */ + + BSTNode *root; /* Pointer to the root of the tree */ + int nodeCount; /* Number of entries in the map */ + Comparator *cmpp; /* Pointer to the comparator */ + + int (*cmpFn)(const KeyType &, const KeyType &); + +/* Private methods */ + +/* + * Implementation notes: findNode(t, key) + * -------------------------------------- + * Searches the tree rooted at t to find the specified key, searching + * in the left or right subtree, as approriate. If a matching node + * is found, findNode returns a pointer to the value cell in that node. + * If no matching node exists in the tree, findNode returns NULL. + */ + + ValueType *findNode(BSTNode *t, const KeyType & key) const { + if (t == NULL) return NULL; + int sign = compareKeys(key, t->key); + if (sign == 0) return &t->value; + if (sign < 0) { + return findNode(t->left, key); + } else { + return findNode(t->right, key); + } + } + +/* + * Implementation notes: addNode(t, key, heightFlag) + * ------------------------------------------------- + * Searches the tree rooted at t to find the specified key, searching + * in the left or right subtree, as approriate. If a matching node + * is found, addNode returns a pointer to the value cell in that node, + * just like findNode. If no matching node exists in the tree, addNode + * creates a new node with a default value. The heightFlag reference + * parameter returns a bool indicating whether the height of the tree + * was changed by this operation. + */ + + ValueType *addNode(BSTNode * & t, const KeyType & key, bool & heightFlag) { + heightFlag = false; + if (t == NULL) { + t = new BSTNode(); + t->key = key; + t->value = ValueType(); + t->bf = BST_IN_BALANCE; + t->left = t->right = NULL; + heightFlag = true; + nodeCount++; + return &t->value; + } + int sign = compareKeys(key, t->key); + if (sign == 0) return &t->value; + ValueType *vp = NULL; + int bfDelta = BST_IN_BALANCE; + if (sign < 0) { + vp = addNode(t->left, key, heightFlag); + if (heightFlag) bfDelta = BST_LEFT_HEAVY; + } else { + vp = addNode(t->right, key, heightFlag); + if (heightFlag) bfDelta = BST_RIGHT_HEAVY; + } + updateBF(t, bfDelta); + heightFlag = (bfDelta != 0 && t->bf != BST_IN_BALANCE); + return vp; + } + +/* + * Implementation notes: removeNode(t, key) + * ---------------------------------------- + * Removes the node containing the specified key from the tree rooted + * at t. The return value is true if the height of this subtree + * changes. The removeTargetNode method does the actual deletion. + */ + + bool removeNode(BSTNode * & t, const KeyType & key) { + if (t == NULL) return false; + int sign = compareKeys(key, t->key); + if (sign == 0) return removeTargetNode(t); + int bfDelta = BST_IN_BALANCE; + if (sign < 0) { + if (removeNode(t->left, key)) bfDelta = BST_RIGHT_HEAVY; + } else { + if (removeNode(t->right, key)) bfDelta = BST_LEFT_HEAVY; + } + updateBF(t, bfDelta); + return bfDelta != 0 && t->bf == BST_IN_BALANCE; + } + +/* + * Implementation notes: removeTargetNode(t) + * ----------------------------------------- + * Removes the node which is passed by reference as t. The easy case + * occurs when either (or both) of the children is NULL; all you need + * to do is replace the node with its non-NULL child, if any. If both + * children are non-NULL, this code finds the rightmost descendent of + * the left child; this node may not be a leaf, but will have no right + * child. Its left child replaces it in the tree, after which the + * replacement data is moved to the position occupied by the target node. + */ + + bool removeTargetNode(BSTNode * & t) { + BSTNode *toDelete = t; + if (t->left == NULL) { + t = t->right; + delete toDelete; + nodeCount--; + return true; + } else if (t->right == NULL) { + t = t->left; + delete toDelete; + nodeCount--; + return true; + } else { + BSTNode *successor = t->left; + while (successor->right != NULL) { + successor = successor->right; + } + t->key = successor->key; + t->value = successor->value; + if (removeNode(t->left, successor->key)) { + updateBF(t, BST_RIGHT_HEAVY); + return (t->bf == BST_IN_BALANCE); + } + return false; + } + } + +/* + * Implementation notes: updateBF(t, bfDelta) + * ------------------------------------------ + * Updates the balance factor in the node and rebalances the tree + * if necessary. + */ + + void updateBF(BSTNode * & t, int bfDelta) { + t->bf += bfDelta; + if (t->bf < BST_LEFT_HEAVY) { + fixLeftImbalance(t); + } else if (t->bf > BST_RIGHT_HEAVY) { + fixRightImbalance(t); + } + } + +/* + * Implementation notes: fixLeftImbalance(t) + * ----------------------------------------- + * This function is called when a node has been found that is out + * of balance with the longer subtree on the left. Depending on + * the balance factor of the left child, the code performs a + * single or double rotation. + */ + + void fixLeftImbalance(BSTNode * & t) { + BSTNode *child = t->left; + if (child->bf == BST_RIGHT_HEAVY) { + int oldBF = child->right->bf; + rotateLeft(t->left); + rotateRight(t); + t->bf = BST_IN_BALANCE; + switch (oldBF) { + case BST_LEFT_HEAVY: + t->left->bf = BST_IN_BALANCE; + t->right->bf = BST_RIGHT_HEAVY; + break; + case BST_IN_BALANCE: + t->left->bf = t->right->bf = BST_IN_BALANCE; + break; + case BST_RIGHT_HEAVY: + t->left->bf = BST_LEFT_HEAVY; + t->right->bf = BST_IN_BALANCE; + break; + } + } else if (child->bf == BST_IN_BALANCE) { + rotateRight(t); + t->bf = BST_RIGHT_HEAVY; + t->right->bf = BST_LEFT_HEAVY; + } else { + rotateRight(t); + t->right->bf = t->bf = BST_IN_BALANCE; + } + } + +/* + * Implementation notes: rotateLeft(t) + * ----------------------------------- + * This function performs a single left rotation of the tree + * that is passed by reference. The balance factors + * are unchanged by this function and must be corrected at a + * higher level of the algorithm. + */ + + void rotateLeft(BSTNode * & t) { + BSTNode *child = t->right; + t->right = child->left; + child->left = t; + t = child; + } + +/* + * Implementation notes: fixRightImbalance(t) + * ------------------------------------------ + * This function is called when a node has been found that + * is out of balance with the longer subtree on the right. + * Depending on the balance factor of the right child, the + * code performs a single or double rotation. + */ + + void fixRightImbalance(BSTNode * & t) { + BSTNode *child = t->right; + if (child->bf == BST_LEFT_HEAVY) { + int oldBF = child->left->bf; + rotateRight(t->right); + rotateLeft(t); + t->bf = BST_IN_BALANCE; + switch (oldBF) { + case BST_LEFT_HEAVY: + t->left->bf = BST_IN_BALANCE; + t->right->bf = BST_RIGHT_HEAVY; + break; + case BST_IN_BALANCE: + t->left->bf = t->right->bf = BST_IN_BALANCE; + break; + case BST_RIGHT_HEAVY: + t->left->bf = BST_LEFT_HEAVY; + t->right->bf = BST_IN_BALANCE; + break; + } + } else if (child->bf == BST_IN_BALANCE) { + rotateLeft(t); + t->bf = BST_LEFT_HEAVY; + t->left->bf = BST_RIGHT_HEAVY; + } else { + rotateLeft(t); + t->left->bf = t->bf = BST_IN_BALANCE; + } + } + +/* + * Implementation notes: rotateRight(t) + * ------------------------------------ + * This function performs a single right rotation of the tree + * that is passed by reference. The balance factors + * are unchanged by this function and must be corrected at a + * higher level of the algorithm. + */ + + void rotateRight(BSTNode * & t) { + + BSTNode *child = t->left; + t->left = child->right; + child->right = t; + t = child; + } + +/* + * Implementation notes: deleteTree(t) + * ----------------------------------- + * Deletes all the nodes in the tree. + */ + + void deleteTree(BSTNode *t) { + if (t != NULL) { + deleteTree(t->left); + deleteTree(t->right); + delete t; + } + } + +/* + * Implementation notes: mapAll + * ---------------------------- + * Calls fn(key, value) for every key-value pair in the tree. + */ + + void mapAll(BSTNode *t, void (*fn)(KeyType, ValueType)) const { + if (t != NULL) { + mapAll(t->left, fn); + fn(t->key, t->value); + mapAll(t->right, fn); + } + } + + void mapAll(BSTNode *t, + void (*fn)(const KeyType &, const ValueType &)) const { + if (t != NULL) { + mapAll(t->left, fn); + fn(t->key, t->value); + mapAll(t->right, fn); + } + } + + template <typename FunctorType> + void mapAll(BSTNode *t, FunctorType fn) const { + if (t != NULL) { + mapAll(t->left, fn); + fn(t->key, t->value); + mapAll(t->right, fn); + } + } + + void deepCopy(const Map & other) { + root = copyTree(other.root); + nodeCount = other.nodeCount; + cmpp = (other.cmpp == NULL) ? NULL : other.cmpp->clone(); + } + + BSTNode *copyTree(BSTNode * const t) { + if (t == NULL) return NULL; + BSTNode *np = new BSTNode(); + np->key = t->key; + np->value = t->value; + np->bf = t->bf; + np->left = copyTree(t->left); + np->right = copyTree(t->right); + return np; + } + +public: + +/* + * Hidden features + * --------------- + * The remainder of this file consists of the code required to + * support deep copying and iteration. Including these methods in + * the public portion of the interface would make that interface more + * difficult to understand for the average client. + */ + +/* Extended constructors */ + + template <typename CompareType> + explicit Map(CompareType cmp) { + root = NULL; + nodeCount = 0; + cmpp = new TemplateComparator<CompareType>(cmp); + } + +/* + * Implementation notes: compareKeys(k1, k2) + * ----------------------------------------- + * Compares the keys k1 and k2 and returns an integer (-1, 0, or +1) + * depending on whether k1 < k2, k1 == k2, or k1 > k2, respectively. + */ + + int compareKeys(const KeyType & k1, const KeyType & k2) const { + if (cmpp->lessThan(k1, k2)) return -1; + if (cmpp->lessThan(k2, k1)) return +1; + return 0; + } + +/* + * Deep copying support + * -------------------- + * This copy constructor and operator= are defined to make a + * deep copy, making it possible to pass/return maps by value + * and assign from one map to another. + */ + + Map & operator=(const Map & src) { + if (this != &src) { + clear(); + deepCopy(src); + } + return *this; + } + + Map(const Map & src) { + deepCopy(src); + } + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : public std::iterator<std::input_iterator_tag,KeyType> { + + private: + + struct NodeMarker { + BSTNode *np; + bool processed; + }; + + const Map *mp; /* Pointer to the map */ + int index; /* Index of current element */ + Stack<NodeMarker> stack; /* Stack of unprocessed nodes */ + + void findLeftmostChild() { + BSTNode *np = stack.peek().np; + if (np == NULL) return; + while (np->left != NULL) { + NodeMarker marker = { np->left, false }; + stack.push(marker); + np = np->left; + } + } + + public: + + iterator() { + /* Empty */ + } + + iterator(const Map *mp, bool end) { + this->mp = mp; + if (end || mp->nodeCount == 0) { + index = mp->nodeCount; + } else { + index = 0; + NodeMarker marker = { mp->root, false }; + stack.push(marker); + findLeftmostChild(); + } + } + + iterator(const iterator & it) { + mp = it.mp; + index = it.index; + stack = it.stack; + } + + iterator & operator++() { + NodeMarker marker = stack.pop(); + BSTNode *np = marker.np; + if (np->right == NULL) { + while (!stack.isEmpty() && stack.peek().processed) { + stack.pop(); + } + } else { + marker.processed = true; + stack.push(marker); + marker.np = np->right; + marker.processed = false; + stack.push(marker); + findLeftmostChild(); + } + index++; + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + bool operator==(const iterator & rhs) { + return mp == rhs.mp && index == rhs.index; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + KeyType operator*() { + return stack.peek().np->key; + } + + KeyType *operator->() { + return &stack.peek().np->key; + } + + friend class Map; + + }; + + iterator begin() const { + return iterator(this, false); + } + + iterator end() const { + return iterator(this, true); + } + +}; + +template <typename KeyType, typename ValueType> +Map<KeyType,ValueType>::Map() { + root = NULL; + nodeCount = 0; + cmpp = new TemplateComparator< less<KeyType> >(less<KeyType>()); +} + +template <typename KeyType, typename ValueType> +Map<KeyType,ValueType>::~Map() { + if (cmpp != NULL) delete cmpp; + deleteTree(root); +} + +template <typename KeyType, typename ValueType> +int Map<KeyType,ValueType>::size() const { + return nodeCount; +} + +template <typename KeyType, typename ValueType> +bool Map<KeyType,ValueType>::isEmpty() const { + return nodeCount == 0; +} + +template <typename KeyType, typename ValueType> +void Map<KeyType,ValueType>::put(const KeyType & key, + const ValueType & value) { + bool dummy; + *addNode(root, key, dummy) = value; +} + +template <typename KeyType, typename ValueType> +ValueType Map<KeyType,ValueType>::get(const KeyType & key) const { + ValueType *vp = findNode(root, key); + if (vp == NULL) return ValueType(); + return *vp; +} + +template <typename KeyType, typename ValueType> +void Map<KeyType,ValueType>::remove(const KeyType & key) { + removeNode(root, key); +} + +template <typename KeyType, typename ValueType> +void Map<KeyType,ValueType>::clear() { + deleteTree(root); + root = NULL; + nodeCount = 0; +} + +template <typename KeyType, typename ValueType> +bool Map<KeyType,ValueType>::containsKey(const KeyType & key) const { + return findNode(root, key) != NULL; +} + +template <typename KeyType, typename ValueType> +ValueType & Map<KeyType,ValueType>::operator[](const KeyType & key) { + bool dummy; + return *addNode(root, key, dummy); +} + +template <typename KeyType, typename ValueType> +ValueType Map<KeyType,ValueType>::operator[](const KeyType & key) const { + return get(key); +} + +template <typename KeyType, typename ValueType> +void Map<KeyType,ValueType>::mapAll(void (*fn)(KeyType, ValueType)) const { + mapAll(root, fn); +} + +template <typename KeyType, typename ValueType> +void Map<KeyType,ValueType>::mapAll(void (*fn)(const KeyType &, + const ValueType &)) const { + mapAll(root, fn); +} + +template <typename KeyType, typename ValueType> +template <typename FunctorType> +void Map<KeyType,ValueType>::mapAll(FunctorType fn) const { + mapAll(root, fn); +} + +template <typename KeyType, typename ValueType> +std::string Map<KeyType,ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +template <typename KeyType,typename ValueType> +Vector<KeyType> Map<KeyType,ValueType>::keys() const { + Vector<KeyType> keyset; + foreach (KeyType key in *this) { + keyset.add(key); + } + return keyset; +} + +template <typename KeyType,typename ValueType> +Vector<ValueType> Map<KeyType,ValueType>::values() const { + Vector<ValueType> values; + foreach (KeyType key in *this) { + values.add(this->get(key)); + } + return values; +} + +/* + * Implementation notes: << and >> + * ------------------------------- + * The insertion and extraction operators use the template facilities in + * strlib.h to read and write generic values in a way that treats strings + * specially. + */ + +template <typename KeyType, typename ValueType> +std::ostream & operator<<(std::ostream & os, + const Map<KeyType,ValueType> & map) { + os << "{"; + typename Map<KeyType,ValueType>::iterator begin = map.begin(); + typename Map<KeyType,ValueType>::iterator end = map.end(); + typename Map<KeyType,ValueType>::iterator it = begin; + while (it != end) { + if (it != begin) os << ", "; + writeGenericValue(os, *it, false); + os << ":"; + writeGenericValue(os, map[*it], false); + ++it; + } + return os << "}"; +} + +template <typename KeyType, typename ValueType> +std::istream & operator>>(std::istream & is, Map<KeyType,ValueType> & map) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + map.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + KeyType key; + readGenericValue(is, key); + is >> ch; + if (ch != ':') error("operator >>: Missing colon after key"); + ValueType value; + readGenericValue(is, value); + map[key] = value; + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +#endif diff --git a/labb8/lib/StanfordCPPLib/platform.cpp b/labb8/lib/StanfordCPPLib/platform.cpp new file mode 100755 index 0000000..d24140e --- /dev/null +++ b/labb8/lib/StanfordCPPLib/platform.cpp @@ -0,0 +1,1417 @@ +/* + * File: platform.cpp + * ------------------ + * This file implements the platform interface by passing commands to + * a Java back end that manages the display. + */ + +#ifdef _WIN32 +# include <windows.h> +# include <tchar.h> +# undef MOUSE_EVENT +# undef KEY_EVENT +# undef MOUSE_MOVED +# undef HELP_KEY +#else +# include <sys/types.h> +# include <sys/stat.h> +# include <dirent.h> +# include <errno.h> +# include <pwd.h> +# include <unistd.h> +static bool tracePipe; +static int pin; +static int pout; +#endif + +#include <algorithm> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> +#include "error.h" +#include "filelib.h" +#include "gevents.h" +#include "gtimer.h" +#include "gtypes.h" +#include "hashmap.h" +#include "queue.h" +#include "platform.h" +#include "stack.h" +#include "strlib.h" +#include "tokenscanner.h" +#include "vector.h" + +using namespace std; + +static string getLineConsole(); +static void putConsole(const string & str); +static void endLineConsole(); +static void echoConsole(const string & str); +static void fileLogConsole(const string & str); +static int scanInt(TokenScanner & scanner); +static double scanDouble(TokenScanner & scanner); +static GDimension scanDimension(const string & str); +static GRectangle scanRectangle(const string & str); + +class ConsoleStreambuf : public streambuf { + +private: + +/* Constants */ + + static const int BUFFER_SIZE = 1024; + +/* Instance variables */ + + char inBuffer[BUFFER_SIZE]; + char outBuffer[BUFFER_SIZE]; + +public: + + ConsoleStreambuf() { + setg(inBuffer, inBuffer, inBuffer); + setp(outBuffer, outBuffer + BUFFER_SIZE); + } + + ~ConsoleStreambuf() { + /* Empty */ + } + + virtual int underflow() { + // Allow long strings at some point + string line = getLineConsole(); + int n = line.length(); + if (n + 1 >= BUFFER_SIZE) error("String too long"); + for (int i = 0; i < n; i++) { + inBuffer[i] = line[i]; + } + inBuffer[n++] = '\n'; + inBuffer[n] = '\0'; + setg(inBuffer, inBuffer, inBuffer + n); + return inBuffer[0]; + } + + virtual int overflow(int ch = EOF) { + string line = ""; + for (char *cp = pbase(); cp < pptr(); cp++) { + if (*cp == '\n') { + putConsole(line); + endLineConsole(); + line = ""; + } else { + line += *cp; + } + } + if (line != "") { + putConsole(line); + } + setp(outBuffer, outBuffer + BUFFER_SIZE); + if (ch != EOF) { + outBuffer[0] = ch; + pbump(1); + } + return ch != EOF; + } + + virtual int sync() { + return overflow(); + } +}; + +/* Private data */ + +static Queue<GEvent> eventQueue; +static HashMap<string,GTimerData *> timerTable; +static HashMap<string,GWindowData *> windowTable; +static HashMap<string,GObject *> sourceTable; +static HashMap<string,string> optionTable; +static string programName; +static ofstream logfile; +static streambuf* cin_old_buf; +static streambuf* cout_old_buf; +static ConsoleStreambuf* cinout_new_buf; + + +#ifdef _WIN32 +static HANDLE rdFromJBE = NULL; +static HANDLE wrFromJBE = NULL; +static HANDLE rdToJBE = NULL; +static HANDLE wrToJBE = NULL; +#endif + +/* Prototypes */ + +static void initPipe(); +static void putPipe(string line); +static string getPipe(); +static string getResult(); +static void getStatus(); +static GEvent parseEvent(string line); +static GEvent parseMouseEvent(TokenScanner & scanner, EventType type); +static GEvent parseKeyEvent(TokenScanner & scanner, EventType type); +static GEvent parseTimerEvent(TokenScanner & scanner, EventType type); +static GEvent parseWindowEvent(TokenScanner & scanner, EventType type); +static GEvent parseActionEvent(TokenScanner & scanner, EventType type); + +/* Implementation of the Platform class */ + +Platform::Platform() { + /* Empty */ +} + +Platform::~Platform() { + /* Empty */ +} + +/* Unix implementations of filelib.h primitives */ + +#ifndef _WIN32 + +bool Platform::fileExists(string filename) { + struct stat fileInfo; + return stat(filename.c_str(), &fileInfo) == 0; +} + +bool Platform::isFile(string filename) { + struct stat fileInfo; + if (stat(filename.c_str(), &fileInfo) != 0) return false; + return S_ISREG(fileInfo.st_mode) != 0; +} + +bool Platform::isSymbolicLink(string filename) { + struct stat fileInfo; + if (stat(filename.c_str(), &fileInfo) != 0) return false; + return S_ISLNK(fileInfo.st_mode) != 0; +} + +bool Platform::isDirectory(string filename) { + struct stat fileInfo; + if (stat(filename.c_str(), &fileInfo) != 0) return false; + return S_ISDIR(fileInfo.st_mode) != 0; +} + +void Platform::setCurrentDirectory(string path) { + if (chdir(path.c_str()) == 0) { + string msg = "setCurrentDirectory: "; + string err = string(strerror(errno)); + error(msg + err); + } +} + +string Platform::getCurrentDirectory() { + char *cwd = getcwd(NULL, 0); + if (cwd == NULL) { + string msg = "getCurrentDirectory: "; + string err = string(strerror(errno)); + error(msg + err); + } + string result = string(cwd); + free(cwd); + return result; +} + +void Platform::createDirectory(string path) { + if (endsWith(path, "/")) { + path = path.substr(0, path.length() - 2); + } + if (mkdir(path.c_str(), 0777) != 0) { + if (errno == EEXIST && isDirectory(path)) return; + string msg = "createDirectory: "; + string err = string(strerror(errno)); + error(msg + err); + } +} + +string Platform::getDirectoryPathSeparator() { + return "/"; +} + +string Platform::getSearchPathSeparator() { + return ":"; +} + +string Platform::expandPathname(string filename) { + if (filename == "") return ""; + int len = filename.length(); + if (filename[0] == '~') { + int spos = 1; + while (spos < len && filename[spos] != '\\' && filename[spos] != '/') { + spos++; + } + char *homedir = NULL; + if (spos == 1) { + homedir = getenv("HOME"); + if (homedir == NULL) homedir = getpwuid(getuid())->pw_dir; + } else { + struct passwd *pw = getpwnam(filename.substr(1, spos - 1).c_str()); + if (pw == NULL) error("expandPathname: No such user"); + homedir = pw->pw_dir; + } + filename = string(homedir) + filename.substr(spos); + len = filename.length(); + } + for (int i = 0; i < len; i++) { + if (filename[i] == '\\') filename[i] = '/'; + } + return filename; +} + +void Platform::listDirectory(string path, vector<string> & list) { + if (path == "") path = "."; + DIR *dir = opendir(path.c_str()); + if (dir == NULL) error(string("listDirectory: Can't open ") + path); + list.clear(); + while (true) { + struct dirent *ep = readdir(dir); + if (ep == NULL) break; + string name = string(ep->d_name); + if (name != "." && name != "..") list.push_back(name); + } + closedir(dir); + sort(list.begin(), list.end()); +} + +#else + +bool Platform::fileExists(string filename) { + return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES; +} + +bool Platform::isFile(string filename) { + DWORD attr = GetFileAttributesA(filename.c_str()); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_NORMAL); +} + +bool Platform::isSymbolicLink(string filename) { + DWORD attr = GetFileAttributesA(filename.c_str()); + return attr != INVALID_FILE_ATTRIBUTES + && (attr & FILE_ATTRIBUTE_REPARSE_POINT); +} + +bool Platform::isDirectory(string filename) { + DWORD attr = GetFileAttributesA(filename.c_str()); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY); +} + +void Platform::setCurrentDirectory(string path) { + if (!isDirectory(path) || !SetCurrentDirectoryA(path.c_str())) { + error("setCurrentDirectory: Can't change to " + path); + } +} + +string Platform::getCurrentDirectory() { + char path[MAX_PATH + 1]; + int n = GetCurrentDirectoryA(MAX_PATH + 1, path); + return string(path, n); +} + +void Platform::createDirectory(string path) { + if (!CreateDirectoryA(path.c_str(), NULL)) { + error("createDirectory: Can't create " + path); + } +} + +string Platform::getDirectoryPathSeparator() { + return "\\"; +} + +string Platform::getSearchPathSeparator() { + return ";"; +} + +string Platform::expandPathname(string filename) { + if (filename == "") return ""; + int len = filename.length(); + for (int i = 0; i < len; i++) { + if (filename[i] == '/') filename[i] = '\\'; + } + return filename; +} + +void Platform::listDirectory(string path, vector<string> & list) { + if (path == "") path = "."; + string pattern = path + "\\*.*"; + WIN32_FIND_DATAA fd; + HANDLE h = FindFirstFileA(pattern.c_str(), &fd); + if (h == INVALID_HANDLE_VALUE) { + error("listDirectory: Can't list directory"); + } + list.clear(); + while (true) { + string name = string(fd.cFileName); + if (name != "." && name != "..") list.push_back(name); + if (!FindNextFileA(h, &fd)) break; + } + FindClose(h); + sort(list.begin(), list.end()); +} + +#endif + +string Platform::openFileDialog(string title, string mode, string path) { + ostringstream os; + os << "File.openFileDialog("; + writeQuotedString(os, title); + os << ", \"" << mode << "\", \"" << path << "\")"; + putPipe(os.str()); + return getResult(); +} + +void Platform::createGWindow(const GWindow & gw, double width, double height, + GObject *topCompound) { + ostringstream os; + os << gw.gwd; + string id = os.str(); + windowTable.put(id, gw.gwd); + os.str(""); + os << "GWindow.create(\"" << id << "\", " << width << ", " << height + << ", \"" << topCompound << "\")"; + putPipe(os.str()); + getStatus(); +} + +void Platform::deleteGWindow(const GWindow & gw) { + ostringstream os; + os << gw.gwd; + string id = os.str(); + windowTable.remove(id); + os.str(""); + os << "GWindow.delete(\"" << gw.gwd << "\")"; + putPipe(os.str()); +} + +void Platform::close(const GWindow & gw) { + ostringstream os; + os << "GWindow.close(\"" << gw.gwd << "\")"; + putPipe(os.str()); +} + +void Platform::requestFocus(const GWindow & gw) { + ostringstream os; + os << "GWindow.requestFocus(\"" << gw.gwd << "\")"; + putPipe(os.str()); +} + +void Platform::clear(const GWindow & gw) { + ostringstream os; + os << "GWindow.clear(\"" << gw.gwd << "\")"; + putPipe(os.str()); +} + +void Platform::repaint(const GWindow & gw) { + ostringstream os; + os << "GWindow.repaint(\"" << gw.gwd << "\")"; + putPipe(os.str()); +} + +void Platform::setVisible(const GWindow&, bool) { +// ostringstream os; +// os << boolalpha << "GWindow.setVisible(\"" << gw.gwd << "\", " +// << flag << ")"; +// putPipe(os.str()); +} + +void Platform::setWindowTitle(const GWindow & gw, string title) { + ostringstream os; + os << "GWindow.setTitle(\"" << gw.gwd << "\", "; + writeQuotedString(os, title); + os << ")"; + putPipe(os.str()); +} + +double Platform::getScreenWidth() { + putPipe("GWindow.getScreenWidth()"); + return stringToReal(getResult()); +} + +double Platform::getScreenHeight() { + putPipe("GWindow.getScreenHeight()"); + return stringToReal(getResult()); +} + +void Platform::pause(double milliseconds) { + ostringstream os; + os << "GTimer.pause(" << milliseconds << ")"; + putPipe(os.str()); + getStatus(); +} + +void Platform::createTimer(const GTimer & timer, double delay) { + ostringstream os; + os << timer.gtd; + string id = os.str(); + timerTable.put(id, timer.gtd); + os.str(""); + os << "GTimer.create(\"" << id << "\", " << delay << ")"; + putPipe(os.str()); +} + +void Platform::deleteTimer(const GTimer & timer) { + ostringstream os; + os << timer.gtd; + string id = os.str(); + timerTable.remove(id); + os.str(""); + os << "GTimer.deleteTimer(\"" << id << "\")"; + putPipe(os.str()); +} + +void Platform::startTimer(const GTimer & timer) { + ostringstream os; + os << "GTimer.startTimer(\"" << timer.gtd << "\")"; + putPipe(os.str()); +} + +void Platform::stopTimer(const GTimer & timer) { + ostringstream os; + os << "GTimer.stopTimer(\"" << timer.gtd << "\")"; + putPipe(os.str()); +} + +void Platform::createSound(Sound *sound, string filename) { + ostringstream os; + os << "Sound.create(\"" << sound << "\", "; + writeQuotedString(os, filename); + os << ")"; + putPipe(os.str()); + getStatus(); +} + +void Platform::deleteSound(Sound *sound) { + ostringstream os; + os << "Sound.delete(\"" << sound << "\")"; + putPipe(os.str()); +} + +void Platform::playSound(Sound *sound) { + ostringstream os; + os << "Sound.play(\"" << sound << "\")"; + putPipe(os.str()); +} + +void Platform::deleteGObject(GObject *gobj) { + ostringstream os; + os << "GObject.delete(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::add(GObject *compound, GObject *gobj) { + ostringstream os; + os << "GCompound.add(\"" << compound << "\", \"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::remove(GObject *gobj) { + ostringstream os; + os << "GObject.remove(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::setRegionAlignment(const GWindow & gw, string region, + string align) { + ostringstream os; + os << "GWindow.setRegionAlignment(\"" << gw.gwd << "\", \"" << region + << "\", \"" << align << "\")"; + putPipe(os.str()); +} + +void Platform::addToRegion(const GWindow & gw, GObject *gobj, string region) { + ostringstream os; + os << "GWindow.addToRegion(\"" << gw.gwd << "\", \"" << gobj << "\", \"" + << region << "\")"; + putPipe(os.str()); +} + +void Platform::removeFromRegion(const GWindow & gw, GObject *gobj, + string region) { + ostringstream os; + os << "GWindow.removeFromRegion(\"" << gw.gwd << "\", \"" + << gobj << "\", \"" << region << "\")"; + putPipe(os.str()); +} + +void Platform::sendForward(GObject *gobj) { + ostringstream os; + os << "GObject.sendForward(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::sendToFront(GObject *gobj) { + ostringstream os; + os << "GObject.sendToFront(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::sendBackward(GObject *gobj) { + ostringstream os; + os << "GObject.sendBackward(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::sendToBack(GObject *gobj) { + ostringstream os; + os << "GObject.sendToBack(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::setVisible(GObject *gobj, bool flag) { + ostringstream os; + os << boolalpha << "GObject.setVisible(\"" << gobj << "\", " << flag << ")"; + putPipe(os.str()); +} + +void Platform::setColor(GObject *gobj, string color) { + ostringstream os; + os << "GObject.setColor(\"" << gobj << "\", \"" << color << "\")"; + putPipe(os.str()); +} + +void Platform::scale(GObject *gobj, double sx, double sy) { + ostringstream os; + os << "GObject.scale(\"" << gobj << "\", " << sx << ", " << sy << ")"; + putPipe(os.str()); +} + +void Platform::rotate(GObject *gobj, double theta) { + ostringstream os; + os << "GObject.rotate(\"" << gobj << "\", " << theta << ")"; + putPipe(os.str()); +} + +// Move this computation into gobjects.cpp + +bool Platform::contains(const GObject *gobj, double x, double y) { + ostringstream os; + os << "GObject.contains(\"" << gobj << "\", " << x << ", " << y << ")"; + putPipe(os.str()); + return getResult() == "true"; +} + +// Move this computation into gobjects.cpp + +GRectangle Platform::getBounds(const GObject *gobj) { + ostringstream os; + os << "GObject.getBounds(\"" << gobj << "\")"; + putPipe(os.str()); + string result = getResult(); + if (!startsWith(result, "GRectangle(")) error(result); + return scanRectangle(result); +} + +void Platform::setLineWidth(GObject *gobj, double lineWidth) { + ostringstream os; + os << "GObject.setLineWidth(\"" << gobj << "\", " << lineWidth << ")"; + putPipe(os.str()); +} + +void Platform::setLocation(GObject *gobj, double x, double y) { + ostringstream os; + os << "GObject.setLocation(\"" << gobj << "\", " << x << ", " << y << ")"; + putPipe(os.str()); +} + +void Platform::setSize(GObject *gobj, double width, double height) { + ostringstream os; + os << "GObject.setSize(\"" << gobj << "\", " << width << ", " + << height << ")"; + putPipe(os.str()); +} + +void Platform::setFrameRectangle(GObject *gobj, double x, double y, + double width, double height) { + ostringstream os; + os << "GArc.setFrameRectangle(\"" << gobj << "\", " + << x << ", " << y << ", " + << width << ", " << height << ")"; + putPipe(os.str()); +} + +void Platform::draw(const GWindow & gw, const GObject *gobj) { + ostringstream os; + os << "GWindow.draw(\"" << gw.gwd << "\", \"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::setFilled(GObject *gobj, bool flag) { + ostringstream os; + os << boolalpha << "GObject.setFilled(\"" << gobj << "\", " << flag << ")"; + putPipe(os.str()); +} + +void Platform::setFillColor(GObject *gobj, string color) { + ostringstream os; + os << "GObject.setFillColor(\"" << gobj << "\", \"" << color << "\")"; + putPipe(os.str()); +} + +void Platform::createGRect(GObject *gobj, double width, double height) { + ostringstream os; + os << "GRect.create(\"" << gobj << "\", " << width << ", " + << height << ")"; + putPipe(os.str()); +} + +void Platform::createGRoundRect(GObject *gobj, double width, double height, + double corner) { + ostringstream os; + os << "GRoundRect.create(\"" << gobj << "\", " << width << ", " << height + << ", " << corner << ")"; + putPipe(os.str()); +} + +void Platform::createG3DRect(GObject *gobj, double width, double height, + bool raised) { + ostringstream os; + os << boolalpha << "G3DRect.create(\"" << gobj << "\", " + << width << ", " << height << ", " << raised << ")"; + putPipe(os.str()); +} + +void Platform::setRaised(GObject *gobj, bool raised) { + ostringstream os; + os << boolalpha << "G3DRect.setRaised(\"" << gobj << "\", " + << raised << ")"; + putPipe(os.str()); +} + +void Platform::createGLabel(GObject *gobj, string label) { + ostringstream os; + // *** BUGBUG: must escape quotation marks in label string (Marty) + os << "GLabel.create(\"" << gobj << "\", "; + writeQuotedString(os, label); + os << ")"; + putPipe(os.str()); +} + +void Platform::createGLine(GObject *gobj, double x1, double y1, + double x2, double y2) { + ostringstream os; + os << "GLine.create(\"" << gobj << "\", " << x1 << ", " << y1 + << ", " << x2 << ", " << y2 << ")"; + putPipe(os.str()); +} + +void Platform::setStartPoint(GObject *gobj, double x, double y) { + ostringstream os; + os << "GLine.setStartPoint(\"" << gobj << "\", " << x << ", " << y << ")"; + putPipe(os.str()); +} + +void Platform::setEndPoint(GObject *gobj, double x, double y) { + ostringstream os; + os << "GLine.setEndPoint(\"" << gobj << "\", " << x << ", " << y << ")"; + putPipe(os.str()); +} + +void Platform::createGArc(GObject *gobj, double width, double height, + double start, double sweep) { + ostringstream os; + os << "GArc.create(\"" << gobj << "\", " << width << ", " << height + << ", " << start << ", " << sweep << ")"; + putPipe(os.str()); +} + +void Platform::setStartAngle(GObject *gobj, double angle) { + ostringstream os; + os << "GArc.setStartAngle(\"" << gobj << "\", " << angle << ")"; + putPipe(os.str()); +} + +void Platform::setSweepAngle(GObject *gobj, double angle) { + ostringstream os; + os << "GArc.setSweepAngle(\"" << gobj << "\", " << angle << ")"; + putPipe(os.str()); +} + +GDimension Platform::createGImage(GObject *gobj, string filename) { + ostringstream os; + os << "GImage.create(\"" << gobj << "\", \"" << filename << "\")"; + putPipe(os.str()); + string result = getResult(); + if (!startsWith(result, "GDimension(")) error(result); + return scanDimension(result); +} + +void Platform::createGPolygon(GObject *gobj) { + ostringstream os; + os << "GPolygon.create(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::addVertex(GObject *gobj, double x, double y) { + ostringstream os; + os << "GPolygon.addVertex(\"" << gobj << "\", " << x << ", " << y << ")"; + putPipe(os.str()); +} + +void Platform::createGOval(GObject *gobj, double width, double height) { + ostringstream os; + os << "GOval.create(\"" << gobj << "\", " << width << ", " + << height << ")"; + putPipe(os.str()); +} + +void Platform::setActionCommand(GObject *gobj, string cmd) { + ostringstream os; + os << "GInteractor.setActionCommand(\"" << gobj << "\", "; + writeQuotedString(os, cmd); + os << ")"; + putPipe(os.str()); +} + +GDimension Platform::getSize(GObject *gobj) { + ostringstream os; + os << "GInteractor.getSize(\"" << gobj << "\")"; + putPipe(os.str()); + return scanDimension(getResult()); +} + +void Platform::createGButton(GObject *gobj, string label) { + ostringstream os; + os << gobj; + sourceTable.put(os.str(), gobj); + os.str(""); + os << "GButton.create(\"" << gobj << "\", "; + writeQuotedString(os, label); + os << ")"; + putPipe(os.str()); +} + +void Platform::createGCheckBox(GObject *gobj, string label) { + ostringstream os; + os << gobj; + sourceTable.put(os.str(), gobj); + os.str(""); + os << "GCheckBox.create(\"" << gobj << "\", "; + writeQuotedString(os, label); + os << ")"; + putPipe(os.str()); +} + +bool Platform::isSelected(GObject *gobj) { + ostringstream os; + os << "GCheckBox.isSelected(\"" << gobj << "\")"; + putPipe(os.str()); + return getResult() == "true"; +} + +void Platform::setSelected(GObject *gobj, bool state) { + ostringstream os; + os << boolalpha << "GCheckBox.setSelected(\"" << gobj << "\", " + << state << ")"; + putPipe(os.str()); +} + +void Platform::createGSlider(GObject *gobj, int min, int max, int value) { + ostringstream os; + os << gobj; + sourceTable.put(os.str(), gobj); + os.str(""); + os << "GSlider.create(\"" << gobj << "\", " << min << ", " << max + << ", " << value << ")"; + putPipe(os.str()); +} + +int Platform::getValue(GObject *gobj) { + ostringstream os; + os << "GSlider.getValue(\"" << gobj << "\")"; + putPipe(os.str()); + return stringToInteger(getResult()); +} + +void Platform::setValue(GObject *gobj, int value) { + ostringstream os; + os << "GSlider.setValue(\"" << gobj << "\", " << value << ")"; + putPipe(os.str()); +} + +void Platform::createGTextField(GObject *gobj, int nChars) { + ostringstream os; + os << gobj; + sourceTable.put(os.str(), gobj); + os.str(""); + os << "GTextField.create(\"" << gobj << "\", " << nChars << ")"; + putPipe(os.str()); +} + +string Platform::getText(GObject *gobj) { + ostringstream os; + os << "GTextField.getText(\"" << gobj << "\")"; + putPipe(os.str()); + return getResult(); +} + +void Platform::setText(GObject *gobj, string str) { + ostringstream os; + os << "GTextField.setText(\"" << gobj << "\", "; + writeQuotedString(os, str); + os << ")"; + putPipe(os.str()); +} + +void Platform::createGChooser(GObject *gobj) { + ostringstream os; + os << gobj; + sourceTable.put(os.str(), gobj); + os.str(""); + os << "GChooser.create(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::addItem(GObject *gobj, string item) { + ostringstream os; + os << "GChooser.addItem(\"" << gobj << "\", "; + writeQuotedString(os, item); + os << ")"; + putPipe(os.str()); +} + +string Platform::getSelectedItem(GObject *gobj) { + ostringstream os; + os << "GChooser.getSelectedItem(\"" << gobj << "\")"; + putPipe(os.str()); + return getResult(); +} + +void Platform::setSelectedItem(GObject *gobj, string item) { + ostringstream os; + os << "GChooser.setSelectedItem(\"" << gobj << "\", "; + writeQuotedString(os, item); + os << ")"; + putPipe(os.str()); +} + +void Platform::createGCompound(GObject *gobj) { + ostringstream os; + os << "GCompound.create(\"" << gobj << "\")"; + putPipe(os.str()); +} + +void Platform::setFont(GObject *gobj, string font) { + ostringstream os; + os << "GLabel.setFont(\"" << gobj << "\", \"" << font << "\")"; + putPipe(os.str()); +} + +void Platform::setLabel(GObject *gobj, string str) { + ostringstream os; + os << "GLabel.setLabel(\"" << gobj << "\", "; + writeQuotedString(os, str); + os << ")"; + putPipe(os.str()); +} + +double Platform::getFontAscent(const GObject *gobj) { + ostringstream os; + os << "GLabel.getFontAscent(\"" << gobj << "\")"; + putPipe(os.str()); + return stringToReal(getResult()); +} + +double Platform::getFontDescent(const GObject *gobj) { + ostringstream os; + os << "GLabel.getFontDescent(\"" << gobj << "\")"; + putPipe(os.str()); + return stringToReal(getResult()); +} + +GDimension Platform::getGLabelSize(const GObject *gobj) { + ostringstream os; + os << "GLabel.getGLabelSize(\"" << gobj << "\")"; + putPipe(os.str()); + return scanDimension(getResult()); +} + +GEvent Platform::getNextEvent(int mask) { + if (eventQueue.isEmpty()) { + putPipe("GEvent.getNextEvent(" + integerToString(mask) + ")"); + getResult(); + if (eventQueue.isEmpty()) return GEvent(); + } + return eventQueue.dequeue(); +} + +GEvent Platform::waitForEvent(int mask) { + while (eventQueue.isEmpty()) { + putPipe("GEvent.waitForEvent(" + integerToString(mask) + ")"); + getResult(); + } + return eventQueue.dequeue(); +} + +void Platform::exitGraphics() { + putPipe("GWindow.exitGraphics()"); + exit(0); +} + +Platform *getPlatform() { + static Platform gp; + return &gp; +} + +#ifdef _WIN32 + +/* Windows implementation of interface to Java back end */ + +int startupMain(int argc, char **argv) { + extern int Main(int argc, char **argv); + programName = getRoot(getTail(argv[0])); + initPipe(); +#ifndef PLAIN_TEXT_CONSOLE + cin_old_buf = cin.rdbuf(); + cout_old_buf = cout.rdbuf(); + cinout_new_buf = new ConsoleStreambuf(); + cin.rdbuf(cinout_new_buf); + cout.rdbuf(cinout_new_buf); + ShowWindow(GetConsoleWindow(), SW_HIDE); +#endif + return Main(argc, argv); +} + +static void initPipe() { + SECURITY_ATTRIBUTES attr; + attr.nLength = sizeof(SECURITY_ATTRIBUTES); + attr.bInheritHandle = true; + attr.lpSecurityDescriptor = NULL; + if (!CreatePipe(&rdFromJBE, &wrFromJBE, &attr, 0)) { + error("Can't create fromJBE"); + } + if (!SetHandleInformation(rdFromJBE, HANDLE_FLAG_INHERIT, 0)) { + error("SetHandleInformation failed for fromJBE"); + } + if (!CreatePipe(&rdToJBE, &wrToJBE, &attr, 0)) { + error("Can't create toJBE"); + } + if (!SetHandleInformation(wrToJBE, HANDLE_FLAG_INHERIT, 0)) { + error("SetHandleInformation failed for toJBE"); + } + string cmd = "java -jar spl.jar " + programName; + int n = cmd.length(); + char *cmdLine = new char[n + 1]; + for (int i = 0; i < n; i++) { + cmdLine[i] = cmd[i]; + } + cmdLine[n] = '\0'; + PROCESS_INFORMATION pInfo; + memset(&pInfo, 0, sizeof(PROCESS_INFORMATION)); + STARTUPINFOA sInfo; + memset(&sInfo, 0, sizeof(STARTUPINFOA)); + sInfo.cb = sizeof(STARTUPINFOA); + sInfo.dwFlags = STARTF_USESTDHANDLES; + sInfo.hStdInput = rdToJBE; + sInfo.hStdOutput = wrFromJBE; + sInfo.hStdError = wrFromJBE; + int ok = CreateProcessA(NULL, cmdLine, NULL, NULL, true, CREATE_NO_WINDOW, + NULL, NULL, &sInfo, &pInfo); + if (!ok) { + DWORD err = GetLastError(); + cerr << endl; + cerr << "ERROR: Stanford C++ library was unable to connect" << endl; + cerr << " to its Java back-end to launch 'spl.jar'." << endl; + cerr << " Please check your Java installation and make sure" << endl; + cerr << " that spl.jar is properly attached to your project." << endl; + cerr << endl; + cerr << err << endl; + } else { + CloseHandle(pInfo.hProcess); + CloseHandle(pInfo.hThread); + } +} + +static void putPipe(string line) { + DWORD nch; + WriteFile(wrToJBE, line.c_str(), line.length(), &nch, NULL); + WriteFile(wrToJBE, "\n", 1, &nch, NULL); + FlushFileBuffers(wrToJBE); +} + +static string getPipe() { + string line = ""; + DWORD nch; + while (true) { + char ch; + ReadFile(rdFromJBE, &ch, 1, &nch, NULL); + if (ch == '\n' || ch == '\r') break; + line += ch; + } + + // cerr << "getPipe(): \"" << line << "\"" << endl; + return line; +} + +#else + +/* Linux/Mac implementation of interface to Java back end */ + +static void scanOptions() { + char *home = getenv("HOME"); + if (home != NULL) { + string filename = string() + home + "/.spl"; + ifstream infile(filename.c_str()); + if (!infile.fail()) { + string line; + while (getline(infile, line)) { + size_t equals = line.find('='); + if (equals != string::npos) { + string key = line.substr(0, equals); + string value = line.substr(equals + 1); + optionTable.put(key, value); + } + } + infile.close(); + } + } +} + +static string getOption(string key) { + char *str = getenv(key.c_str()); + if (str != NULL) return string(str); + return optionTable.get(key); +} + +int startupMain(int argc, char **argv) { + extern int Main(int argc, char **argv); + string arg0 = argv[0]; + programName = getRoot(getTail(arg0)); + size_t ax = arg0.find(".app/Contents/"); + if (ax != string::npos) { + while (ax > 0 && arg0[ax] != '/') { + ax--; + } + if (ax > 0) { + string cwd = arg0.substr(0, ax); + chdir(cwd.c_str()); + } + } + char *noConsoleFlag = getenv("NOCONSOLE"); + if (noConsoleFlag != NULL && startsWith(string(noConsoleFlag), "t")) { + return Main(argc, argv); + } + scanOptions(); + initPipe(); +#ifndef PLAIN_TEXT_CONSOLE + cin_old_buf = cin.rdbuf(); + cout_old_buf = cout.rdbuf(); + cinout_new_buf = new ConsoleStreambuf(); + cin.rdbuf(cinout_new_buf); + cout.rdbuf(cinout_new_buf); + string font = getOption("CPPFONT"); + if (font != "") setConsoleFont(font); +#endif + return Main(argc, argv); +} + +static void initPipe() { + char *trace = getenv("JBETRACE"); + logfile.open("/dev/tty"); + tracePipe = trace != NULL && startsWith(toLowerCase(trace), "t"); + int toJBE[2], fromJBE[2]; + pipe(toJBE); + pipe(fromJBE); + int child = fork(); + if (child == 0) { + dup2(toJBE[0], 0); + close(toJBE[0]); + close(toJBE[1]); + dup2(fromJBE[1], 1); + close(fromJBE[0]); + close(fromJBE[1]); +#ifdef __APPLE__ + string option = "-Xdock:name=" + programName; + execlp("java", "java", option.c_str(), "-jar", "spl.jar", + programName.c_str(), NULL); +#else + execlp("java", "java", "-jar", "spl.jar", programName.c_str(), NULL); +#endif + cerr << endl; + cerr << "ERROR: Stanford C++ library was unable to connect" << endl; + cerr << " to its Java back-end to launch 'spl.jar'." << endl; + cerr << " Please check your Java installation and make sure" << endl; + cerr << " that spl.jar is properly attached to your project." << endl; + cerr << endl; + throw new ErrorException("Could not exec spl.jar"); + } else { + pin = fromJBE[0]; + pout = toJBE[1]; + close(fromJBE[1]); + close(toJBE[0]); + } +} + +static void putPipe(string line) { + write(pout, line.c_str(), line.length()); + write(pout, "\n", 1); + if (tracePipe) logfile << "-> " << line << endl; +} + +static string getPipe() { + string line = ""; + while (true) { + char ch; + read(pin, &ch, 1); + if (ch == '\n') break; + line += ch; + } + if (tracePipe) logfile << "<- " << line << endl; + return line; +} + +#endif + +static string getResult() { + while (true) { + string line = getPipe(); + bool result = startsWith(line, "result:"); + bool event = startsWith(line, "event:"); + bool hasACMException = line.find("acm.util.ErrorException") != string::npos; + bool hasException = line.find("xception") != string::npos; + + // *** added by Marty: if there is a back-end error, display it + if (((result || event) && hasACMException) || (!result && !event && hasException)) { + cerr << "ERROR emitted from Stanford Java back-end process:" + << endl << line << endl; + throw new ErrorException(line.substr(result ? 7 : 6)); + } else if (startsWith(line, "result:")) { + return line.substr(7); + } else if (startsWith(line, "event:")) { + eventQueue.enqueue(parseEvent(line.substr(6))); + } else { + if (line.find("\tat") == 0) { + // part of a Java exception stack trace, so echo it + cerr << line << endl; + } + } + } +} + +static void getStatus() { + string result = getResult(); + if (result != "ok") error(result); +} + +static GEvent parseEvent(string line) { + TokenScanner scanner(line); + scanner.ignoreWhitespace(); + scanner.scanNumbers(); + scanner.scanStrings(); + string name = scanner.nextToken(); + if (name == "mousePressed") { + return parseMouseEvent(scanner, MOUSE_PRESSED); + } else if (name == "mouseReleased") { + return parseMouseEvent(scanner, MOUSE_RELEASED); + } else if (name == "mouseClicked") { + return parseMouseEvent(scanner, MOUSE_CLICKED); + } else if (name == "mouseMoved") { + return parseMouseEvent(scanner, MOUSE_MOVED); + } else if (name == "mouseDragged") { + return parseMouseEvent(scanner, MOUSE_DRAGGED); + } else if (name == "keyPressed") { + return parseKeyEvent(scanner, KEY_PRESSED); + } else if (name == "keyReleased") { + return parseKeyEvent(scanner, KEY_RELEASED); + } else if (name == "keyTyped") { + return parseKeyEvent(scanner, KEY_TYPED); + } else if (name == "actionPerformed") { + return parseActionEvent(scanner, ACTION_PERFORMED); + } else if (name == "timerTicked") { + return parseTimerEvent(scanner, TIMER_TICKED); + } else if (name == "windowClosed") { + GWindowEvent e = parseWindowEvent(scanner, WINDOW_CLOSED); + e.getGWindow().setVisible(false); + e.getGWindow().close(); + return e; + } else if (name == "windowResized") { + return parseWindowEvent(scanner, WINDOW_RESIZED); + } else if (name == "lastWindowClosed") { + exit(0); + } else if (name == "lastWindowGWindow_closed") { + exit(0); + } else { + /* Ignore for now */ + } + return GEvent(); +} + +static GEvent parseMouseEvent(TokenScanner & scanner, EventType type) { + scanner.verifyToken("("); + string id = scanner.getStringValue(scanner.nextToken()); + scanner.verifyToken(","); + double time = scanDouble(scanner); + scanner.verifyToken(","); + int modifiers = scanInt(scanner); + scanner.verifyToken(","); + double x = scanDouble(scanner); + scanner.verifyToken(","); + double y = scanDouble(scanner); + scanner.verifyToken(")"); + GMouseEvent e(type, GWindow(windowTable.get(id)), x, y); + e.setEventTime(time); + e.setModifiers(modifiers); + return e; +} + +static GEvent parseKeyEvent(TokenScanner & scanner, EventType type) { + scanner.verifyToken("("); + string id = scanner.getStringValue(scanner.nextToken()); + scanner.verifyToken(","); + double time = scanDouble(scanner); + scanner.verifyToken(","); + int modifiers = scanInt(scanner); + scanner.verifyToken(","); + int keyChar = scanInt(scanner); + scanner.verifyToken(","); + int keyCode = scanInt(scanner); + scanner.verifyToken(")"); + GKeyEvent e(type, GWindow(windowTable.get(id)), char(keyChar), keyCode); + e.setEventTime(time); + e.setModifiers(modifiers); + return e; +} + +static GEvent parseTimerEvent(TokenScanner & scanner, EventType type) { + scanner.verifyToken("("); + string id = scanner.getStringValue(scanner.nextToken()); + scanner.verifyToken(","); + double time = scanDouble(scanner); + scanner.verifyToken(")"); + GTimerEvent e(type, GTimer(timerTable.get(id))); + e.setEventTime(time); + return e; +} + +static GEvent parseWindowEvent(TokenScanner & scanner, EventType type) { + scanner.verifyToken("("); + string id = scanner.getStringValue(scanner.nextToken()); + scanner.verifyToken(","); + double time = scanDouble(scanner); + scanner.verifyToken(")"); + GWindowEvent e(type, GWindow(windowTable.get(id))); + e.setEventTime(time); + return e; +} + +static GEvent parseActionEvent(TokenScanner & scanner, EventType type) { + scanner.verifyToken("("); + string id = scanner.getStringValue(scanner.nextToken()); + scanner.verifyToken(","); + string action = scanner.getStringValue(scanner.nextToken()); + scanner.verifyToken(","); + double time = scanDouble(scanner); + scanner.verifyToken(")"); + GActionEvent e(type, sourceTable.get(id), action); + e.setEventTime(time); + return e; +} + +/* Console code */ + +void Platform::clearConsole() { + putPipe("JBEConsole.clear()"); +} + +void Platform::setConsoleFont(const string & font) { + ostringstream os; + os << "JBEConsole.setFont(\"" << font << "\")"; + putPipe(os.str()); +} + +void Platform::setConsoleSize(double width, double height) { + ostringstream os; + os << "JBEConsole.setSize(" << width << ", " << height << ")"; + putPipe(os.str()); +} + +static string getLineConsole() { + putPipe("JBEConsole.getLine()"); + string result = getResult(); + echoConsole(result + "\n"); // wrong for multiple inputs on one line + return result; +} + +static void putConsole(const string & str) { + ostringstream os; + os << "JBEConsole.print("; + writeQuotedString(os, str); + os << ")"; + putPipe(os.str()); + echoConsole(str); +} + +static void echoConsole(const string & str) { + if (getConsoleEcho()) { + // write to the standard cout console for output copy/pasting + cin.rdbuf(cin_old_buf); + cout.rdbuf(cout_old_buf); + cout << str; + cout.flush(); + cin.rdbuf(cinout_new_buf); + cout.rdbuf(cinout_new_buf); + } + fileLogConsole(str); +} + +static void fileLogConsole(const string & str) { + string consoleLogFile = getConsoleLogFile(); + if (consoleLogFile.length() > 0) { + // A bit inefficient; opens/writes/closes the file on each print + // statement. But this enables fine-grained control and changing + // the log file in mid-execution with minimal code base change + ofstream outfile; + outfile.open(consoleLogFile.c_str(), fstream::out | fstream::app); + outfile << str; + outfile.flush(); + outfile.close(); + } +} + +static void endLineConsole() { + putPipe("JBEConsole.println()"); + echoConsole("\n"); +} + +static int scanInt(TokenScanner & scanner) { + string token = scanner.nextToken(); + if (token == "-") token += scanner.nextToken(); + return stringToInteger(token); +} + +static double scanDouble(TokenScanner & scanner) { + string token = scanner.nextToken(); + if (token == "-") token += scanner.nextToken(); + return stringToReal(token); +} + +static GDimension scanDimension(const string & str) { + TokenScanner scanner(str); + scanner.scanNumbers(); + scanner.ignoreWhitespace(); + scanner.verifyToken("GDimension"); + scanner.verifyToken("("); + double width = scanDouble(scanner); + scanner.verifyToken(","); + double height = scanDouble(scanner); + scanner.verifyToken(")"); + return GDimension(width, height); +} + +static GRectangle scanRectangle(const string & str) { + TokenScanner scanner(str); + scanner.scanNumbers(); + scanner.ignoreWhitespace(); + scanner.verifyToken("GRectangle"); + scanner.verifyToken("("); + double x = scanDouble(scanner); + scanner.verifyToken(","); + double y = scanDouble(scanner); + scanner.verifyToken(","); + double width = scanDouble(scanner); + scanner.verifyToken(","); + double height = scanDouble(scanner); + scanner.verifyToken(")"); + return GRectangle(x, y, width, height); +} diff --git a/labb8/lib/StanfordCPPLib/platform.h b/labb8/lib/StanfordCPPLib/platform.h new file mode 100755 index 0000000..85f916f --- /dev/null +++ b/labb8/lib/StanfordCPPLib/platform.h @@ -0,0 +1,134 @@ +/* + * File: platform.h + * ---------------- + * This file defines the <code>Platform</code> class, which encapsulates + * the platform-specific parts of the StanfordCPPLib package. This file is + * logically part of the implementation and is not interesting to clients. + */ + +#ifndef _platform_h +#define _platform_h + +#include <string> +#include <vector> +#include "gevents.h" +#include "gwindow.h" +#include "sound.h" + +class Platform { +private: + Platform(); + friend Platform *getPlatform(); + +public: + virtual ~Platform(); + void clearConsole(); + void setConsoleFont(const std::string & font); + void setConsoleSize(double width, double height); + bool fileExists(std::string filename); + bool isFile(std::string filename); + bool isSymbolicLink(std::string filename); + bool isDirectory(std::string filename); + void setCurrentDirectory(std::string path); + std::string getCurrentDirectory(); + void createDirectory(std::string path); + std::string getDirectoryPathSeparator(); + std::string getSearchPathSeparator(); + std::string expandPathname(std::string filename); + void listDirectory(std::string path, vector<std::string> & list); + std::string openFileDialog(std::string title, std::string mode, + std::string path); + void createGWindow(const GWindow & gw, double width, double height, + GObject *topCompound); + void deleteGWindow(const GWindow & gw); + void close(const GWindow & gw); + void requestFocus(const GWindow & gw); + void clear(const GWindow & gw); + void repaint(const GWindow & gw); + void setVisible(const GWindow & gw, bool flag); + void setResizable(const GWindow & gw, bool flag); + void setWindowTitle(const GWindow & gw, std::string title); + void setRegionAlignment(const GWindow & gw, std::string region, + std::string align); + void addToRegion(const GWindow & gw, GObject *gobj, std::string region); + void removeFromRegion(const GWindow & gw, GObject *gobj, + std::string region); + void pause(double milliseconds); + double getScreenWidth(); + double getScreenHeight(); + GEvent waitForEvent(int mask); + GEvent getNextEvent(int mask); + void exitGraphics(); + void createTimer(const GTimer & timer, double delay); + void deleteTimer(const GTimer & timer); + void startTimer(const GTimer & timer); + void stopTimer(const GTimer & timer); + void createSound(Sound *sound, std::string filename); + void deleteSound(Sound *sound); + void playSound(Sound *sound); + void createGRect(GObject *gobj, double width, double height); + void createGRoundRect(GObject *gobj, double width, double height, + double corner); + void createG3DRect(GObject *gobj, double width, double height, bool raised); + void setRaised(GObject *gobj, bool raised); + void createGOval(GObject *gobj, double width, double height); + void createGArc(GObject *gobj, double width, double height, + double start, double sweep); + void setStartAngle(GObject *gobj, double angle); + void setSweepAngle(GObject *gobj, double angle); + void createGLine(GObject *gobj, double x1, double y1, double x2, double y2); + void setStartPoint(GObject *gobj, double x, double y); + void setEndPoint(GObject *gobj, double x, double y); + void createGLabel(GObject *gobj, std::string label); + GDimension createGImage(GObject *gobj, std::string filename); + void createGPolygon(GObject *gobj); + void addVertex(GObject *gobj, double x, double y); + void setActionCommand(GObject *gobj, std::string cmd); + GDimension getSize(GObject *gobj); + void createGButton(GObject *gobj, std::string label); + void createGCheckBox(GObject *gobj, std::string label); + bool isSelected(GObject *gobj); + void setSelected(GObject *gobj, bool state); + void createGSlider(GObject *gobj, int min, int max, int value); + int getValue(GObject *gobj); + void setValue(GObject *gobj, int value); + void createGTextField(GObject *gobj, int nChars); + std::string getText(GObject *gobj); + void setText(GObject *gobj, std::string str); + void createGChooser(GObject *gobj); + void addItem(GObject *gobj, std::string item); + std::string getSelectedItem(GObject *gobj); + void setSelectedItem(GObject *gobj, std::string item); + void createGCompound(GObject *gobj); + void deleteGObject(GObject *gobj); + void add(GObject *compound, GObject *gobj); + void remove(GObject *gobj); + void sendForward(GObject *gobj); + void sendToFront(GObject *gobj); + void sendBackward(GObject *gobj); + void sendToBack(GObject *gobj); + void setVisible(GObject *gobj, bool flag); + void setColor(GObject *gobj, std::string color); + void scale(GObject *gobj, double sx, double sy); + void rotate(GObject *gobj, double theta); + GRectangle getBounds(const GObject *gobj); + bool contains(const GObject *gobj, double x, double y); + void setLineWidth(GObject *gobj, double lineWidth); + void setLocation(GObject *gobj, double x, double y); + void setSize(GObject *gobj, double width, double height); + void setFrameRectangle(GObject *gobj, double x, double y, + double width, double height); + void draw(const GWindow & gw, const GObject *gobj); + void setFilled(GObject *gobj, bool flag); + void setFillColor(GObject *gobj, std::string color); + void setFont(GObject *gobj, std::string font); + void setLabel(GObject *gobj, std::string str); + double getFontAscent(const GObject *gobj); + double getFontDescent(const GObject *gobj); + GDimension getGLabelSize(const GObject *gobj); + +}; + +Platform *getPlatform(); + +#endif diff --git a/labb8/lib/StanfordCPPLib/point.cpp b/labb8/lib/StanfordCPPLib/point.cpp new file mode 100755 index 0000000..351e4c8 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/point.cpp @@ -0,0 +1,44 @@ +/* + * File: point.cpp + * --------------- + * This file implements the point.h interface. + */ + +#include <string> +#include "point.h" +#include "strlib.h" +using namespace std; + +Point::Point() { + x = 0; + y = 0; +} + +Point::Point(int x, int y) { + this->x = x; + this->y = y; +} + +int Point::getX() const { + return x; +} + +int Point::getY() const { + return y; +} + +string Point::toString() const { + return "(" + integerToString(x) + "," + integerToString(y) + ")"; +} + +bool Point::operator==(const Point & p2) const { + return (x == p2.x) && (y == p2.y); +} + +bool Point::operator!=(const Point & p2) const { + return (x != p2.x) || (y != p2.y); +} + +ostream & operator<<(ostream & os, const Point & pt) { + return os << pt.toString(); +} diff --git a/labb8/lib/StanfordCPPLib/point.h b/labb8/lib/StanfordCPPLib/point.h new file mode 100755 index 0000000..e138117 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/point.h @@ -0,0 +1,113 @@ +/* + * File: point.h + * ------------- + * This file exports a class representing an integer-valued <i>x</i>-<i>y</i> + * pair. + */ + +#ifndef _point_h +#define _point_h + +#include <string> + +/* + * Class: Point + * ------------ + * This class represents an <i>x</i>-<i>y</i> coordinate point on a + * two-dimensional integer grid. If you need to work with real-valued points, + * you should use the <a href="gtypes.html"><code>gtypes.h</code></a> + * interface instead. + */ + +class Point { + +public: + +/* + * Constructor: Point + * Usage: Point origin; + * Point pt(x, y); + * ---------------------- + * Creates a <code>Point</code> object with the specified x and y coordinates. + * If the coordinates are not supplied, the default constructor sets these + * fields to 0. + */ + + Point(); + Point(int x, int y); + +/* + * Method: getX + * Usage: int x = pt.getX(); + * ------------------------- + * Returns the <i>x</i>-coordinate of the point. + */ + + int getX() const; + +/* + * Method: getY + * Usage: int y = pt.getY(); + * ------------------------- + * Returns the <i>y</i>-coordinate of the point. + */ + + int getY() const; + +/* + * Method: toString + * Usage: string str = pt.toString(); + * ---------------------------------- + * Returns a string representation of the <code>Point</code> in the form + * <code>"(x, y)"</code>. + */ + + std::string toString() const; + +/* + * Friend operator: == + * Usage: if (p1 == p2) ... + * ------------------------ + * Returns <code>true</code> if <code>p1</code> and <code>p2</code> + * are the same point. + */ + + bool operator==(const Point & p2) const; + +/* + * Friend operator: != + * Usage: if (p1 != p2) ... + * ------------------------ + * Returns <code>true</code> if <code>p1</code> and <code>p2</code> + * are different. + */ + + bool operator!=(const Point & p2) const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +/* Instance variables */ + + int x; /* The x-coordinate of the point */ + int y; /* The y-coordinate of the point */ + +}; + +/* + * Operator: << + * Usage: cout << pt; + * ------------------ + * Overloads the <code><<</code> operator so that it is able + * to display <code>Point</code> values. + */ + +std::ostream & operator<<(std::ostream & os, const Point & pt); + +#endif diff --git a/labb8/lib/StanfordCPPLib/pqueue.h b/labb8/lib/StanfordCPPLib/pqueue.h new file mode 100755 index 0000000..1946797 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/pqueue.h @@ -0,0 +1,400 @@ +/* + * File: pqueue.h + * -------------- + * This file exports the <code>PriorityQueue</code> class, a + * collection in which values are processed in priority order. + */ + +#ifndef _pqueue_h +#define _pqueue_h + +#include "vector.h" + +/* + * Class: PriorityQueue<ValueType> + * ------------------------------- + * This class models a structure called a <b><i>priority queue</i></b> + * in which values are processed in order of priority. As in conventional + * English usage, lower priority numbers correspond to higher effective + * priorities, so that a priority 1 item takes precedence over a + * priority 2 item. + */ + +template <typename ValueType> +class PriorityQueue { + +public: + +/* + * Constructor: PriorityQueue + * Usage: PriorityQueue<ValueType> pq; + * ----------------------------------- + * Initializes a new priority queue, which is initially empty. + */ + + PriorityQueue(); + +/* + * Destructor: ~PriorityQueue + * -------------------------- + * Frees any heap storage associated with this priority queue. + */ + + virtual ~PriorityQueue(); + +/* + * Method: size + * Usage: int n = pq.size(); + * ------------------------- + * Returns the number of values in the priority queue. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (pq.isEmpty()) ... + * ---------------------------- + * Returns <code>true</code> if the priority queue contains no elements. + */ + + bool isEmpty() const; + +/* + * Method: clear + * Usage: pq.clear(); + * ------------------ + * Removes all elements from the priority queue. + */ + + void clear(); + +/* + * Method: enqueue + * Usage: pq.enqueue(value, priority); + * ----------------------------------- + * Adds <code>value</code> to the queue with the specified priority. + * Lower priority numbers correspond to higher priorities, which + * means that all priority 1 elements are dequeued before any + * priority 2 elements. + */ + + void enqueue(ValueType value, double priority); + +/* + * Method: changePriority + * Usage: pq.changePriority(value, newPriority); + * ----------------------------------- + * Adjusts <code>value</code> in the queue to now have the specified new priority, + * which must be at least as urgent (lower number) than that value's previous + * priority in the queue. + * Throws an error if the element value is not present in the queue, or if the + * new priority passed is not at least as urgent as its current priority. + */ + +void changePriority(ValueType value, double newPriority); + +/* + * Method: dequeue + * Usage: ValueType first = pq.dequeue(); + * -------------------------------------- + * Removes and returns the highest priority value. If multiple + * entries in the queue have the same priority, those values are + * dequeued in the same order in which they were enqueued. + */ + + ValueType dequeue(); + +/* + * Method: peek + * Usage: ValueType first = pq.peek(); + * ----------------------------------- + * Returns the value of highest priority in the queue, without + * removing it. + */ + + ValueType peek() const; + +/* + * Method: peekPriority + * Usage: double priority = pq.peekPriority(); + * ------------------------------------------- + * Returns the priority of the first element in the queue, without + * removing it. + */ + + double peekPriority() const; + +/* + * Method: front + * Usage: ValueType first = pq.front(); + * ------------------------------------ + * Returns the first value in the queue by reference. + */ + + ValueType & front(); + +/* + * Method: back + * Usage: ValueType last = pq.back(); + * ---------------------------------- + * Returns the last value in the queue by reference. + */ + + ValueType & back(); + +/* + * Method: toString + * Usage: string str = pq.toString(); + * ---------------------------------- + * Converts the queue to a printable string representation. + */ + + std::string toString(); + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Implementation notes: PriorityQueue data structure + * -------------------------------------------------- + * The PriorityQueue class is implemented using a data structure called + * a heap. + */ + +private: + +/* Type used for each heap entry */ + + struct HeapEntry { + ValueType value; + double priority; + long sequence; + }; + +/* Instance variables */ + + Vector<HeapEntry> heap; + long enqueueCount; + int backIndex; + int count; + int capacity; + +/* Private function prototypes */ + + void enqueueHeap(ValueType & value, double priority); + ValueType dequeueHeap(); + bool takesPriority(int i1, int i2); + void swapHeapEntries(int i1, int i2); + +}; + +extern void error(std::string msg); + +template <typename ValueType> +PriorityQueue<ValueType>::PriorityQueue() { + clear(); +} + +/* + * Implementation notes: ~PriorityQueue destructor + * ----------------------------------------------- + * All of the dynamic memory is allocated in the Vector class, + * so no work is required at this level. + */ + +template <typename ValueType> +PriorityQueue<ValueType>::~PriorityQueue() { + /* Empty */ +} + +template <typename ValueType> +int PriorityQueue<ValueType>::size() const { + return count; +} + +template <typename ValueType> +bool PriorityQueue<ValueType>::isEmpty() const { + return count == 0; +} + +template <typename ValueType> +void PriorityQueue<ValueType>::clear() { + heap.clear(); + count = 0; +} + +template <typename ValueType> +void PriorityQueue<ValueType>::enqueue(ValueType value, double priority) { + if (count == heap.size()) heap.add(HeapEntry()); + int index = count++; + heap[index].value = value; + heap[index].priority = priority; + heap[index].sequence = enqueueCount++; + if (index == 0 || takesPriority(backIndex, index)) backIndex = index; + while (index > 0) { + int parent = (index - 1) / 2; + if (takesPriority(parent, index)) break; + swapHeapEntries(parent, index); + index = parent; + } +} + +template <typename ValueType> +void PriorityQueue<ValueType>::changePriority(ValueType value, double newPriority) { + // parts of this implementation are adapted from TrailblazerPQueue.h, + // which was written by Keith Schwarz + + if (!(newPriority == newPriority)) { + error("PriorityQueue::changePriority(" + genericValueToString(value) + ", " + + realToString(newPriority) + "): Attempted to use NaN as a priority."); + } + + // find the element in the pqueue; must use a simple iteration over elements + for (int i = 0; i < count; i++) { + if (heap[i].value == value) { + if (heap[i].priority < newPriority) { + error("PriorityQueue::changePriority(" + genericValueToString(value) + ", " + + realToString(newPriority) + + "): new priority cannot be less urgent than current priority of " + + realToString(heap[i].priority) + "."); + } + heap[i].priority = newPriority; + + // after changing the priority, must percolate up to proper level + // to maintain heap ordering + while (i > 0) { + int parent = (i - 1) / 2; + if (takesPriority(parent, i)) break; + swapHeapEntries(parent, i); + i = parent; + } + + return; + } + } + + // if we get here, the element was not ever found + error("PriorityQueue::changePriority(" + genericValueToString(value) + ", " + + realToString(newPriority) + "): Element value not found."); +} + +/* + * Implementation notes: dequeue, peek, peekPriority + * ------------------------------------------------- + * These methods must check for an empty queue and report an error + * if there is no first element. + */ + +template <typename ValueType> +ValueType PriorityQueue<ValueType>::dequeue() { + if (count == 0) error("dequeue: Attempting to dequeue an empty queue"); + count--; + bool wasBack = (backIndex == count); + ValueType value = heap[0].value; + swapHeapEntries(0, count); + int index = 0; + while (true) { + int left = 2 * index + 1; + int right = 2 * index + 2; + if (left >= count) break; + int child = left; + if (right < count && takesPriority(right, left)) child = right; + if (takesPriority(index, child)) break; + swapHeapEntries(index, child); + index = child; + } + if (wasBack) backIndex = index; + return value; +} + +template <typename ValueType> +ValueType PriorityQueue<ValueType>::peek() const { + if (count == 0) error("peek: Attempting to peek at an empty queue"); + return heap.get(0).value; +} + +template <typename ValueType> +double PriorityQueue<ValueType>::peekPriority() const { + if (count == 0) error("peekPriority: Attempting to peek at an empty queue"); + return heap.get(0).priority; +} + +template <typename ValueType> +ValueType & PriorityQueue<ValueType>::front() { + if (count == 0) error("front: Attempting to read front of an empty queue"); + return heap.get(0).value; +} + +template <typename ValueType> +ValueType & PriorityQueue<ValueType>::back() { + if (count == 0) error("back: Attempting to read back of an empty queue"); + return heap.get(backIndex).value; +} + +template <typename ValueType> +bool PriorityQueue<ValueType>::takesPriority(int i1, int i2) { + if (heap[i1].priority < heap[i2].priority) return true; + if (heap[i1].priority > heap[i2].priority) return false; + return (heap[i1].sequence < heap[i2].sequence); +} + +template <typename ValueType> +void PriorityQueue<ValueType>::swapHeapEntries(int i1, int i2) { + HeapEntry entry = heap[i1]; + heap[i1] = heap[i2]; + heap[i2] = entry; +} + +template <typename ValueType> +std::string PriorityQueue<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, + const PriorityQueue<ValueType> & pq) { + os << "{"; + PriorityQueue<ValueType> copy = pq; + int len = pq.size(); + for (int i = 0; i < len; i++) { + if (i > 0) os << ", "; + os << copy.peekPriority() << ":"; + writeGenericValue(os, copy.dequeue(), true); + } + return os << "}"; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, PriorityQueue<ValueType> & pq) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + pq.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + double priority; + is >> priority >> ch; + if (ch != ':') error("operator >>: Missing colon after priority"); + ValueType value; + readGenericValue(is, value); + pq.enqueue(value, priority); + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +#endif diff --git a/labb8/lib/StanfordCPPLib/private/main.h b/labb8/lib/StanfordCPPLib/private/main.h new file mode 100755 index 0000000..698a97c --- /dev/null +++ b/labb8/lib/StanfordCPPLib/private/main.h @@ -0,0 +1,58 @@ +/* + * File: main.h + * ------------ + * This file renames the <code>main</code> method in the client's + * program to <code>Main</code>, thereby allowing a custom + * <code>main</code> method in the libraries to take control + * before passing control back to the client program. The main macro + * also defines a function getMainFlags that returns an int whose bits + * indicate which of the various interfaces have been loaded by this + * definition of main. + * + * Note: This file can be loaded more than once and must therefore + * check to see what has already been defined. + */ + +#ifdef main +# undef main +# undef CONSOLE_FLAG +# undef GRAPHICS_FLAG +#else +# define MAIN_USES_CONSOLE (1<<0) +# define MAIN_USES_GRAPHICS (1<<1) +#endif + +#ifdef _console_h +# define CONSOLE_FLAG MAIN_USES_CONSOLE +#else +# define CONSOLE_FLAG 0 +#endif + +#ifdef _gwindow_h +# define GRAPHICS_FLAG MAIN_USES_GRAPHICS +#else +# define GRAPHICS_FLAG 0 +#endif + +#if CONSOLE_FLAG | GRAPHICS_FLAG + +#define main main(int argc, char **argv) { \ + extern int _mainFlags; \ + _mainFlags = GRAPHICS_FLAG + CONSOLE_FLAG; \ + return startupMain(argc, argv); \ + } \ + int Main + +extern int startupMain(int argc, char **argv); + +#else + +#define main main(int argc, char **argv) { \ + extern int _mainFlags; \ + _mainFlags = GRAPHICS_FLAG + CONSOLE_FLAG; \ + return mainWrapper(argc, argv); } \ + int Main + +extern int mainWrapper(int argc, char **argv); + +#endif diff --git a/labb8/lib/StanfordCPPLib/private/randompatch.h b/labb8/lib/StanfordCPPLib/private/randompatch.h new file mode 100755 index 0000000..c7a4b86 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/private/randompatch.h @@ -0,0 +1,63 @@ +/* + * File: private/randompatch.h + * --------------------------- + * This file patches the implementation of the random number library + * to avoid some serious bugs in standard implementations of rand, + * particularly on Mac OS X. It also includes a hack to set the + * seed from the RANDOM_SEED environment variable, which makes it + * possible to produce repeatable figures. + */ + +/* + * Implementation notes: rand, srand + * --------------------------------- + * To ensure that this package works the same way on all platforms, + * this file completely reimplements the rand and srand methods. The + * algorithm is a conventional linear congruential generator with the + * parameters used in GNU's gclib. RAND_MAX for this implementation + * is 2147483647 (2^31 - 1). + */ + +#define MULTIPLIER 1103515245 +#define OFFSET 12345 + +static int _seed = 1; + +#undef rand +#define rand() ((_seed = MULTIPLIER * _seed + OFFSET) & 0x7FFFFFFF) + +#undef srand +#define srand(seed) (_seed = int(seed), _seed = (_seed <= 0) ? 1 : _seed) + +#undef RAND_MAX +#define RAND_MAX 2147483647 + +/* + * Implementation notes: Windows patch + * ----------------------------------- + * On some versions of Windows, the time function is too coarse to use + * as a random seed. On those versions, this definition substitutes the + * GetTickCount function. + */ + +#if defined (_MSC_VER) && (_MSC_VER >= 1200) +# include <windows.h> +# define time(dummy) (GetTickCount()) +#endif + +#ifdef __APPLE__ + +# include <cstdlib> + + static time_t patchedTime(time_t *) { + char *str = getenv("RANDOM_SEED"); + if (str == NULL) { + return time(NULL); + } else { + return atoi(str); + } + } + +# define time(dummy) patchedTime(dummy) + +#endif diff --git a/labb8/lib/StanfordCPPLib/private/tokenpatch.h b/labb8/lib/StanfordCPPLib/private/tokenpatch.h new file mode 100755 index 0000000..d4930e5 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/private/tokenpatch.h @@ -0,0 +1,11 @@ +/* + * File: tokenpatch.h + * ------------------ + * This file renames TokenType and WORD to avoid conflict with the + * <windows.h> header. + */ + +#ifdef _MSC_VER +# define TokenType TokenTypeT +# define WORD WORD_TC +#endif diff --git a/labb8/lib/StanfordCPPLib/private/tplatform.h b/labb8/lib/StanfordCPPLib/private/tplatform.h new file mode 100755 index 0000000..6cf4bae --- /dev/null +++ b/labb8/lib/StanfordCPPLib/private/tplatform.h @@ -0,0 +1,25 @@ +/* + * File: tplatform.h + * ----------------- + * This interface defines the platform-specific methods on threads + * and locks. + */ + +/* Methods for threads */ + +int forkForPlatform(void (*fn)(void *), void *arg); +void incThreadRefCountForPlatform(int id); +void decThreadRefCountForPlatform(int id); +void joinForPlatform(int id); +int getCurrentThreadForPlatform(); +void yieldForPlatform(); + +/* Methods for locks */ + +int initLockForPlatform(); +void incLockRefCountForPlatform(int id); +void decLockRefCountForPlatform(int id); +void lockForPlatform(int id); +void unlockForPlatform(int id); +void waitForPlatform(int id); +void signalForPlatform(int id); diff --git a/labb8/lib/StanfordCPPLib/queue.h b/labb8/lib/StanfordCPPLib/queue.h new file mode 100755 index 0000000..0d94a2c --- /dev/null +++ b/labb8/lib/StanfordCPPLib/queue.h @@ -0,0 +1,372 @@ +/* + * File: queue.h + * ------------- + * This file exports the <code>Queue</code> class, a collection + * in which values are ordinarily processed in a first-in/first-out + * (FIFO) order. + */ + +#ifndef _queue_h +#define _queue_h + +#include "vector.h" + +/* + * Class: Queue<ValueType> + * ----------------------- + * This class models a linear structure called a <b><i>queue</i></b> + * in which values are added at one end and removed from the other. + * This discipline gives rise to a first-in/first-out behavior (FIFO) + * that is the defining feature of queues. + */ + +template <typename ValueType> +class Queue { + +public: + +/* + * Constructor: Queue + * Usage: Queue<ValueType> queue; + * ------------------------------ + * Initializes a new empty queue. + */ + + Queue(); + +/* + * Destructor: ~Queue + * ------------------ + * Frees any heap storage associated with this queue. + */ + + virtual ~Queue(); + +/* + * Method: size + * Usage: int n = queue.size(); + * ---------------------------- + * Returns the number of values in the queue. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (queue.isEmpty()) ... + * ------------------------------- + * Returns <code>true</code> if the queue contains no elements. + */ + + bool isEmpty() const; + +/* + * Method: clear + * Usage: queue.clear(); + * --------------------- + * Removes all elements from the queue. + */ + + void clear(); + +/* + * Method: enqueue + * Usage: queue.enqueue(value); + * ---------------------------- + * Adds <code>value</code> to the end of the queue. + */ + + void enqueue(ValueType value); + +/* + * Method: dequeue + * Usage: ValueType first = queue.dequeue(); + * ----------------------------------------- + * Removes and returns the first item in the queue. + */ + + ValueType dequeue(); + +/* + * Method: peek + * Usage: ValueType first = queue.peek(); + * -------------------------------------- + * Returns the first value in the queue, without removing it. For + * compatibility with the STL classes, this method is also exported + * under the name <code>front</code>, in which case it returns the + * value by reference. + */ + + ValueType peek() const; + +/* + * Method: front + * Usage: ValueType first = queue.front(); + * --------------------------------------- + * Returns the first value in the queue by reference. + */ + + ValueType & front(); + +/* + * Method: back + * Usage: ValueType last = queue.back(); + * ------------------------------------- + * Returns the last value in the queue by reference. + */ + + ValueType & back(); + +/* + * Method: toString + * Usage: string str = queue.toString(); + * ------------------------------------- + * Converts the queue to a printable string representation. + */ + + std::string toString(); + +/* + * Operator: == + * Usage: queue1 == queue2 + * ------------------- + * Returns <code>true</code> if <code>queue1</code> and <code>queue2</code> + * contain the same elements. + */ + + bool operator==(const Queue & queue2) const; + +/* + * Operator: != + * Usage: queue1 != queue2 + * ------------------- + * Returns <code>true</code> if <code>queue1</code> and <code>queue2</code> + * do not contain the same elements. + */ + + bool operator!=(const Queue & queue2) const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Implementation notes: Queue data structure + * ------------------------------------------ + * The Queue class is implemented using a ring buffer. + */ + +private: + +/* Instance variables */ + + Vector<ValueType> ringBuffer; + int count; + int capacity; + int head; + int tail; + +/* Private functions */ + + void expandRingBufferCapacity(); + +}; + +extern void error(std::string msg); + +/* + * Implementation notes: Queue data structure + * ------------------------------------------ + * The array-based queue stores the elements in successive index + * positions in a vector, just as a stack does. What makes the + * queue structure more complex is the need to avoid shifting + * elements as the queue expands and contracts. In the array + * model, this goal is achieved by keeping track of both the + * head and tail indices. The tail index increases by one each + * time an element is enqueued, and the head index increases by + * one each time an element is dequeued. Each index therefore + * marches toward the end of the allocated vector and will + * eventually reach the end. Rather than allocate new memory, + * this implementation lets each index wrap around back to the + * beginning as if the ends of the array of elements were joined + * to form a circle. This representation is called a ring buffer. + */ + +const int INITIAL_CAPACITY = 10; + +/* + * Implementation notes: Queue constructor + * --------------------------------------- + * The constructor must allocate the array storage for the queue + * elements and initialize the fields of the object. + */ + +template <typename ValueType> +Queue<ValueType>::Queue() { + clear(); +} + +/* + * Implementation notes: ~Queue destructor + * --------------------------------------- + * All of the dynamic memory is allocated in the Vector class, + * so no work is required at this level. + */ + +template <typename ValueType> +Queue<ValueType>::~Queue() { + /* Empty */ +} + +template <typename ValueType> +int Queue<ValueType>::size() const { + return count; +} + +template <typename ValueType> +bool Queue<ValueType>::isEmpty() const { + return count == 0; +} + +template <typename ValueType> +void Queue<ValueType>::clear() { + capacity = INITIAL_CAPACITY; + ringBuffer = Vector<ValueType>(capacity); + head = 0; + tail = 0; + count = 0; +} + +template <typename ValueType> +void Queue<ValueType>::enqueue(ValueType value) { + if (count >= capacity - 1) expandRingBufferCapacity(); + ringBuffer[tail] = value; + tail = (tail + 1) % capacity; + count++; +} + +/* + * Implementation notes: dequeue, peek + * ----------------------------------- + * These methods must check for an empty queue and report an error + * if there is no first element. + */ + +template <typename ValueType> +ValueType Queue<ValueType>::dequeue() { + if (count == 0) error("dequeue: Attempting to dequeue an empty queue"); + ValueType result = ringBuffer[head]; + head = (head + 1) % capacity; + count--; + return result; +} + +template <typename ValueType> +ValueType Queue<ValueType>::peek() const { + if (count == 0) error("peek: Attempting to peek at an empty queue"); + return ringBuffer.get(head); +} + +template <typename ValueType> +ValueType & Queue<ValueType>::front() { + if (count == 0) error("front: Attempting to read front of an empty queue"); + return ringBuffer[head]; +} + +template <typename ValueType> +ValueType & Queue<ValueType>::back() { + if (count == 0) error("back: Attempting to read back of an empty queue"); + return ringBuffer[(tail + capacity - 1) % capacity]; +} + +/* + * Implementation notes: expandRingBufferCapacity + * ---------------------------------------------- + * This private method doubles the capacity of the ringBuffer vector. + * Note that this implementation also shifts all the elements back to + * the beginning of the vector. + */ + +template <typename ValueType> +void Queue<ValueType>::expandRingBufferCapacity() { + Vector<ValueType> copy = ringBuffer; + ringBuffer = Vector<ValueType>(2 * capacity); + for (int i = 0; i < count; i++) { + ringBuffer[i] = copy[(head + i) % capacity]; + } + head = 0; + tail = count; + capacity *= 2; +} + +template <typename ValueType> +std::string Queue<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +template <typename ValueType> +bool Queue<ValueType>::operator==(const Queue & queue2) const { + if (size() != queue2.size()) return false; + Queue<ValueType> copy1 = *this; + Queue<ValueType> copy2 = queue2; + for (int i = 0; i < size(); i++) { + if (copy1.dequeue() != copy2.dequeue()) { + return false; + } + } + return true; +} + +template <typename ValueType> +bool Queue<ValueType>::operator!=(const Queue & queue2) const { + return !(*this == queue2); +} + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, const Queue<ValueType> & queue) { + os << "{"; + Queue<ValueType> copy = queue; + int len = queue.size(); + for (int i = 0; i < len; i++) { + if (i > 0) os << ", "; + writeGenericValue(os, copy.dequeue(), true); + } + return os << "}"; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, Queue<ValueType> & queue) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + queue.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + ValueType value; + readGenericValue(is, value); + queue.enqueue(value); + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +// hashing functions for queues; defined in hashmap.cpp +int hashCode(const Queue<std::string>& q); +int hashCode(const Queue<int>& q); +int hashCode(const Queue<char>& q); +int hashCode(const Queue<long>& q); +int hashCode(const Queue<double>& q); + +#endif diff --git a/labb8/lib/StanfordCPPLib/random.cpp b/labb8/lib/StanfordCPPLib/random.cpp new file mode 100755 index 0000000..b7ea860 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/random.cpp @@ -0,0 +1,99 @@ +/* + * File: random.cpp + * ---------------- + * This file implements the random.h interface. + */ + +#include <cstdlib> +#include <cmath> +#include <ctime> +#include "random.h" +#include "private/randompatch.h" +using namespace std; + +/* Private function prototype */ + +static void initRandomSeed(); + +/* + * Implementation notes: randomInteger + * ----------------------------------- + * The code for randomInteger produces the number in four steps: + * + * 1. Generate a random real number d in the range [0 .. 1). + * 2. Scale the number to the range [0 .. N) where N is the number of values. + * 3. Translate the number so that the range starts at the appropriate value. + * 4. Convert the result to the next lower integer. + * + * The implementation is complicated by the fact that both the expression + * + * RAND_MAX + 1 + * + * and the expression for the number of values + * + * high - low + 1 + * + * can overflow the integer range. These calculations must therefore be + * performed using doubles instead of ints. + */ + +int randomInteger(int low, int high) { + initRandomSeed(); + double d = rand() / (double(RAND_MAX) + 1); + double s = d * (double(high) - low + 1); + return int(floor(low + s)); +} + +/* + * Implementation notes: randomReal + * -------------------------------- + * The code for randomReal is similar to that for randomInteger, + * without the final conversion step. + */ + +double randomReal(double low, double high) { + initRandomSeed(); + double d = rand() / (double(RAND_MAX) + 1); + double s = d * (high - low); + return low + s; +} + +/* + * Implementation notes: randomChance + * ---------------------------------- + * The code for randomChance calls randomReal(0, 1) and then checks + * whether the result is less than the requested probability. + */ + +bool randomChance(double p) { + initRandomSeed(); + return randomReal(0, 1) < p; +} + +/* + * Implementation notes: setRandomSeed + * ----------------------------------- + * The setRandomSeed function simply forwards its argument to srand. + * The call to initRandomSeed is required to set the initialized flag. + */ + +void setRandomSeed(int seed) { + initRandomSeed(); + srand(seed); +} + +/* + * Implementation notes: initRandomSeed + * ------------------------------------ + * The initRandomSeed function declares a static variable that keeps track + * of whether the seed has been initialized. The first time initRandomSeed + * is called, initialized is false, so the seed is set to the current time. + */ + +static void initRandomSeed() { + static bool initialized = false; + if (!initialized) { + srand(int(time(NULL))); + initialized = true; + } +} diff --git a/labb8/lib/StanfordCPPLib/random.h b/labb8/lib/StanfordCPPLib/random.h new file mode 100755 index 0000000..3286f8f --- /dev/null +++ b/labb8/lib/StanfordCPPLib/random.h @@ -0,0 +1,58 @@ +/* + * File: random.h + * -------------- + * This file exports functions for generating pseudorandom numbers. + */ + +#ifndef _random_h +#define _random_h + +/* + * Function: randomInteger + * Usage: int n = randomInteger(low, high); + * ---------------------------------------- + * Returns a random integer in the range <code>low</code> to + * <code>high</code>, inclusive. + */ + +int randomInteger(int low, int high); + +/* + * Function: randomReal + * Usage: double d = randomReal(low, high); + * ---------------------------------------- + * Returns a random real number in the half-open interval + * [<code>low</code> .. <code>high</code>). A half-open + * interval includes the first endpoint but not the second, which + * means that the result is always greater than or equal to + * <code>low</code> but strictly less than <code>high</code>. + */ + +double randomReal(double low, double high); + +/* + * Function: randomChance + * Usage: if (randomChance(p)) ... + * ------------------------------- + * Returns <code>true</code> with the probability indicated by <code>p</code>. + * The argument <code>p</code> must be a floating-point number between + * 0 (never) and 1 (always). For example, calling + * <code>randomChance(.30)</code> returns <code>true</code> 30 percent + * of the time. + */ + +bool randomChance(double p); + +/* + * Function: setRandomSeed + * Usage: setRandomSeed(seed); + * --------------------------- + * Sets the internal random number seed to the specified value. You + * can use this function to set a specific starting point for the + * pseudorandom sequence or to ensure that program behavior is + * repeatable during the debugging phase. + */ + +void setRandomSeed(int seed); + +#endif diff --git a/labb8/lib/StanfordCPPLib/set.h b/labb8/lib/StanfordCPPLib/set.h new file mode 100755 index 0000000..4395fa5 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/set.h @@ -0,0 +1,621 @@ +/* + * File: set.h + * ----------- + * This file exports the <code>Set</code> class, which implements a + * collection for storing a set of distinct elements. + */ + +#ifndef _set_h +#define _set_h + +#include <iostream> +#include "foreach.h" +#include "map.h" +#include "vector.h" + +/* + * Class: Set<ValueType> + * --------------------- + * This class stores a collection of distinct elements. + */ + +template <typename ValueType> +class Set { + +public: + +/* + * Constructor: Set + * Usage: Set<ValueType> set; + * -------------------------- + * Creates an empty set of the specified element type. + */ + + Set(); + +/* + * Destructor: ~Set + * ---------------- + * Frees any heap storage associated with this set. + */ + + virtual ~Set(); + +/* + * Method: size + * Usage: count = set.size(); + * -------------------------- + * Returns the number of elements in this set. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (set.isEmpty()) ... + * ----------------------------- + * Returns <code>true</code> if this set contains no elements. + */ + + bool isEmpty() const; + +/* + * Method: add + * Usage: set.add(value); + * ---------------------- + * Adds an element to this set, if it was not already there. For + * compatibility with the STL <code>set</code> class, this method + * is also exported as <code>insert</code>. + */ + + void add(const ValueType & value); + void insert(const ValueType & value); + +/* + * Method: remove + * Usage: set.remove(value); + * ------------------------- + * Removes an element from this set. If the value was not + * contained in the set, no error is generated and the set + * remains unchanged. + */ + + void remove(const ValueType & value); + +/* + * Method: contains + * Usage: if (set.contains(value)) ... + * ----------------------------------- + * Returns <code>true</code> if the specified value is in this set. + */ + + bool contains(const ValueType & value) const; + +/* + * Method: isSubsetOf + * Usage: if (set.isSubsetOf(set2)) ... + * ------------------------------------ + * Implements the subset relation on sets. It returns + * <code>true</code> if every element of this set is + * contained in <code>set2</code>. + */ + + bool isSubsetOf(const Set & set2) const; + +/* + * Method: clear + * Usage: set.clear(); + * ------------------- + * Removes all elements from this set. + */ + + void clear(); + +/* + * Operator: == + * Usage: set1 == set2 + * ------------------- + * Returns <code>true</code> if <code>set1</code> and <code>set2</code> + * contain the same elements. + */ + + bool operator==(const Set & set2) const; + +/* + * Operator: != + * Usage: set1 != set2 + * ------------------- + * Returns <code>true</code> if <code>set1</code> and <code>set2</code> + * are different. + */ + + bool operator!=(const Set & set2) const; + +/* + * Operator: + + * Usage: set1 + set2 + * set1 + element + * --------------------- + * Returns the union of sets <code>set1</code> and <code>set2</code>, which + * is the set of elements that appear in at least one of the two sets. The + * right hand set can be replaced by an element of the value type, in which + * case the operator returns a new set formed by adding that element. + */ + + Set operator+(const Set & set2) const; + Set operator+(const ValueType & element) const; + +/* + * Operator: * + * Usage: set1 * set2 + * ------------------ + * Returns the intersection of sets <code>set1</code> and <code>set2</code>, + * which is the set of all elements that appear in both. + */ + + Set operator*(const Set & set2) const; + +/* + * Operator: - + * Usage: set1 - set2 + * set1 - element + * --------------------- + * Returns the difference of sets <code>set1</code> and <code>set2</code>, + * which is all of the elements that appear in <code>set1</code> but + * not <code>set2</code>. The right hand set can be replaced by an + * element of the value type, in which case the operator returns a new + * set formed by removing that element. + */ + + Set operator-(const Set & set2) const; + Set operator-(const ValueType & element) const; + +/* + * Operator: += + * Usage: set1 += set2; + * set1 += value; + * --------------------- + * Adds all of the elements from <code>set2</code> (or the single + * specified value) to <code>set1</code>. As a convenience, the + * <code>Set</code> package also overloads the comma operator so + * that it is possible to initialize a set like this: + * + *<pre> + * Set<int> digits; + * digits += 0, 1, 2, 3, 4, 5, 6, 7, 8, 9; + *</pre> + */ + + Set & operator+=(const Set & set2); + Set & operator+=(const ValueType & value); + +/* + * Operator: *= + * Usage: set1 *= set2; + * -------------------- + * Removes any elements from <code>set1</code> that are not present in + * <code>set2</code>. + */ + + Set & operator*=(const Set & set2); + +/* + * Operator: -= + * Usage: set1 -= set2; + * set1 -= value; + * --------------------- + * Removes the elements from <code>set2</code> (or the single + * specified value) from <code>set1</code>. As a convenience, the + * <code>Set</code> package also overloads the comma operator so + * that it is possible to remove multiple elements from a set + * like this: + * + *<pre> + * digits -= 0, 2, 4, 6, 8; + *</pre> + * + * which removes the values 0, 2, 4, 6, and 8 from the set + * <code>digits</code>. + */ + + Set & operator-=(const Set & set2); + Set & operator-=(const ValueType & value); + +/* + * Method: first + * Usage: ValueType value = set.first(); + * ------------------------------------- + * Returns the first value in the set in the order established by the + * <code>foreach</code> macro. If the set is empty, <code>first</code> + * generates an error. + */ + + ValueType first() const; + +/* + * Method: toString + * Usage: string str = set.toString(); + * ----------------------------------- + * Converts the set to a printable string representation. + */ + + std::string toString(); + +/* + * Method: mapAll + * Usage: set.mapAll(fn); + * ---------------------- + * Iterates through the elements of the set and calls <code>fn(value)</code> + * for each one. The values are processed in ascending order, as defined + * by the comparison function. + */ + + void mapAll(void (*fn)(ValueType)) const; + void mapAll(void (*fn)(const ValueType &)) const; + + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional Set operations + * ------------------------- + * In addition to the methods listed in this interface, the Set class + * supports the following operations: + * + * - Stream I/O using the << and >> operators + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement and STL iterators + * + * The iteration forms process the Set in ascending order. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + + Map<ValueType,bool> map; /* Map used to store the element */ + bool removeFlag; /* Flag to differentiate += and -= */ + +public: + +/* + * Hidden features + * --------------- + * The remainder of this file consists of the code required to + * support the comma operator, deep copying, and iteration. + * Including these methods in the public interface would make + * that interface more difficult to understand for the average client. + */ + +/* Extended constructors */ + + template <typename CompareType> + explicit Set(CompareType cmp) : map(Map<ValueType,bool>(cmp)) { + /* Empty */ + } + + Set & operator,(const ValueType & value) { + if (this->removeFlag) { + this->remove(value); + } else { + this->add(value); + } + return *this; + } + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : public std::iterator<std::input_iterator_tag,ValueType> { + + private: + + typename Map<ValueType,bool>::iterator mapit; /* Iterator for the map */ + + public: + + iterator() { + /* Empty */ + } + + iterator(typename Map<ValueType,bool>::iterator it) : mapit(it) { + /* Empty */ + } + + iterator(const iterator & it) { + mapit = it.mapit; + } + + iterator & operator++() { + ++mapit; + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + bool operator==(const iterator & rhs) { + return mapit == rhs.mapit; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + ValueType operator*() { + return *mapit; + } + + ValueType *operator->() { + return mapit; + } + }; + + iterator begin() const { + return iterator(map.begin()); + } + + iterator end() const { + return iterator(map.end()); + } + +}; + +extern void error(std::string msg); + +template <typename ValueType> +Set<ValueType>::Set() { + /* Empty */ +} + +template <typename ValueType> +Set<ValueType>::~Set() { + /* Empty */ +} + +template <typename ValueType> +int Set<ValueType>::size() const { + return map.size(); +} + +template <typename ValueType> +bool Set<ValueType>::isEmpty() const { + return map.isEmpty(); +} + +template <typename ValueType> +void Set<ValueType>::add(const ValueType & value) { + map.put(value, true); +} + +template <typename ValueType> +void Set<ValueType>::insert(const ValueType & value) { + map.put(value, true); +} + +template <typename ValueType> +void Set<ValueType>::remove(const ValueType & value) { + map.remove(value); +} + +template <typename ValueType> +bool Set<ValueType>::contains(const ValueType & value) const { + return map.containsKey(value); +} + +template <typename ValueType> +void Set<ValueType>::clear() { + map.clear(); +} + +template <typename ValueType> +bool Set<ValueType>::isSubsetOf(const Set & set2) const { + iterator it = begin(); + iterator end = this->end(); + while (it != end) { + if (!set2.map.containsKey(*it)) return false; + ++it; + } + return true; +} + +/* + * Implementation notes: set operators + * ----------------------------------- + * The implementations for the set operators use iteration to walk + * over the elements in one or both sets. + */ + +template <typename ValueType> +bool Set<ValueType>::operator==(const Set & set2) const { + if (size() != set2.map.size()) return false; + iterator it1 = begin(); + iterator it2 = set2.map.begin(); + iterator end = this->end(); + while (it1 != end) { + if (map.compareKeys(*it1, *it2) != 0) return false; + ++it1; + ++it2; + } + return true; +} + +template <typename ValueType> +bool Set<ValueType>::operator!=(const Set & set2) const { + return !(*this == set2); +} + +template <typename ValueType> +Set<ValueType> Set<ValueType>::operator+(const Set & set2) const { + Set<ValueType> set = *this; + foreach (ValueType value in set2) { + set.add(value); + } + return set; +} + +template <typename ValueType> +Set<ValueType> Set<ValueType>::operator+(const ValueType & element) const { + Set<ValueType> set = *this; + set.add(element); + return set; +} + +template <typename ValueType> +Set<ValueType> Set<ValueType>::operator*(const Set & set2) const { + Set<ValueType> set = *this; + set.clear(); + foreach (ValueType value in *this) { + if (set2.contains(value)) set.add(value); + } + return set; +} + +template <typename ValueType> +Set<ValueType> Set<ValueType>::operator-(const Set & set2) const { + Set<ValueType> set = *this; + foreach (ValueType value in set2) { + set.remove(value); + } + return set; +} + +template <typename ValueType> +Set<ValueType> Set<ValueType>::operator-(const ValueType & element) const { + Set<ValueType> set = *this; + set.remove(element); + return set; +} + +template <typename ValueType> +Set<ValueType> & Set<ValueType>::operator+=(const Set & set2) { + foreach (ValueType value in set2) { + this->add(value); + } + return *this; +} + +template <typename ValueType> +Set<ValueType> & Set<ValueType>::operator+=(const ValueType & value) { + this->add(value); + this->removeFlag = false; + return *this; +} + +template <typename ValueType> +Set<ValueType> & Set<ValueType>::operator*=(const Set & set2) { + Vector<ValueType> toRemove; + foreach (ValueType value in *this) { + if (!set2.map.containsKey(value)) toRemove.add(value); + } + foreach (ValueType value in toRemove) { + this->remove(value); + } + return *this; +} + +template <typename ValueType> +Set<ValueType> & Set<ValueType>::operator-=(const Set & set2) { + Vector<ValueType> toRemove; + foreach (ValueType value in *this) { + if (set2.map.containsKey(value)) toRemove.add(value); + } + foreach (ValueType value in toRemove) { + this->remove(value); + } + return *this; +} + +template <typename ValueType> +Set<ValueType> & Set<ValueType>::operator-=(const ValueType & value) { + this->remove(value); + this->removeFlag = true; + return *this; +} + +template <typename ValueType> +ValueType Set<ValueType>::first() const { + if (isEmpty()) error("first: set is empty"); + return *begin(); +} + +template <typename ValueType> +std::string Set<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +template <typename ValueType> +void Set<ValueType>::mapAll(void (*fn)(ValueType)) const { + map.mapAll(fn); +} + +template <typename ValueType> +void Set<ValueType>::mapAll(void (*fn)(const ValueType &)) const { + map.mapAll(fn); +} + +template <typename ValueType> +template <typename FunctorType> +void Set<ValueType>::mapAll(FunctorType fn) const { + map.mapAll(fn); +} + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, const Set<ValueType> & set) { + os << "{"; + bool started = false; + foreach (ValueType value in set) { + if (started) os << ", "; + writeGenericValue(os, value, true); + started = true; + } + os << "}"; + return os; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, Set<ValueType> & set) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + set.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + ValueType value; + readGenericValue(is, value); + set += value; + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +// hashing functions for sets; defined in hashmap.cpp +int hashCode(const Set<std::string>& s); +int hashCode(const Set<int>& s); +int hashCode(const Set<char>& s); +int hashCode(const Set<long>& s); +int hashCode(const Set<double>& s); + +#endif diff --git a/labb8/lib/StanfordCPPLib/simpio.cpp b/labb8/lib/StanfordCPPLib/simpio.cpp new file mode 100755 index 0000000..4f91551 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/simpio.cpp @@ -0,0 +1,67 @@ +/* + * File: simpio.cpp + * ---------------- + * This file implements the simpio.h interface. + */ + +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> +#include "simpio.h" +using namespace std; + +/* + * Implementation notes: getInteger, getReal + * ----------------------------------------- + * Each of these functions reads a complete input line and then uses the + * <sstream> library to parse that line into a value of the desired type. + * If that fails, the implementation asks the user for a new value. + */ + +int getInteger(string prompt) { + int value; + string line; + while (true) { + cout << prompt; + getline(cin, line); + istringstream stream(line); + stream >> value >> ws; + if (!stream.fail() && stream.eof()) break; + cout << "Illegal integer format. Try again." << endl; + if (prompt == "") prompt = "Enter an integer: "; + } + return value; +} + +double getReal(string prompt) { + double value; + string line; + while (true) { + cout << prompt; + getline(cin, line); + istringstream stream(line); + stream >> value >> ws; + if (!stream.fail() && stream.eof()) break; + cout << "Illegal numeric format. Try again." << endl; + if (prompt == "") prompt = "Enter a number: "; + } + return value; +} + +/* + * Implementation notes: getLine + * ----------------------------- + * The getLine function simply combines the process of displaying a + * prompt and reading an input line into a single call. The primary + * reason for including this function in the library is to ensure + * that the process of reading integers, floating-point numbers, and + * strings remains as consistent as possible. + */ + +string getLine(string prompt) { + string line; + cout << prompt; + getline(cin, line); + return line; +} diff --git a/labb8/lib/StanfordCPPLib/simpio.h b/labb8/lib/StanfordCPPLib/simpio.h new file mode 100755 index 0000000..25c32c1 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/simpio.h @@ -0,0 +1,53 @@ +/* + * File: simpio.h + * -------------- + * This file exports a set of functions that simplify input/output + * operations in C++ and provide some error-checking on console input. + */ + +#ifndef _simpio_h +#define _simpio_h + +#include <string> + +/* + * Function: getInteger + * Usage: int n = getInteger(prompt); + * ---------------------------------- + * Reads a complete line from <code>cin</code> and scans it as an + * integer. If the scan succeeds, the integer value is returned. If + * the argument is not a legal integer or if extraneous characters + * (other than whitespace) appear in the string, the user is given + * a chance to reenter the value. If supplied, the optional + * <code>prompt</code> string is printed before reading the value. + */ + +int getInteger(std::string prompt = ""); + +/* + * Function: getReal + * Usage: double x = getReal(prompt); + * ---------------------------------- + * Reads a complete line from <code>cin</code> and scans it as a + * floating-point number. If the scan succeeds, the floating-point + * value is returned. If the input is not a legal number or if + * extraneous characters (other than whitespace) appear in the string, + * the user is given a chance to reenter the value. If supplied, the + * optional <code>prompt</code> string is printed before reading the value. + */ + +double getReal(std::string prompt = ""); + +/* + * Function: getLine + * Usage: string line = getLine(prompt); + * ------------------------------------- + * Reads a line of text from <code>cin</code> and returns that line + * as a string. The newline character that terminates the input is + * not stored as part of the return value. If supplied, the optional + * <code>prompt</code> string is printed before reading the value. + */ + +std::string getLine(std::string prompt = ""); + +#endif diff --git a/labb8/lib/StanfordCPPLib/sound.cpp b/labb8/lib/StanfordCPPLib/sound.cpp new file mode 100755 index 0000000..f0c4705 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/sound.cpp @@ -0,0 +1,31 @@ +/* + * File: sound.cpp + * --------------- + * Implementation of the Sound class. + */ + +#include <iostream> +#include <sstream> +#include <string> +#include <vector> +#include "gevents.h" +#include "gtypes.h" +#include "sound.h" +#include "vector.h" +#include "platform.h" +using namespace std; + +static Platform *pp = getPlatform(); + +Sound::Sound(string filename) { + pp->createSound(this, filename); +} + +Sound::~Sound() { + pp->deleteSound(this); +} + +void Sound::play() { + pp->playSound(this); +} + diff --git a/labb8/lib/StanfordCPPLib/sound.h b/labb8/lib/StanfordCPPLib/sound.h new file mode 100755 index 0000000..89e2066 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/sound.h @@ -0,0 +1,63 @@ +/* + * File: sound.h + * ------------- + * This file defines a class that represents a sound. + */ + +#ifndef _sound_h +#define _sound_h + +/* + * Class: Sound + * ------------ + * This class encapsulates a sound file. The sound file is specified in the + * constructor and must be a file in either the current directory or a + * subdirectory named <code>sounds</code>. + * + * <p>The following code, for example, plays the sound file + * <code>ringtone.wav</code>: + * + *<pre> + * Sound ringtone("ringtone.wav"); + * ringtone.play(); + *</pre> + */ + +class Sound { + +public: + +/* + * Constructor: Sound + * Usage: Sound sound; + * Sound sound(filename); + * ----------------------------- + * Creates a <code>Sound</code> object. The default constructor + * creates an empty sound that cannot be played. The second form + * initializes the sound by reading in the contents of the specified + * file. + */ + + Sound(std::string filename); + +/* + * Destructor: ~Sound + * ------------------ + * Frees the memory associated with the sound. + */ + + virtual ~Sound(); + +/* + * Method: play + * Usage: sound.play(); + * -------------------- + * Starts playing the sound. This call returns immediately without waiting + * for the sound to finish. + */ + + void play(); + +}; + +#endif diff --git a/labb8/lib/StanfordCPPLib/stack.h b/labb8/lib/StanfordCPPLib/stack.h new file mode 100755 index 0000000..21d5058 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/stack.h @@ -0,0 +1,285 @@ +/* + * File: stack.h + * ------------- + * This file exports the <code>Stack</code> class, which implements + * a collection that processes values in a last-in/first-out (LIFO) order. + */ + +#ifndef _stack_h +#define _stack_h + +#include "vector.h" + +/* + * Class: Stack<ValueType> + * ----------------------- + * This class models a linear structure called a <b><i>stack</i></b> + * in which values are added and removed only from one end. + * This discipline gives rise to a last-in/first-out behavior (LIFO) + * that is the defining feature of stacks. The fundamental stack + * operations are <code>push</code> (add to top) and <code>pop</code> + * (remove from top). + */ + +template <typename ValueType> +class Stack { + +public: + +/* + * Constructor: Stack + * Usage: Stack<ValueType> stack; + * ------------------------------ + * Initializes a new empty stack. + */ + + Stack(); + +/* + * Destructor: ~Stack + * ------------------ + * Frees any heap storage associated with this stack. + */ + + virtual ~Stack(); + +/* + * Method: size + * Usage: int n = stack.size(); + * ---------------------------- + * Returns the number of values in this stack. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (stack.isEmpty()) ... + * ------------------------------- + * Returns <code>true</code> if this stack contains no elements. + */ + + bool isEmpty() const; + +/* + * Method: clear + * Usage: stack.clear(); + * --------------------- + * Removes all elements from this stack. + */ + + void clear(); + +/* + * Method: push + * Usage: stack.push(value); + * ------------------------- + * Pushes the specified value onto this stack. + */ + + void push(ValueType value); + +/* + * Method: pop + * Usage: ValueType top = stack.pop(); + * ----------------------------------- + * Removes the top element from this stack and returns it. This + * method signals an error if called on an empty stack. + */ + + ValueType pop(); + +/* + * Method: peek + * Usage: ValueType top = stack.peek(); + * ------------------------------------ + * Returns the value of top element from this stack, without removing + * it. This method signals an error if called on an empty stack. For + * compatibility with the STL classes, this method is also exported + * under the name <code>top</code>, in which case it returns the value + * by reference. + */ + + ValueType peek() const; + ValueType & top(); + +/* + * Method: toString + * Usage: string str = stack.toString(); + * ------------------------------------- + * Converts the stack to a printable string representation. + */ + + std::string toString(); + +/* + * Operator: == + * Usage: stack1 == stack2 + * ------------------- + * Returns <code>true</code> if <code>stack1</code> and <code>stack2</code> + * contain the same elements. + */ + + bool operator==(const Stack & stack2) const; + +/* + * Operator: != + * Usage: stack1 != stack2 + * ------------------- + * Returns <code>true</code> if <code>stack1</code> and <code>stack2</code> + * do not contain the same elements. + */ + + bool operator!=(const Stack & stack2) const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Implementation notes: Stack data structure + * ------------------------------------------ + * The easiest way to implement a stack is to store the elements in a + * Vector. Doing so means that the problems of dynamic memory allocation + * and copy assignment are already solved by the implementation of the + * underlying Vector class. + */ + +private: + Vector<ValueType> elements; + +}; + +extern void error(std::string msg); + +/* + * Stack class implementation + * -------------------------- + * The Stack is internally managed using a Vector. This layered design + * makes the implementation extremely simple, to the point that most + * methods can be implemented in as single line. + */ + +template <typename ValueType> +Stack<ValueType>::Stack() { + /* Empty */ +} + +template <typename ValueType> +Stack<ValueType>::~Stack() { + /* Empty */ +} + +template <typename ValueType> +int Stack<ValueType>::size() const { + return elements.size(); +} + +template <typename ValueType> +bool Stack<ValueType>::isEmpty() const { + return size() == 0; +} + +template <typename ValueType> +void Stack<ValueType>::push(ValueType value) { + elements.add(value); +} + +template <typename ValueType> +ValueType Stack<ValueType>::pop() { + if (isEmpty()) error("pop: Attempting to pop an empty stack"); + ValueType top = elements[elements.size() - 1]; + elements.remove(elements.size() - 1); + return top; +} + +template <typename ValueType> +ValueType Stack<ValueType>::peek() const { + if (isEmpty()) error("peek: Attempting to peek at an empty stack"); + return elements.get(elements.size() - 1); +} + +template <typename ValueType> +ValueType & Stack<ValueType>::top() { + if (isEmpty()) error("top: Attempting to read top of an empty stack"); + return elements[elements.size() - 1]; +} + +template <typename ValueType> +void Stack<ValueType>::clear() { + elements.clear(); +} + +template <typename ValueType> +std::string Stack<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +template <typename ValueType> +bool Stack<ValueType>::operator==(const Stack & stack2) const { + if (size() != stack2.size()) return false; + for (int i = 0; i < size(); i++) { + if (this->elements[i] != stack2.elements[i]) { + return false; + } + } + return true; +} + +template <typename ValueType> +bool Stack<ValueType>::operator!=(const Stack & stack2) const { + return !(*this == stack2); +} + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, const Stack<ValueType> & stack) { + os << "{"; + Stack<ValueType> copy = stack; + Stack<ValueType> reversed; + while (!copy.isEmpty()) { + reversed.push(copy.pop()); + } + int len = stack.size(); + for (int i = 0; i < len; i++) { + if (i > 0) os << ", "; + writeGenericValue(os, reversed.pop(), true); + } + return os << "}"; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, Stack<ValueType> & stack) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + stack.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + ValueType value; + readGenericValue(is, value); + stack.push(value); + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +// hashing functions for stacks; defined in hashmap.cpp +int hashCode(const Stack<std::string>& s); +int hashCode(const Stack<int>& s); +int hashCode(const Stack<char>& s); +int hashCode(const Stack<long>& s); +int hashCode(const Stack<double>& s); + +#endif diff --git a/labb8/lib/StanfordCPPLib/startup.cpp b/labb8/lib/StanfordCPPLib/startup.cpp new file mode 100755 index 0000000..d6d3ff8 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/startup.cpp @@ -0,0 +1,34 @@ +/* + * File: startup.cpp + * ----------------- + * This file implements the wrapper for main that allows for + * system initialization and error handling. + */ + +#include <cstdlib> +#include <iostream> +#include "error.h" +using namespace std; + +#if defined (_MSC_VER) && (_MSC_VER >= 1200) +# include <windows.h> +# define MSC_ERROR_FLAGS (MB_OK | MB_ICONSTOP | MB_TOPMOST) +#endif + +/* Global flag word indicating option for main */ + +int _mainFlags; + +int mainWrapper(int argc, char **argv) { + extern int Main(int argc, char **argv); + try { + return Main(argc, argv); + } catch (ErrorException & ex) { + string msg = "Error: " + ex.getMessage(); + cerr << msg << endl; +#ifdef _MSC_VER + MessageBoxA(NULL, msg.c_str(), "Error!", MSC_ERROR_FLAGS); +#endif + return EXIT_FAILURE; + } +} diff --git a/labb8/lib/StanfordCPPLib/strlib.cpp b/labb8/lib/StanfordCPPLib/strlib.cpp new file mode 100755 index 0000000..7e53235 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/strlib.cpp @@ -0,0 +1,252 @@ +/* + * File: strlib.cpp + * ---------------- + * This file implements the strlib.h interface. + */ + +#include <cctype> +#include <iomanip> +#include <iostream> +#include <sstream> +#include "error.h" +#include "strlib.h" +using namespace std; + +/* Function prototypes */ + +/* + * Implementation notes: numeric conversion + * ---------------------------------------- + * These functions use the <sstream> library to perform the conversion. + */ + +string integerToString(int n) { + ostringstream stream; + stream << n; + return stream.str(); +} + +int stringToInteger(string str) { + istringstream stream(str); + int value; + stream >> value >> ws; + if (stream.fail() || !stream.eof()) { + error("stringToInteger: Illegal integer format (" + str + ")"); + } + return value; +} + +string realToString(double d) { + ostringstream stream; + stream << uppercase << d; + return stream.str(); +} + +double stringToReal(string str) { + istringstream stream(str); + double value; + stream >> value >> ws; + if (stream.fail() || !stream.eof()) { + error("stringToReal: Illegal floating-point format (" + str + ")"); + } + return value; +} + +/* + * Implementation notes: case conversion + * ------------------------------------- + * The functions toUpperCase and toLowerCase return a new string whose + * characters appear in the desired case. These implementations rely on + * the fact that the characters in the string are copied when the + * argument is passed to the function, which makes it possible to change + * the case of the copy without affecting the original. + */ + +string toUpperCase(string str) { + int nChars = str.length(); + for (int i = 0; i < nChars; i++) { + str[i] = toupper(str[i]); + } + return str; +} + +string toLowerCase(string str) { + int nChars = str.length(); + for (int i = 0; i < nChars; i++) { + str[i] = tolower(str[i]); + } + return str; +} + +/* + * Implementation notes: equalsIgnoreCase + * -------------------------------------- + * This implementation uses a for loop to cycle through the characters in + * each string. Converting each string to uppercase and then comparing + * the results makes for a shorter but less efficient implementation. + */ + +bool equalsIgnoreCase(string s1, string s2) { + if (s1.length() != s2.length()) return false; + int nChars = s1.length(); + for (int i = 0; i < nChars; i++) { + if (tolower(s1[i]) != tolower(s2[i])) return false; + } + return true; +} + +/* + * Implementation notes: startsWith, endsWith + * ------------------------------------------ + * These implementations are overloaded to allow the second argument to + * be either a string or a character. + */ + +bool startsWith(string str, string prefix) { + if (str.length() < prefix.length()) return false; + int nChars = prefix.length(); + for (int i = 0; i < nChars; i++) { + if (str[i] != prefix[i]) return false; + } + return true; +} + +bool startsWith(string str, char prefix) { + return str.length() > 0 && str[0] == prefix; +} + +bool endsWith(string str, string suffix) { + int nChars = suffix.length(); + int start = str.length() - nChars; + if (start < 0) return false; + for (int i = 0; i < nChars; i++) { + if (str[start + i] != suffix[i]) return false; + } + return true; +} + +bool endsWith(string str, char suffix) { + return str.length() > 0 && str[str.length() - 1] == suffix; +} + +string trim(string str) { + int finish = str.length() - 1; + while (finish >= 0 && isspace(str[finish])) { + finish--; + } + int start = 0; + while (start <= finish && isspace(str[start])) { + start++; + } + return str.substr(start, finish - start + 1); +} + +/* + * Implementation notes: readQuotedString and writeQuotedString + * ------------------------------------------------------------ + * Most of the work in these functions has to do with escape sequences. + */ + +static const string STRING_DELIMITERS = ",:)}]\n"; + +bool stringNeedsQuoting(const string & str) { + int n = str.length(); + for (int i = 0; i < n; i++) { + char ch = str[i]; + if (isspace(ch)) return false; + if (STRING_DELIMITERS.find(ch) != string::npos) return true; + } + return false; +} + +void readQuotedString(istream & is, string & str) { + str = ""; + char ch; + while (is.get(ch) && isspace(ch)) { + /* Empty */ + } + if (is.fail()) return; + if (ch == '\'' || ch == '"') { + char delim = ch; + while (is.get(ch) && ch != delim) { + if (is.fail()) error("Unterminated string"); + if (ch == '\\') { + if (!is.get(ch)) error("Unterminated string"); + if (isdigit(ch) || ch == 'x') { + int maxDigits = 3; + int base = 8; + if (ch == 'x') { + base = 16; + maxDigits = 2; + } + int result = 0; + int digit = 0; + for (int i = 0; i < maxDigits && ch != delim; i++) { + if (isdigit(ch)) { + digit = ch - '0'; + } else if (base == 16 && isxdigit(ch)) { + digit = toupper(ch) - 'A' + 10; + } else { + break; + } + result = base * result + digit; + if (!is.get(ch)) error("Unterminated string"); + } + ch = char(result); + is.unget(); + } else { + switch (ch) { + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case '"': ch = '"'; break; + case '\'': ch = '\''; break; + case '\\': ch = '\\'; break; + } + } + } + str += ch; + } + } else { + str += ch; + int endTrim = 0; + while (is.get(ch) && STRING_DELIMITERS.find(ch) == string::npos) { + str += ch; + if (!isspace(ch)) endTrim = str.length(); + } + if (is) is.unget(); + str = str.substr(0, endTrim); + } +} + +void writeQuotedString(ostream & os, const string & str, bool forceQuotes) { + if (!forceQuotes && stringNeedsQuoting(str)) forceQuotes = true; + if (forceQuotes) os << '"'; + int len = str.length(); + for (int i = 0; i < len; i++) { + char ch = str.at(i); + switch (ch) { + case '\a': os << "\\a"; break; + case '\b': os << "\\b"; break; + case '\f': os << "\\f"; break; + case '\n': os << "\\n"; break; + case '\r': os << "\\r"; break; + case '\t': os << "\\t"; break; + case '\v': os << "\\v"; break; + case '\\': os << "\\\\"; break; + default: + if (isprint(ch) && ch != '"') { + os << ch; + } else { + ostringstream oss; + oss << oct << setw(3) << setfill('0') << (int(ch) & 0xFF); + os << "\\" << oss.str(); + } + } + } + if (forceQuotes) os << '"'; +} diff --git a/labb8/lib/StanfordCPPLib/strlib.h b/labb8/lib/StanfordCPPLib/strlib.h new file mode 100755 index 0000000..bb27629 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/strlib.h @@ -0,0 +1,222 @@ +/* + * File: strlib.h + * -------------- + * This file exports several useful string functions that are not + * included in the C++ string library. + */ + +#ifndef _strlib_h +#define _strlib_h + +#include <iostream> +#include <sstream> +#include <string> + +/* + * Function: integerToString + * Usage: string s = integerToString(n); + * ------------------------------------- + * Converts an integer into the corresponding string of digits. + * For example, calling <code>integerToString(123)</code> returns + * the string <code>"123"</code>. + */ + +std::string integerToString(int n); + +/* + * Function: stringToInteger + * Usage: int n = stringToInteger(str); + * ------------------------------------ + * Converts a string of digits into an integer. If the string is not a + * legal integer or contains extraneous characters other than whitespace, + * <code>stringToInteger</code> calls <code>error</code> with an + * appropriate message. + */ + +int stringToInteger(std::string str); + +/* + * Function: realToString + * Usage: string s = realToString(d); + * ---------------------------------- + * Converts a floating-point number into the corresponding string form. + * For example, calling <code>realToString(23.45)</code> returns + * the string <code>"23.45"</code>. + */ + +std::string realToString(double d); + +/* + * Function: stringToReal + * Usage: double d = stringToReal(str); + * ------------------------------------ + * Converts a string representing a real number into its corresponding + * value. If the string is not a legal floating-point number or contains + * extraneous characters other than whitespace, <code>stringToReal</code> + * calls <code>error</code> with an appropriate message. + */ + +double stringToReal(std::string str); + +/* + * Function: toUpperCase + * Usage: string s = toUpperCase(str); + * ----------------------------------- + * Returns a new string in which all lowercase characters have been converted + * into their uppercase equivalents. + */ + +std::string toUpperCase(std::string str); + +/* + * Function: toLowerCase + * Usage: string s = toLowerCase(str); + * ----------------------------------- + * Returns a new string in which all uppercase characters have been converted + * into their lowercase equivalents. + */ + +std::string toLowerCase(std::string str); + +/* + * Function: equalsIgnoreCase + * Usage: if (equalsIgnoreCase(s1, s2)) ... + * ---------------------------------------- + * Returns <code>true</code> if <code>s1</code> and <code>s2</code> are + * equal discounting differences in case. + */ + +bool equalsIgnoreCase(std::string s1, std::string s2); + +/* + * Function: startsWith + * Usage: if (startsWith(str, prefix)) ... + * --------------------------------------- + * Returns <code>true</code> if the string <code>str</code> starts with + * the specified prefix, which may be either a string or a character. + */ + +bool startsWith(std::string str, std::string prefix); +bool startsWith(std::string str, char prefix); + +/* + * Function: endsWith + * Usage: if (endsWith(str, suffix)) ... + * ------------------------------------- + * Returns <code>true</code> if the string <code>str</code> ends with + * the specified suffix, which may be either a string or a character. + */ + +bool endsWith(std::string str, std::string suffix); +bool endsWith(std::string str, char suffix); + +/* + * Function: trim + * Usage: string trimmed = trim(str); + * ---------------------------------- + * Returns a new string after removing any whitespace characters + * from the beginning and end of the argument. + */ + +std::string trim(std::string str); + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +/* + * Friend function: writeQuotedString + * Usage: writeQuotedString(outfile, str, forceQuotes); + * ---------------------------------------------------- + * Writes the string str to outfile surrounded by double quotes, converting + * special characters to escape sequences, as necessary. If the optional + * parameter forceQuotes is explicitly set to false, quotes are included + * in the output only if necessary. + */ + +void writeQuotedString(std::ostream & os, const std::string & str, + bool forceQuotes = true); + +/* + * Friend function: readQuotedString + * Usage: readQuotedString(infile, str); + * ------------------------------------- + * Reads the next string from infile into the reference parameter str. + * If the first character (other than whitespace) is either a single + * or a double quote, this function reads characters up to the + * matching quote, processing standard escape sequences as it goes. + * If not, readString reads characters up to any of the characters + * in the string STRING_DELIMITERS in the implementation file. + */ + +void readQuotedString(std::istream & is, std::string & str); + +/* + * Friend function: stringNeedsQuoting + * Usage: if (stringNeedsQuoting(str)) ... + * --------------------------------------- + * Checks whether the string needs quoting in order to be read correctly. + */ + +bool stringNeedsQuoting(const std::string & str); + +/* + * Friend function: writeGenericValue + * Usage: writeGenericValue(os, value, forceQuotes); + * ------------------------------------------------- + * Writes a generic value to the output stream. If that value is a string, + * this function uses writeQuotedString to write the value. + */ + +template <typename ValueType> +void writeGenericValue(std::ostream & os, const ValueType & value, + bool) { + os << value; +} + +template <> +inline void writeGenericValue(std::ostream & os, const std::string & value, + bool forceQuotes) { + writeQuotedString(os, value, forceQuotes); +} + +template <typename ValueType> +inline std::string genericValueToString(const ValueType & value, + bool forceQuotes = false) { + std::ostringstream os; + writeGenericValue(os, value, forceQuotes); + return os.str(); +} + +template <> +inline std::string genericValueToString(const std::string & value, + bool forceQuotes) { + std::ostringstream os; + writeQuotedString(os, value, forceQuotes); + return os.str(); +} + + +/* + * Friend function: readGenericValue + * Usage: readGenericValue(is, value); + * ----------------------------------- + * Reads a generic value from the input stream. If that value is a string, + * this function uses readQuotedString to read the value. + */ + +template <typename ValueType> +void readGenericValue(std::istream & is, ValueType & value) { + is >> value; +} + +template <> +inline void readGenericValue(std::istream & is, std::string & value) { + readQuotedString(is, value); +} + + +#endif diff --git a/labb8/lib/StanfordCPPLib/tokenscanner.cpp b/labb8/lib/StanfordCPPLib/tokenscanner.cpp new file mode 100755 index 0000000..6dadd0e --- /dev/null +++ b/labb8/lib/StanfordCPPLib/tokenscanner.cpp @@ -0,0 +1,411 @@ +/* + * File: tokenscanner.cpp + * ---------------------- + * Implementation for the TokenScanner class. + */ + +#include <cctype> +#include <iostream> +#include "error.h" +#include "tokenscanner.h" +#include "strlib.h" +#include "stack.h" +using namespace std; + +TokenScanner::TokenScanner() { + initScanner(); + setInput(""); +} + +TokenScanner::TokenScanner(string str) { + initScanner(); + setInput(str); +} + +TokenScanner::TokenScanner(istream & infile) { + initScanner(); + setInput(infile); +} + +TokenScanner::~TokenScanner() { + if (stringInputFlag) delete isp; +} + +void TokenScanner::setInput(string str) { + stringInputFlag = true; + buffer = str; + isp = new istringstream(buffer); + savedTokens = NULL; +} + +void TokenScanner::setInput(istream & infile) { + stringInputFlag = false; + isp = &infile; + savedTokens = NULL; +} + +bool TokenScanner::hasMoreTokens() { + string token = nextToken(); + saveToken(token); + return (token != ""); +} + +string TokenScanner::nextToken() { + if (savedTokens != NULL) { + StringCell *cp = savedTokens; + string token = cp->str; + savedTokens = cp->link; + delete cp; + return token; + } + while (true) { + if (ignoreWhitespaceFlag) skipSpaces(); + int ch = isp->get(); + if (ch == '/' && ignoreCommentsFlag) { + ch = isp->get(); + if (ch == '/') { + while (true) { + ch = isp->get(); + if (ch == '\n' || ch == '\r' || ch == EOF) break; + } + continue; + } else if (ch == '*') { + int prev = EOF; + while (true) { + ch = isp->get(); + if (ch == EOF || (prev == '*' && ch == '/')) break; + prev = ch; + } + continue; + } + if (ch != EOF) isp->unget(); + ch = '/'; + } + if (ch == EOF) return ""; + if ((ch == '"' || ch == '\'') && scanStringsFlag) { + isp->unget(); + return scanString(); + } + if (isdigit(ch) && scanNumbersFlag) { + isp->unget(); + return scanNumber(); + } + if (isWordCharacter(ch)) { + isp->unget(); + return scanWord(); + } + string op = string(1, ch); + while (isOperatorPrefix(op)) { + ch = isp->get(); + if (ch == EOF) break; + op += ch; + } + while (op.length() > 1 && !isOperator(op)) { + isp->unget(); + op.erase(op.length() - 1, 1); + } + return op; + } +} + +void TokenScanner::saveToken(string token) { + StringCell *cp = new StringCell; + cp->str = token; + cp->link = savedTokens; + savedTokens = cp; +} + +void TokenScanner::ignoreWhitespace() { + ignoreWhitespaceFlag = true; +} + +void TokenScanner::ignoreComments() { + ignoreCommentsFlag = true; +} + +void TokenScanner::scanNumbers() { + scanNumbersFlag = true; +} + +void TokenScanner::scanStrings() { + scanStringsFlag = true; +} + +void TokenScanner::addWordCharacters(string str) { + wordChars += str; +} + +void TokenScanner::addOperator(string op) { + StringCell *cp = new StringCell; + cp->str = op; + cp->link = operators; + operators = cp; +} + +int TokenScanner::getPosition() const { + if (savedTokens == NULL) { + return int(isp->tellg()); + } else { + return int(isp->tellg()) - savedTokens->str.length(); + } + return -1; +} + +bool TokenScanner::isWordCharacter(char ch) const { + return isalnum(ch) || wordChars.find(ch) != string::npos; +}; + +void TokenScanner::verifyToken(string expected) { + string token = nextToken(); + if (token != expected) { + string msg = "Found \"" + token + "\"" + + " when expecting \"" + expected + "\""; + error(msg); + } +}; + +TokenType TokenScanner::getTokenType(string token) const { + if (token == "") return TokenType(EOF); + char ch = token[0]; + if (isspace(ch)) return SEPARATOR; + if (ch == '"' || (ch == '\'' && token.length() > 1)) return STRING; + if (isdigit(ch)) return NUMBER; + if (isWordCharacter(ch)) return WORD; + return OPERATOR; +}; + +string TokenScanner::getStringValue(string token) const { + string str = ""; + int start = 0; + int finish = token.length(); + if (finish > 1 && (token[0] == '"' || token[0] == '\'')) { + start = 1; + finish--; + } + for (int i = start; i < finish; i++) { + char ch = token[i]; + if (ch == '\\') { + ch = token[++i]; + if (isdigit(ch) || ch == 'x') { + int base = 8; + if (ch == 'x') { + base = 16; + i++; + } + int result = 0; + int digit = 0; + while (i < finish) { + ch = token[i]; + if (isdigit(ch)) { + digit = ch - '0'; + } else if (isalpha(ch)) { + digit = toupper(ch) - 'A' + 10; + } else { + digit = base; + } + if (digit >= base) break; + result = base * result + digit; + i++; + } + ch = char(result); + i--; + } else { + switch (ch) { + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case '"': ch = '"'; break; + case '\'': ch = '\''; break; + case '\\': ch = '\\'; break; + } + } + } + str += ch; + } + return str; +} + +int TokenScanner::getChar() { + return isp->get(); +} + +void TokenScanner::ungetChar(int) { + isp->unget(); +} + +/* Private methods */ + +void TokenScanner::initScanner() { + ignoreWhitespaceFlag = false; + ignoreCommentsFlag = false; + scanNumbersFlag = false; + scanStringsFlag = false; + operators = NULL; +} + +/* + * Implementation notes: skipSpaces + * -------------------------------- + * Advances the position of the scanner until the current character is + * not a whitespace character. + */ + +void TokenScanner::skipSpaces() { + while (true) { + int ch = isp->get(); + if (ch == EOF) return; + if (!isspace(ch)) { + isp->unget(); + return; + } + } +} + +/* + * Implementation notes: scanWord + * ------------------------------ + * Reads characters until the scanner reaches the end of a sequence + * of word characters. + */ + +string TokenScanner::scanWord() { + string token = ""; + while (true) { + int ch = isp->get(); + if (ch == EOF) break; + if (!isWordCharacter(ch)) { + isp->unget(); + break; + } + token += char(ch); + } + return token; +} + +/* + * Implementation notes: scanNumber + * -------------------------------- + * Reads characters until the scanner reaches the end of a legal number. + * The function operates by simulating what computer scientists + * call a finite-state machine. The program uses the variable + * <code>state</code> to record the history of the process and + * determine what characters would be legal at this point in time. + */ + +string TokenScanner::scanNumber() { + string token = ""; + NumberScannerState state = INITIAL_STATE; + while (state != FINAL_STATE) { + int ch = isp->get(); + switch (state) { + case INITIAL_STATE: + if (!isdigit(ch)) { + error("Internal error: illegal call to scanNumber"); + } + state = BEFORE_DECIMAL_POINT; + break; + case BEFORE_DECIMAL_POINT: + if (ch == '.') { + state = AFTER_DECIMAL_POINT; + } else if (ch == 'E' || ch == 'e') { + state = STARTING_EXPONENT; + } else if (!isdigit(ch)) { + if (ch != EOF) isp->unget(); + state = FINAL_STATE; + } + break; + case AFTER_DECIMAL_POINT: + if (ch == 'E' || ch == 'e') { + state = STARTING_EXPONENT; + } else if (!isdigit(ch)) { + if (ch != EOF) isp->unget(); + state = FINAL_STATE; + } + break; + case STARTING_EXPONENT: + if (ch == '+' || ch == '-') { + state = FOUND_EXPONENT_SIGN; + } else if (isdigit(ch)) { + state = SCANNING_EXPONENT; + } else { + if (ch != EOF) isp->unget(); + isp->unget(); + state = FINAL_STATE; + } + break; + case FOUND_EXPONENT_SIGN: + if (isdigit(ch)) { + state = SCANNING_EXPONENT; + } else { + if (ch != EOF) isp->unget(); + isp->unget(); + isp->unget(); + state = FINAL_STATE; + } + break; + case SCANNING_EXPONENT: + if (!isdigit(ch)) { + if (ch != EOF) isp->unget(); + state = FINAL_STATE; + } + break; + default: + state = FINAL_STATE; + break; + } + if (state != FINAL_STATE) { + token += char(ch); + } + } + return token; +} + +/* + * Implementation notes: scanString + * -------------------------------- + * Reads and returns a quoted string from the scanner, continuing until + * it scans the matching delimiter. The scanner generates an error if + * there is no closing quotation mark before the end of the input. + */ + +string TokenScanner::scanString() { + string token = ""; + char delim = isp->get(); + token += delim; + bool escape = false; + while (true) { + int ch = isp->get(); + if (ch == EOF) error("TokenScanner found unterminated string"); + if (ch == delim && !escape) break; + escape = (ch == '\\') && !escape; + token += ch; + } + return token + delim; +} + +/* + * Implementation notes: isOperator, isOperatorPrefix + * -------------------------------------------------- + * These methods search the list of operators and return true if the + * specified operator is either in the list or a prefix of an operator + * in the list, respectively. This code could be made considerably more + * efficient by implementing operators as a trie. + */ + +bool TokenScanner::isOperator(string op) { + for (StringCell *cp = operators; cp != NULL; cp = cp->link) { + if (op == cp->str) return true; + } + return false; +} + +bool TokenScanner::isOperatorPrefix(string op) { + for (StringCell *cp = operators; cp != NULL; cp = cp->link) { + if (startsWith(cp->str, op)) return true; + } + return false; +} diff --git a/labb8/lib/StanfordCPPLib/tokenscanner.h b/labb8/lib/StanfordCPPLib/tokenscanner.h new file mode 100755 index 0000000..0b2eb32 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/tokenscanner.h @@ -0,0 +1,351 @@ +/* + * File: tokenscanner.h + * -------------------- + * This file exports a <code>TokenScanner</code> class that divides + * a string into individual logical units called <b><i>tokens</i></b>. + */ + +#ifndef _tokenscanner_h +#define _tokenscanner_h + +#include <iostream> +#include <string> +#include "private/tokenpatch.h" + +/* + * Type: TokenType + * --------------- + * This enumerated type defines the values of the + * <code>getTokenType</code> method. + */ + +#ifdef _WIN32 +# define TokenType TokenTypeT +# define WORD WORD_TC +#endif + +enum TokenType { SEPARATOR, WORD, NUMBER, STRING, OPERATOR }; + +/* + * Class: TokenScanner + * ------------------- + * This class divides a string into individual tokens. The typical + * use of the <code>TokenScanner</code> class is illustrated by the + * following pattern, which reads the tokens in the string variable + * <code>input</code>: + * + *<pre> + * TokenScanner scanner(input); + * while (scanner.hasMoreTokens()) { + * string token = scanner.nextToken(); + * ... process the token ... + * } + *</pre> + * + * The <code>TokenScanner</code> class exports several additional methods + * that give clients more control over its behavior. Those methods are + * described individually in the documentation. + */ + +class TokenScanner { + +public: + +/* + * Constructor: TokenScanner + * Usage: TokenScanner scanner; + * TokenScanner scanner(str); + * TokenScanner scanner(infile); + * ------------------------------------ + * Initializes a scanner object. The initial token stream comes from + * the specified string or input stream, if supplied. The default + * constructor creates a scanner with an empty token stream. + */ + + TokenScanner(); + TokenScanner(std::string str); + TokenScanner(std::istream & infile); + +/* + * Destructor: ~TokenScanner + * ------------------------- + * Deallocates the storage associated with this scanner. + */ + + virtual ~TokenScanner(); + +/* + * Method: setInput + * Usage: scanner.setInput(str); + * scanner.setInput(infile); + * -------------------------------- + * Sets the token stream for this scanner to the specified string or + * input stream. Any previous token stream is discarded. + */ + + void setInput(std::string str); + void setInput(std::istream & infile); + +/* + * Method: hasMoreTokens + * Usage: if (scanner.hasMoreTokens()) ... + * --------------------------------------- + * Returns <code>true</code> if there are additional tokens for this + * scanner to read. + */ + + bool hasMoreTokens(); + +/* + * Method: nextToken + * Usage: token = scanner.nextToken(); + * ----------------------------------- + * Returns the next token from this scanner. If <code>nextToken</code> + * is called when no tokens are available, it returns the empty string. + */ + + std::string nextToken(); + +/* + * Method: saveToken + * Usage: scanner.saveToken(token); + * -------------------------------- + * Pushes the specified token back into this scanner's input stream. + * On the next call to <code>nextToken</code>, the scanner will return + * the saved token without reading any additional characters from the + * token stream. + */ + + void saveToken(std::string token); + +/* + * Method: getPosition + * Usage: int pos = scanner.getPosition(); + * --------------------------------------- + * Returns the current position of the scanner in the input stream. + * If <code>saveToken</code> has been called, this position corresponds + * to the beginning of the saved token. If <code>saveToken</code> is + * called more than once, <code>getPosition</code> returns -1. + */ + + int getPosition() const; + +/* + * Method: ignoreWhitespace + * Usage: scanner.ignoreWhitespace(); + * ---------------------------------- + * Tells the scanner to ignore whitespace characters. By default, + * the <code>nextToken</code> method treats whitespace characters + * (typically spaces and tabs) just like any other punctuation mark + * and returns them as single-character tokens. + * Calling + * + *<pre> + * scanner.ignoreWhitespace(); + *</pre> + * + * changes this behavior so that the scanner ignore whitespace characters. + */ + + void ignoreWhitespace(); + +/* + * Method: ignoreComments + * Usage: scanner.ignoreComments(); + * -------------------------------- + * Tells the scanner to ignore comments. The scanner package recognizes + * both the slash-star and slash-slash comment format from the C-based + * family of languages. Calling + * + *<pre> + * scanner.ignoreComments(); + *</pre> + * + * sets the parser to ignore comments. + */ + + void ignoreComments(); + +/* + * Method: scanNumbers + * Usage: scanner.scanNumbers(); + * ----------------------------- + * Controls how the scanner treats tokens that begin with a digit. By + * default, the <code>nextToken</code> method treats numbers and letters + * identically and therefore does not provide any special processing for + * numbers. Calling + * + *<pre> + * scanner.scanNumbers(); + *</pre> + * + * changes this behavior so that <code>nextToken</code> returns the + * longest substring that can be interpreted as a real number. + */ + + void scanNumbers(); + +/* + * Method: scanStrings + * Usage: scanner.scanStrings(); + * ----------------------------- + * Controls how the scanner treats tokens enclosed in quotation marks. By + * default, quotation marks (either single or double) are treated just like + * any other punctuation character. Calling + * + *<pre> + * scanner.scanStrings(); + *</pre> + * + * changes this assumption so that <code>nextToken</code> returns a single + * token consisting of all characters through the matching quotation mark. + * The quotation marks are returned as part of the scanned token so that + * clients can differentiate strings from other token types. + */ + + void scanStrings(); + +/* + * Method: addWordCharacters + * Usage: scanner.addWordCharacters(str); + * -------------------------------------- + * Adds the characters in <code>str</code> to the set of characters + * legal in a <code>WORD</code> token. For example, calling + * <code>addWordCharacters("_")</code> adds the underscore to the + * set of characters that are accepted as part of a word. + */ + + void addWordCharacters(std::string str); + +/* + * Method: isWordCharacter + * Usage: if (scanner.isWordCharacter(ch)) ... + * ------------------------------------------- + * Returns <code>true</code> if the character is valid in a word. + */ + + bool isWordCharacter(char ch) const; + +/* + * Method: addOperator + * Usage: scanner.addOperator(op); + * ------------------------------- + * Defines a new multicharacter operator. Whenever you call + * <code>nextToken</code> when the input stream contains operator + * characters, the scanner returns the longest possible operator + * string that can be read at that point. + */ + + void addOperator(std::string op); + +/* + * Method: verifyToken + * Usage: scanner.verifyToken(expected); + * ------------------------------------- + * Reads the next token and makes sure it matches the string + * <code>expected</code>. If it does not, <code>verifyToken</code> + * throws an error. + */ + + void verifyToken(std::string expected); + +/* + * Method: getTokenType + * Usage: TokenType type = scanner.getTokenType(token); + * ---------------------------------------------------- + * Returns the type of this token. This type will match one of the + * following enumerated type constants: <code>EOF</code>, + * <code>SEPARATOR</code>, <code>WORD</code>, <code>NUMBER</code>, + * <code>STRING</code>, or <code>OPERATOR</code>. + */ + +TokenType getTokenType(std::string token) const; + +/* + * Method: getChar + * Usage: int ch = scanner.getChar(); + * ---------------------------------- + * Reads the next character from the scanner input stream. + */ + +int getChar(); + +/* + * Method: ungetChar + * Usage: scanner.ungetChar(ch); + * ----------------------------- + * Pushes the character <code>ch</code> back into the scanner stream. + * The character must match the one that was read. + */ + +void ungetChar(int ch); + +/* + * Method: getStringValue + * Usage: string str = scanner.getStringValue(token); + * -------------------------------------------------- + * Returns the string value of a token. This value is formed by removing + * any surrounding quotation marks and replacing escape sequences by the + * appropriate characters. + */ + + std::string getStringValue(std::string token) const; + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +/* + * Private type: StringCell + * ------------------------ + * This type is used to construct linked lists of cells, which are used + * to represent both the stack of saved tokens and the set of defined + * operators. These types cannot use the Stack and Lexicon classes + * directly because tokenscanner.h is an extremely low-level interface, + * and doing so would create circular dependencies in the .h files. + */ + + struct StringCell { + std::string str; + StringCell *link; + }; + + enum NumberScannerState { + INITIAL_STATE, + BEFORE_DECIMAL_POINT, + AFTER_DECIMAL_POINT, + STARTING_EXPONENT, + FOUND_EXPONENT_SIGN, + SCANNING_EXPONENT, + FINAL_STATE + }; + + std::string buffer; /* The original argument string */ + std::istream *isp; /* The input stream for tokens */ + bool stringInputFlag; /* Flag indicating string input */ + bool ignoreWhitespaceFlag; /* Scanner ignores whitespace */ + bool ignoreCommentsFlag; /* Scanner ignores comments */ + bool scanNumbersFlag; /* Scanner parses numbers */ + bool scanStringsFlag; /* Scanner parses strings */ + std::string wordChars; /* Additional word characters */ + StringCell *savedTokens; /* Stack of saved tokens */ + StringCell *operators; /* List of multichar operators */ + +/* Private method prototypes */ + + void initScanner(); + void skipSpaces(); + std::string scanWord(); + std::string scanNumber(); + std::string scanString(); + bool isOperator(std::string op); + bool isOperatorPrefix(std::string op); + +}; + +#endif diff --git a/labb8/lib/StanfordCPPLib/vector.h b/labb8/lib/StanfordCPPLib/vector.h new file mode 100755 index 0000000..9781daa --- /dev/null +++ b/labb8/lib/StanfordCPPLib/vector.h @@ -0,0 +1,727 @@ +/* + * File: vector.h + * -------------- + * This file exports the <code>Vector</code> class, which provides an + * efficient, safe, convenient replacement for the array type in C++. + */ + +#ifndef _vector_h +#define _vector_h + +#include <iterator> +#include <iostream> +#include <sstream> +#include <string> +#include "foreach.h" +#include "strlib.h" + +/* + * Class: Vector<ValueType> + * ------------------------ + * This class stores an ordered list of values similar to an array. + * It supports traditional array selection using square brackets, but + * also supports inserting and deleting elements. It is similar in + * function to the STL <code>vector</code> type, but is simpler both + * to use and to implement. + */ + +template <typename ValueType> +class Vector { + +public: + +/* + * Constructor: Vector + * Usage: Vector<ValueType> vec; + * Vector<ValueType> vec(n, value); + * --------------------------------------- + * Initializes a new vector. The default constructor creates an + * empty vector. The second form creates an array with <code>n</code> + * elements, each of which is initialized to <code>value</code>; + * if <code>value</code> is missing, the elements are initialized + * to the default value for the type. + */ + + Vector(); + explicit Vector(int n, ValueType value = ValueType()); + +/* + * Destructor: ~Vector + * ------------------- + * Frees any heap storage allocated by this vector. + */ + + virtual ~Vector(); + +/* + * Method: size + * Usage: int nElems = vec.size(); + * ------------------------------- + * Returns the number of elements in this vector. + */ + + int size() const; + +/* + * Method: isEmpty + * Usage: if (vec.isEmpty()) ... + * ----------------------------- + * Returns <code>true</code> if this vector contains no elements. + */ + + bool isEmpty() const; + +/* + * Method: clear + * Usage: vec.clear(); + * ------------------- + * Removes all elements from this vector. + */ + + void clear(); + +/* + * Method: get + * Usage: ValueType val = vec.get(index); + * -------------------------------------- + * Returns the element at the specified index in this vector. This + * method signals an error if the index is not in the array range. + */ + + const ValueType & get(int index) const; + +/* + * Method: set + * Usage: vec.set(index, value); + * ----------------------------- + * Replaces the element at the specified index in this vector with + * a new value. The previous value at that index is overwritten. + * This method signals an error if the index is not in the array range. + */ + + void set(int index, const ValueType & value); + +/* + * Method: insert + * Usage: vec.insert(0, value); + * ---------------------------- + * Inserts the element into this vector before the specified index. + * All subsequent elements are shifted one position to the right. This + * method signals an error if the index is outside the range from 0 + * up to and including the length of the vector. + */ + + void insert(int index, ValueType value); + +/* + * Method: remove + * Usage: vec.remove(index); + * ------------------------- + * Removes the element at the specified index from this vector. + * All subsequent elements are shifted one position to the left. This + * method signals an error if the index is outside the array range. + */ + + void remove(int index); + +/* + * Method: add + * Usage: vec.add(value); + * ---------------------- + * Adds a new value to the end of this vector. To ensure compatibility + * with the <code>vector</code> class in the Standard Template Library, + * this method is also called <code>push_back</code>. + */ + + void add(ValueType value); + void push_back(ValueType value); + +/* + * Operator: [] + * Usage: vec[index] + * ----------------- + * Overloads <code>[]</code> to select elements from this vector. + * This extension enables the use of traditional array notation to + * get or set individual elements. This method signals an error if + * the index is outside the array range. The file supports two + * versions of this operator, one for <code>const</code> vectors and + * one for mutable vectors. + */ + + ValueType & operator[](int index); + const ValueType & operator[](int index) const; + +/* + * Operator: + + * Usage: v1 + v2 + * -------------- + * Concatenates two vectors. + */ + + Vector operator+(const Vector & v2) const; + +/* + * Operator: += + * Usage: v1 += v2; + * v1 += value; + * ------------------- + * Adds all of the elements from <code>v2</code> (or the single + * specified value) to <code>v1</code>. As a convenience, the + * <code>Vector</code> package also overloads the comma operator so + * that it is possible to initialize a vector like this: + * + *<pre> + * Vector<int> digits; + * digits += 0, 1, 2, 3, 4, 5, 6, 7, 8, 9; + *</pre> + */ + + Vector & operator+=(const Vector & v2); + Vector & operator+=(const ValueType & value); + + bool operator==(const Vector & v2); + bool operator!=(const Vector & v2); + +/* + * Method: toString + * Usage: string str = vec.toString(); + * ----------------------------------- + * Converts the vector to a printable string representation. + */ + + std::string toString(); + +/* + * Method: mapAll + * Usage: vec.mapAll(fn); + * ---------------------- + * Calls the specified function on each element of the vector in + * ascending index order. + */ + + void mapAll(void (*fn)(ValueType)) const; + void mapAll(void (*fn)(const ValueType &)) const; + + template <typename FunctorType> + void mapAll(FunctorType fn) const; + +/* + * Additional Vector operations + * ---------------------------- + * In addition to the methods listed in this interface, the Vector + * class supports the following operations: + * + * - Stream I/O using the << and >> operators + * - Deep copying for the copy constructor and assignment operator + * - Iteration using the range-based for statement or STL iterators + * + * The iteration forms process the Vector in index order. + */ + +/* Private section */ + +/**********************************************************************/ +/* Note: Everything below this point in the file is logically part */ +/* of the implementation and should not be of interest to clients. */ +/**********************************************************************/ + +private: + +/* + * Implementation notes: Vector data structure + * ------------------------------------------- + * The elements of the Vector are stored in a dynamic array of + * the specified element type. If the space in the array is ever + * exhausted, the implementation doubles the array capacity. + */ + +/* Instance variables */ + + ValueType *elements; /* A dynamic array of the elements */ + int capacity; /* The allocated size of the array */ + int count; /* The number of elements in use */ + +/* Private methods */ + + void expandCapacity(); + void deepCopy(const Vector & src); + +/* + * Hidden features + * --------------- + * The remainder of this file consists of the code required to + * support deep copying and iteration. Including these methods + * in the public interface would make that interface more + * difficult to understand for the average client. + */ + +public: + +/* + * Deep copying support + * -------------------- + * This copy constructor and operator= are defined to make a deep copy, + * making it possible to pass or return vectors by value and assign + * from one vector to another. + */ + + Vector(const Vector & src); + Vector & operator=(const Vector & src); + +/* + * Operator: , + * ----------- + * Adds an element to the vector passed as the left-hand operatand. + * This form makes it easier to initialize vectors in old versions of C++. + */ + + Vector & operator,(const ValueType & value); + +/* + * Iterator support + * ---------------- + * The classes in the StanfordCPPLib collection implement input + * iterators so that they work symmetrically with respect to the + * corresponding STL classes. + */ + + class iterator : + public std::iterator<std::random_access_iterator_tag, ValueType> { + + private: + const Vector *vp; + int index; + + public: + + iterator() { + this->vp = NULL; + } + + iterator(const iterator & it) { + this->vp = it.vp; + this->index = it.index; + } + + iterator(const Vector *vp, int index) { + this->vp = vp; + this->index = index; + } + + iterator & operator++() { + index++; + return *this; + } + + iterator operator++(int) { + iterator copy(*this); + operator++(); + return copy; + } + + iterator & operator--() { + index--; + return *this; + } + + iterator operator--(int) { + iterator copy(*this); + operator--(); + return copy; + } + + bool operator==(const iterator & rhs) { + return vp == rhs.vp && index == rhs.index; + } + + bool operator!=(const iterator & rhs) { + return !(*this == rhs); + } + + bool operator<(const iterator & rhs) { + extern void error(std::string msg); + if (vp != rhs.vp) error("Iterators are in different vectors"); + return index < rhs.index; + } + + bool operator<=(const iterator & rhs) { + extern void error(std::string msg); + if (vp != rhs.vp) error("Iterators are in different vectors"); + return index <= rhs.index; + } + + bool operator>(const iterator & rhs) { + extern void error(std::string msg); + if (vp != rhs.vp) error("Iterators are in different vectors"); + return index > rhs.index; + } + + bool operator>=(const iterator & rhs) { + extern void error(std::string msg); + if (vp != rhs.vp) error("Iterators are in different vectors"); + return index >= rhs.index; + } + + iterator operator+(const int & rhs) { + return iterator(vp, index + rhs); + } + + iterator operator+=(const int & rhs) { + index += rhs; + return *this; + } + + iterator operator-(const int & rhs) { + return iterator(vp, index - rhs); + } + + iterator operator-=(const int & rhs) { + index -= rhs; + return *this; + } + + int operator-(const iterator & rhs) { + extern void error(std::string msg); + if (vp != rhs.vp) error("Iterators are in different vectors"); + return index - rhs.index; + } + + ValueType & operator*() { + return vp->elements[index]; + } + + ValueType *operator->() { + return &vp->elements[index]; + } + + ValueType & operator[](int k) { + return vp->elements[index + k]; + } + + }; + + iterator begin() const { + return iterator(this, 0); + } + + iterator end() const { + return iterator(this, count); + } + +}; + +/* Implementation section */ + +extern void error(std::string msg); + +/* + * Implementation notes: Vector constructor and destructor + * ------------------------------------------------------- + * The constructor allocates storage for the dynamic array + * and initializes the other fields of the object. The + * destructor frees the memory used for the array. + */ + +template <typename ValueType> +Vector<ValueType>::Vector() { + count = capacity = 0; + elements = NULL; +} + +template <typename ValueType> +Vector<ValueType>::Vector(int n, ValueType value) { + count = capacity = n; + elements = (n == 0) ? NULL : new ValueType[n]; + for (int i = 0; i < n; i++) { + elements[i] = value; + } +} + +template <typename ValueType> +Vector<ValueType>::~Vector() { + if (elements != NULL) delete[] elements; +} + +/* + * Implementation notes: Vector methods + * ------------------------------------ + * The basic Vector methods are straightforward and should require + * no detailed documentation. + */ + +template <typename ValueType> +int Vector<ValueType>::size() const { + return count; +} + +template <typename ValueType> +bool Vector<ValueType>::isEmpty() const { + return count == 0; +} + +template <typename ValueType> +void Vector<ValueType>::clear() { + if (elements != NULL) delete[] elements; + count = capacity = 0; + elements = NULL; +} + +template <typename ValueType> +const ValueType & Vector<ValueType>::get(int index) const { + if (index < 0 || index >= count) error("get: index out of range"); + return elements[index]; +} + +template <typename ValueType> +void Vector<ValueType>::set(int index, const ValueType & value) { + if (index < 0 || index >= count) error("set: index out of range"); + elements[index] = value; +} + +/* + * Implementation notes: insert, remove, add + * ----------------------------------------- + * These methods must shift the existing elements in the array to + * make room for a new element or to close up the space left by a + * deleted one. + */ + +template <typename ValueType> +void Vector<ValueType>::insert(int index, ValueType value) { + if (count == capacity) expandCapacity(); + if (index < 0 || index > count) { + error("insert: index out of range"); + } + for (int i = count; i > index; i--) { + elements[i] = elements[i - 1]; + } + elements[index] = value; + count++; +} + +template <typename ValueType> +void Vector<ValueType>::remove(int index) { + if (index < 0 || index >= count) error("remove: index out of range"); + for (int i = index; i < count - 1; i++) { + elements[i] = elements[i + 1]; + } + count--; +} + +template <typename ValueType> +void Vector<ValueType>::add(ValueType value) { + insert(count, value); +} + +template <typename ValueType> +void Vector<ValueType>::push_back(ValueType value) { + insert(count, value); +} + +/* + * Implementation notes: Vector selection + * -------------------------------------- + * The following code implements traditional array selection using + * square brackets for the index. + */ + +template <typename ValueType> +ValueType & Vector<ValueType>::operator[](int index) { + if (index < 0 || index >= count) error("Selection index out of range"); + return elements[index]; +} +template <typename ValueType> +const ValueType & Vector<ValueType>::operator[](int index) const { + if (index < 0 || index >= count) error("Selection index out of range"); + return elements[index]; +} + +template <typename ValueType> +Vector<ValueType> Vector<ValueType>::operator+(const Vector & v2) const { + Vector<ValueType> vec = *this; + foreach (ValueType value in v2) { + vec.add(value); + } + return vec; +} + +template <typename ValueType> +Vector<ValueType> & Vector<ValueType>::operator+=(const Vector & v2) { + foreach (ValueType value in v2) { + *this += value; + } + return *this; +} + +template <typename ValueType> +Vector<ValueType> & Vector<ValueType>::operator+=(const ValueType & value) { + this->add(value); + return *this; +} + +template <typename ValueType> +bool Vector<ValueType>::operator==(const Vector & v2) { + if (this->size() != v2.size()) { + return false; + } + for (int i = 0, size = this->size(); i < size; i++) { + if (this->get(i) != v2.get(i)) { + return false; + } + } + return true; +} + +template <typename ValueType> +bool Vector<ValueType>::operator!=(const Vector & v2) { + return !(*this == v2); +} + +template <typename ValueType> +std::string Vector<ValueType>::toString() { + ostringstream os; + os << *this; + return os.str(); +} + +/* + * Implementation notes: copy constructor and assignment operator + * -------------------------------------------------------------- + * The constructor and assignment operators follow a standard paradigm, + * as described in the associated textbook. + */ + +template <typename ValueType> +Vector<ValueType>::Vector(const Vector & src) { + deepCopy(src); +} + +template <typename ValueType> +Vector<ValueType> & Vector<ValueType>::operator=(const Vector & src) { + if (this != &src) { + if (elements != NULL) delete[] elements; + deepCopy(src); + } + return *this; +} + +template <typename ValueType> +void Vector<ValueType>::deepCopy(const Vector & src) { + count = capacity = src.count; + elements = (capacity == 0) ? NULL : new ValueType[capacity]; + for (int i = 0; i < count; i++) { + elements[i] = src.elements[i]; + } +} + +/* + * Implementation notes: The , operator + * ------------------------------------ + * The comma operator works adding the right operand to the vector and + * then returning the vector by reference so that it is set for the next + * value in the chain. + */ + +template <typename ValueType> +Vector<ValueType> & Vector<ValueType>::operator,(const ValueType & value) { + this->add(value); + return *this; +} + +/* + * Implementation notes: mapAll + * ---------------------------- + * The various versions of the mapAll function apply the function or + * function object to each element in ascending index order. + */ + +template <typename ValueType> +void Vector<ValueType>::mapAll(void (*fn)(ValueType)) const { + for (int i = 0; i < count; i++) { + fn(elements[i]); + } +} + +template <typename ValueType> +void Vector<ValueType>::mapAll(void (*fn)(const ValueType &)) const { + for (int i = 0; i < count; i++) { + fn(elements[i]); + } +} + +template <typename ValueType> +template <typename FunctorType> +void Vector<ValueType>::mapAll(FunctorType fn) const { + for (int i = 0; i < count; i++) { + fn(elements[i]); + } +} + +/* + * Implementation notes: expandCapacity + * ------------------------------------ + * This function doubles the array capacity, copies the old elements + * into the new array, and then frees the old one. + */ + +template <typename ValueType> +void Vector<ValueType>::expandCapacity() { + capacity = max(1, capacity * 2); + ValueType *array = new ValueType[capacity]; + for (int i = 0; i < count; i++) { + array[i] = elements[i]; + } + if (elements != NULL) delete[] elements; + elements = array; +} + +/* + * Implementation notes: << and >> + * ------------------------------- + * The insertion and extraction operators use the template facilities in + * strlib.h to read and write generic values in a way that treats strings + * specially. + */ + +template <typename ValueType> +std::ostream & operator<<(std::ostream & os, const Vector<ValueType> & vec) { + os << "{"; + int len = vec.size(); + for (int i = 0; i < len; i++) { + if (i > 0) os << ", "; + writeGenericValue(os, vec[i], true); + } + return os << "}"; +} + +template <typename ValueType> +std::istream & operator>>(std::istream & is, Vector<ValueType> & vec) { + char ch; + is >> ch; + if (ch != '{') error("operator >>: Missing {"); + vec.clear(); + is >> ch; + if (ch != '}') { + is.unget(); + while (true) { + ValueType value; + readGenericValue(is, value); + vec += value; + is >> ch; + if (ch == '}') break; + if (ch != ',') { + error(std::string("operator >>: Unexpected character ") + ch); + } + } + } + return is; +} + +// hashing functions for vectors; defined in hashmap.cpp +int hashCode(const Vector<std::string>& v); +int hashCode(const Vector<int>& v); +int hashCode(const Vector<char>& v); +int hashCode(const Vector<long>& v); +int hashCode(const Vector<double>& v); + +#endif diff --git a/labb8/lib/readme.txt b/labb8/lib/readme.txt new file mode 100755 index 0000000..9d0d5c8 --- /dev/null +++ b/labb8/lib/readme.txt @@ -0,0 +1,4 @@ +This directory contains any libraries that should be
+linked to your project when it is built.
+The most common library we will link to is the
+Stanford C++ library (cpplib).
diff --git a/labb8/lib/spl.jar b/labb8/lib/spl.jar Binary files differnew file mode 100755 index 0000000..3cdc64f --- /dev/null +++ b/labb8/lib/spl.jar |
