diff options
Diffstat (limited to '20/practice/test_grader.cpp')
-rw-r--r-- | 20/practice/test_grader.cpp | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/20/practice/test_grader.cpp b/20/practice/test_grader.cpp new file mode 100644 index 0000000..b7d9162 --- /dev/null +++ b/20/practice/test_grader.cpp @@ -0,0 +1,264 @@ +#include <iostream> +#include <vector> +#include <sstream> +#include <cassert> +#include <set> +#include <algorithm> +#include <cstring> +#include <cstdio> +#include <sstream> + +#include <errno.h> +#include <signal.h> +#include <sys/select.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +using namespace std; + +#define L 1000000000000000LL + +#define RETRY(x) ({ \ + int _r = (x); \ + while (_r == -1 && errno == EINTR) { \ + _r = (x); \ + } \ + _r; \ +}) + +template<typename T> +string to_string(const T& x) { + ostringstream sout; + sout << x; + return sout.str(); +} + +pid_t start_instance(int argc, char** argv, int* pipes) { + int pipe_a[2]; + int pipe_b[2]; + if (pipe(pipe_a) == -1 || pipe(pipe_b) == -1) { + perror("pipe"); + return -1; + } + + pid_t pid = fork(); + if (pid == -1) { + perror("fork"); + return -1; + } else if (pid == 0) { + close(0); + close(1); + dup2(pipe_b[0], 0); + dup2(pipe_a[1], 1); + close(pipe_a[0]); + close(pipe_a[1]); + close(pipe_b[0]); + close(pipe_b[1]); + + char** args = (char**)malloc(sizeof(char*) * (argc + 2)); + args[0] = strdup("stdbuf"); + args[1] = strdup("-oL"); + for (int i = 1; i < argc; i++) { + args[1 + i] = argv[i]; + } + args[argc + 1] = NULL; + execvp("stdbuf", args); + perror("execvp"); + exit(1); + } + + close(pipe_a[1]); + close(pipe_b[0]); + pipes[0] = pipe_a[0]; + pipes[1] = pipe_b[1]; + return pid; +} + +int main(int argc, char** argv) { + if (argc == 1) { + cout << "USAGE:" << endl; + cout << argv[0] << " ./test_program" << endl; + return 0; + } + if (signal(SIGPIPE, SIG_IGN)) { + perror("signal"); + return 1; + } + + int nfds = 1; + char buf[1024]; + int rfd[2]; + int wfd[2]; + pid_t pids[2]; + bool alive[2] = {true, true}; + bool returned[2] = {false, false}; + string return_val[2] = {"", ""}; + for (int i = 0; i < 2; i++) { + int pipe_tmp[2]; + pids[i] = start_instance(argc, argv, pipe_tmp); + if (pids[i] == -1) { + cerr << "failed to start solution program" << endl; + return 1; + } + + rfd[i] = pipe_tmp[0]; + wfd[i] = pipe_tmp[1]; + nfds = max(nfds, max(rfd[i], wfd[i])) + 1; + } + + int ign; + vector<string > names[2]; + string in[2]; + string out[2]; + + set<string> names_set[2]; + for (int i = 0; i < 2; i++) { + int sz; + cin >> ign >> sz; + names[i].resize(sz); + + out[i] = to_string(i) + " " + to_string(sz) + "\n"; + for (int j = 0; j < sz; j++) { + cin >> names[i][j]; + assert(0 <= names[i][j].size() && names[i][j].size() <= 100); + for(int k = 0; k < names[i][j].size(); k++){ + assert('a' <= names[i][j][k] && names[i][j][k] <= 'z'); + } + + if (j) out[i] += ' '; + out[i] += names[i][j]; + } + out[i] += '\n'; + + names_set[i] = set<string>(names[i].begin(), names[i].end()); + assert(names_set[i].size() == names[i].size()); + } + vector<string> intersection; + for(auto it = names_set[0].begin(); it != names_set[0].end(); ++it){ + if(names_set[1].count(*it)){ + cerr << "COMMON NAME: " << *it << endl; + intersection.push_back(*it); + } + } + assert(intersection.size() == 1); + string answer_val = intersection[0]; + + int total_bits = 0; + for (; alive[0] || alive[1]; ) { + fd_set rfds; + fd_set wfds; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + for (int i = 0; i < 2; i++) { + if (!alive[i]) { + continue; + } + FD_SET(rfd[i], &rfds); + if (out[i].size()) { + FD_SET(wfd[i], &wfds); + } + } + + if (RETRY(select(nfds, &rfds, &wfds, NULL, NULL)) == -1) { + perror("select"); + cout << "E" << endl; + return 0; + } + + for (int i = 0; i < 2; i++) { + if (FD_ISSET(rfd[i], &rfds)) { + ssize_t amt = RETRY(read(rfd[i], buf, sizeof(buf))); + if (amt <= 0) { + close(rfd[i]); + close(wfd[i]); + alive[i] = false; + cerr << "Cow " << i << " communication closed" << endl; + continue; + } + in[i] += string(buf, amt); + cerr << "COW[" << i << "]: " << string(buf, amt) << endl; + + for(;;) { + int nline = in[i].find('\n'); + if (nline == -1) { + break; + } + string line = in[i].substr(0, nline); + in[i] = in[i].substr(nline + 1); + + istringstream sin(line); + string cmd; sin >> cmd; + if (cmd == "MOO") { + string data; sin >> data; + for (int j = 0; j < data.size(); j++) { + if (data[j] != '0' && data[j] != '1') { + cerr << "Cow " << i << " tried to moo non-binary" << endl; + return 1; + } + } + out[1 - i] += data + "\n"; + total_bits += data.size(); + } else if (cmd == "RETURN") { + string val; + sin >> val; + if (returned[i]) { + cerr << "Same cow cannot return twice" << endl; + return 1; + } else { + returned[i] = true; + return_val[i] = val; + } + } else { + cerr << "Unknown command from cow " << i << endl; + return 1; + } + } + if (in[i].size() > 7000000) { + cerr << "Line too long from cow " << i << endl; + return 1; + } + } + if (FD_ISSET(wfd[i], &wfds)) { + // cerr << "TO COW[" << i << "]: " << out[i] << endl; + ssize_t amt = RETRY(write(wfd[i], out[i].data(), out[i].size())); + if (amt <= 0) { + close(rfd[i]); + close(wfd[i]); + alive[i] = false; + cerr << "Cow " << i << " communication closed" << endl; + continue; + } else { + out[i] = out[i].substr(amt); + } + } + } + } + for (int i = 0; i < 2; i++) { + int status = 0; + if (RETRY(waitpid(pids[i], &status, 0)) == -1) { + perror("waitpid"); + return 1; + } + if (WIFSIGNALED(status)) { + cerr << "Cow " << i << " crashed" << endl; + return 1; + } + } + + for (int i = 0; i < 2; i++) { + if (returned[i] && return_val[i] != answer_val) { + cerr << "Cow " << i << " returned the wrong value" << endl; + return 1; + } + } + if (!returned[0] && !returned[1]) { + cerr << "Neither cow returned a value" << endl; + return 1; + } + + cerr << "Found correct name " << answer_val << " using " << + total_bits << " total bits." << endl; + + return 0; +} |