Project 4 Test Data

When the following program is compiled along with StudentTextEditor.cpp, StudentUndo.cpp, and StudentSpellCheck.cpp, it can be run with input 1 through 114 to test your solution. (This program uses some features of C++ we're not covering, but that's irrelevant to you.) Each test case was worth 0.8772 points

#include "TextEditor.h"
#include "Undo.h"
#include "SpellCheck.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <stack>
#include <initializer_list>
#include <algorithm>
#include <memory>
#include <chrono>
#include <cstdlib>
#include <cctype>
#include <cassert>
using namespace std;

const int NTE = 66;
const int NUN = 23;
const int NSP = 25;
const int BASETE = 0;
const int BASEUN = BASETE + NTE;
const int BASESP = BASEUN + NUN;

struct TesterUndo : public Undo
{
	struct Op
	{
		Op(Action a, int rr, int cc, string t) : act(a), r(rr), c(cc), text(t) {}
		Action act;
		int	r;
		int c;
		string text;
	};

	virtual void submit(Action action, int row, int col, char ch = 0)
	{
		undos.push(Op(action, row, col, string(1, ch)));
	}

	virtual Action get(int& row, int& col, int& count, std::string& text)
	{
		if (undos.empty())
			return Action::ERROR;
		Op op = undos.top();
		undos.pop();
		row = op.r;
		col = op.c;
		count = 1;
		text.clear();
		switch (op.act)
		{
		  case Action::INSERT:
			col--;
			count = op.text.size();
			return Action::DELETE;
		  case Action::DELETE:
			text = op.text;
			return Action::INSERT;
		  case Action::JOIN:
			return Action::SPLIT;
		  case Action::SPLIT:
			return Action::JOIN;
		  case Action::ERROR:
			return Action::ERROR; // should never happen
		}
		return Action::ERROR; // should never happen
	}

	virtual void clear()
	{
		undos = stack<Op>();
	}

	void replace(int n, int r, int c, string t)
	{
		if (n <= 0)
			return;
		Op op = undos.top();
		if (op.act != Action::INSERT  &&  op.act != Action::DELETE)
			return;
		for ( ; n > 0; n--)
		{
			op = undos.top();
			undos.pop();
		}
		undos.push(Op(op.act, r, c, t));
		
	}

	stack<Op> undos;
};

string makefilename()
{
	using namespace std::chrono;
	auto now = high_resolution_clock::now().time_since_epoch();
	auto us = duration_cast<microseconds>(now).count();
	return "p4test" + to_string(us % 1000000);
}

template<typename Ptr>
bool load(Ptr& p, string s)  // passed by ref since unique_ptr
{
	string filename = makefilename();
	{
		ofstream ofs(filename);
		if (!ofs)
		{
			cerr << "creating temp file failed!" << endl;
			return false;
		}
		ofs << s;
	}
	bool ret = p->load(filename.c_str());
	remove(filename.c_str());
	return ret;
}

bool goodsuggs(string w, vector<string> suggs, string dict)
{
	for (auto& s : suggs)
	{
		if (s.size() != w.size())
			return false;
		transform(s.begin(), s.end(), s.begin(), [](char ch) { return tolower(ch); });
		auto p = mismatch(s.begin(), s.end(), w.begin());
		if (p.first == s.end())
			return false;
		if (!islower(*p.first)  &&  *p.first != '\'')
			return false;
		if (mismatch(++p.first, s.end(), ++p.second).first != s.end())
			return false;
		auto q = dict.begin();
		for(;;)
		{
			q = search(q, dict.end(), s.begin(), s.end());
			if (q == dict.end())
				return false;
			if (*(q + s.size()) == '\n'  &&
					(q == dict.begin()  ||  *(q-1) == '\n'))
				break;
		}
	}
	sort(suggs.begin(), suggs.end());
	return adjacent_find(suggs.begin(), suggs.end()) == suggs.end();
}

bool isPerm(vector<SpellCheck::Position>& v, const initializer_list<SpellCheck::Position>& exp)
{
	if (v.size() != exp.size())
		return false;
	sort(v.begin(), v.end(), [](const auto& lhs, const auto& rhs) {
		if (lhs.start < rhs.start) return true;
		if (rhs.start < lhs.start) return false;
		return lhs.end < rhs.end;
	});
	return equal(v.begin(), v.end(), exp.begin(), [](const auto& lhs, const auto& rhs) {
		return lhs.start == rhs.start  &&  lhs.end == rhs.end;
	});
}

void testTextEditor(int n)
{
	constexpr auto UP    = TextEditor::Dir::UP;
	constexpr auto DOWN  = TextEditor::Dir::DOWN;
	constexpr auto LEFT  = TextEditor::Dir::LEFT;
	constexpr auto RIGHT = TextEditor::Dir::RIGHT;
	constexpr auto HOME  = TextEditor::Dir::HOME;
	constexpr auto END   = TextEditor::Dir::END;

	auto u = make_unique<TesterUndo>();
	auto t = unique_ptr<TextEditor>(createTextEditor(u.get()));

	int r = 999;
	int c = 999;
	vector<string> v;

	switch (n)
	{
				 default: {
	    cout << "Bad argument TE" << endl;
			} break; case BASETE +  1: {
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
			} break; case BASETE +  2: {
		int n = t->getLines(0, 10, v);
		assert((v.empty()  ||  (v.size() == 1  &&  (v[0].empty()  ||  v[0] == " "))));
			} break; case BASETE +  3: {
		assert(!t->load("/this/file/does/not/exist"));
		assert(load(t, "this is\na file\n"));
			} break; case BASETE +  4: {
		load(t, "this is\na file\n");
		t->move(RIGHT); t->move(RIGHT); t->move(DOWN);
		r = 0; c = 0;
		t->getPos(r, c);
		assert(r != 0  &&  c != 0);
		t->load("/this/file/does/not/exist");
		int rnew = 888, cnew = 888;
		t->getPos(rnew, cnew);
		assert(rnew == r  &&  cnew == c);
			} break; case BASETE +  5: {
		load(t, "this is\na file\n");
		t->getLines(0, 10, v);
		assert(v.size() == 2);
			} break; case BASETE +  6: {
		load(t, "this is\na file\n");
		t->getLines(0, 10, v);
		assert(v.size() >= 2  &&  v[0] == "this is"  &&  v[1] == "a file");
			} break; case BASETE +  7: {
		load(t, "this is\r\na file\r\n");
		t->getLines(0, 10, v);
		assert(v.size() >= 2  &&  v[0] == "this is"  &&  v[1] == "a file");
			} break; case BASETE +  8: {
		load(t, "ttt\neee\nsss\nttt\neee\nrrr\n");
		t->getLines(0, 10, v);
		size_t vszold = v.size();
		load(t, "this is\na file\n");
		v.clear();
		t->getLines(0, 10, v);
		assert(v.size() >= 2  &&  v.size() < vszold);
			} break; case BASETE +  9: {
		load(t, "ttt\neee\nsss\nttt\neee\nrrr\n");
		t->move(RIGHT); t->move(RIGHT); t->move(DOWN);
		r = 0; c = 0;
		t->getPos(r, c);
		assert(r != 0  &&  c != 0);
		load(t, "this is\na file\n");
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
			} break; case BASETE + 10: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('?');
		t->move(UP);
		assert(!t->save("/this/file/does/not/exist"));
		string filename = makefilename();
		assert(t->save(filename.c_str()));
		remove(filename.c_str());
			} break; case BASETE + 11: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('?');
		t->move(UP);
		string filename = makefilename();
		t->save(filename.c_str());
		{
			ifstream ifs(filename);
			assert(ifs);
			string s;
			getline(ifs, s, '\xbb');
			assert(s == "XY\n?\n");
		}
		remove(filename.c_str());
			} break; case BASETE + 12: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('?');
		t->getPos(r, c);
		assert(r != 0  &&  c != 0);
		t->getLines(0, 2, v);
		assert(v.size() != 0);
		t->reset();
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 2, v);
		assert(v.size() == 0);
			} break; case BASETE + 13: {
		t->insert('X');
		t->backspace();
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->backspace();
		t->reset();
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->undo();
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 0);
			} break; case BASETE + 14: {
		t->insert('X');
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->insert('Z');
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XZY");
			} break; case BASETE + 15: {
		t->insert('X');
		t->insert('\t');
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 6);
			} break; case BASETE + 16: {
		t->insert('X');
		t->insert('\t');
		t->insert('Y');
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "X    Y");
			} break; case BASETE + 17: {
		t->insert('X');
		t->move(LEFT);
		t->enter();
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == ""  &&  v[1]== "YX");
			} break; case BASETE + 18: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "X"  &&  v[1]== "Y");
			} break; case BASETE + 19: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(LEFT);
		t->move(UP);
		t->enter();
		t->getPos(r, c);
		assert(r == 2  &&  c == 0);
		t->getLines(0, 4, v);
		assert(v.size() == 4  &&  v[0] == "X"  &&  v[1]== ""  &&  v[2] == "Y"  &&  v[3] == "Z");
			} break; case BASETE + 20: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->enter();
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "X"  &&  v[1]== "Y");
			} break; case BASETE + 21: {
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		t->move(LEFT);
		t->move(LEFT);
		t->del();
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XZ");
			} break; case BASETE + 22: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(UP);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
		t->del();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZ");
			} break; case BASETE + 23: {
		t->insert('X');
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XY");
		t->del();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XY");
			} break; case BASETE + 24: {
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XYZ");
		t->backspace();
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XZ");
			} break; case BASETE + 25: {
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		t->getPos(r, c);
		assert(r == 0  &&  c == 3);
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XYZ");
		t->backspace();
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XY");
			} break; case BASETE + 26: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XY");
		t->backspace();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1 &&  v[0] == "XY");
			} break; case BASETE + 27: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 1  &&  c == 0);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
		t->backspace();
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZ");
			} break; case BASETE + 28: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->getPos(r, c);
		assert(r == 1  &&  c == 0);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "");
		t->backspace();
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		v.clear();
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 29: {
		t->insert('X');
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 30: {
		t->insert('X');
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->move(LEFT);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->move(LEFT);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 31: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->move(LEFT);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 32: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 33: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->move(RIGHT);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 34: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(UP);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 1  &&  c == 0);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 35: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->move(UP);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 36: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->move(UP);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 37: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->insert('Z');
		t->insert('W');
		t->getPos(r, c);
		assert(r == 1  &&  c == 3);
		t->move(UP);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "X"  &&  v[1] == "YZW");
			} break; case BASETE + 38: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->move(DOWN);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 39: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->insert('Z');
		t->move(LEFT);
		t->move(UP);
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->move(DOWN);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "X"  &&  v[1] == "YZ");
			} break; case BASETE + 40: {
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		t->enter();
		t->insert('W');
		t->move(UP);
		t->move(RIGHT);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 3);
		t->move(DOWN);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 1  &&  c == 1);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XYZ"  &&  v[1] == "W");
			} break; case BASETE + 41: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->insert('Z');
		t->getPos(r, c);
		assert(r == 1  &&  c == 2);
		t->move(HOME);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 1  &&  c == 0);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "X"  &&  v[1] == "YZ");
			} break; case BASETE + 42: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->insert('Z');
		t->move(LEFT);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 1  &&  c == 0);
		t->move(HOME);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 1  &&  c == 0);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "X"  &&  v[1] == "YZ");
			} break; case BASETE + 43: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(UP);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->move(END);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 44: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(UP);
		t->move(RIGHT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->move(END);
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 45: {
		t->insert('X');
		assert(t->getLines(-1, 1, v) == -1);
			} break; case BASETE + 46: {
		t->insert('X');
		v.push_back("hello");
		t->getLines(-1, 1, v);
		assert(v.size() == 1  &&  v[0] == "hello");
			} break; case BASETE + 47: {
		t->insert('X');
		assert(t->getLines(0, -1, v) == -1);
			} break; case BASETE + 48: {
		t->insert('X');
		v.push_back("hello");
		t->getLines(0, -1, v);
		assert(v.size() == 1  &&  v[0] == "hello");
			} break; case BASETE + 49: {
		t->insert('X');
		assert(t->getLines(2, 1, v) == -1);
			} break; case BASETE + 50: {
		t->insert('X');
		v.push_back("hello");
		t->getLines(2, 1, v);
		assert(v.size() == 1  &&  v[0] == "hello");
			} break; case BASETE + 51: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->getLines(0, 10, v);
		assert(v.size() == 3);
			} break; case BASETE + 52: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		int n = t->getLines(0, 10, v);
		assert(v.size() == n);
			} break; case BASETE + 53: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		int n = t->getLines(0, 3, v);
		assert(v.size() == n);
			} break; case BASETE + 54: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		v.push_back("hello");
		v.push_back("goodbye");
		int n = t->getLines(0, 3, v);
		assert(n == v.size()  &&  n == 3);
		assert(v[0] == "X"  &&  v[1] == "Y"  &&  v[2] == "Z");
			} break; case BASETE + 55: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		v.push_back("hello");
		t->getLines(3, 0, v);
		assert(v.empty());
			} break; case BASETE + 56: {
		t->insert('X');
		t->enter();
		t->insert('Y');
		t->enter();
		t->insert('Z');
		v.push_back("hello");
		t->getLines(3, 0, v);
		assert(v.empty());
			} break; case BASETE + 57: {
		load(t, "this is\na file\n");
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "this is"  &&  v[1] == "a file");
		t->undo();
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "this is"  &&  v[1] == "a file");
			} break; case BASETE + 58: {
		t->insert('X');
		t->insert('Y');
		t->backspace();
		t->insert('Y');
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
		t->undo();
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "X");
			} break; case BASETE + 59: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->del();
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "X");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 60: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(UP);
		t->move(RIGHT);
		t->del();
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZ");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 61: {
		t->insert('X');
		t->insert('Y');
		t->move(LEFT);
		t->backspace();
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "Y");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
			} break; case BASETE + 62: {
		t->insert('X');
		t->insert('Y');
		t->enter();
		t->insert('Z');
		t->move(LEFT);
		t->backspace();
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZ");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 2);
		t->getLines(0, 2, v);
		assert(v.size() == 2  &&  v[0] == "XY"  &&  v[1] == "Z");
			} break; case BASETE + 63: {
		t->insert('X');
		t->insert('Y');
		t->backspace();
		t->insert('Y');
		t->move(LEFT);
		t->move(LEFT);
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XY");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "X");
			} break; case BASETE + 64: {
		t->insert('W');
		t->move(LEFT);
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		u->replace(3, 0, 1, "XYZ");
		t->getPos(r, c);
		assert(r == 0  &&  c == 3);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZW");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 0);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "W");
			} break; case BASETE + 65: {
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		t->insert('W');
		t->move(LEFT);
		t->move(LEFT);
		t->move(LEFT);
		t->del();
		t->del();
		t->del();
		u->replace(3, 0, 1, "YZW");
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "X");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZW");
			} break; case BASETE + 66: {
		t->insert('X');
		t->insert('Y');
		t->insert('Z');
		t->insert('W');
		t->backspace();
		t->backspace();
		t->backspace();
		u->replace(3, 0, 1, "YZW");
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "X");
		t->undo();
		r = 999; c = 999;
		t->getPos(r, c);
		assert(r == 0  &&  c == 1);
		t->getLines(0, 1, v);
		assert(v.size() == 1  &&  v[0] == "XYZW");
			}
	}
}

void testUndo(int n)
{
	constexpr auto ERR = Undo::Action::ERROR;
	constexpr auto INS = Undo::Action::INSERT;
	constexpr auto DEL = Undo::Action::DELETE;
	constexpr auto JOI = Undo::Action::JOIN;
	constexpr auto SPL = Undo::Action::SPLIT;

	auto u = unique_ptr<Undo>(createUndo());

	int r = 999;
	int c = 999;
	int ct = 999;
	string t = "ZZZZ";

 
	switch (n)
	{
				 default: {
	    cout << "Bad argument UN" << endl;
			} break; case BASEUN +  1: {
		assert(u->get(r, c, ct, t) == ERR);
			} break; case BASEUN +  2: {
		u->get(r, c, ct, t);
		assert(r == 999  &&  c == 999  &&  ct == 999  &&  t == "ZZZZ");
			} break; case BASEUN +  3: {
		u->clear();
		assert(u->get(r, c, ct, t) == ERR);
			} break; case BASEUN +  4: {
		u->submit(INS, 1, 1, 'k');
		u->submit(DEL, 2, 2, 'k');
		u->submit(JOI, 3, 3, 'k');
		assert(u->get(r, c, ct, t) != ERR);
		u->clear();
		assert(u->get(r, c, ct, t) == ERR);
			} break; case BASEUN +  5: {
		assert(u->get(r, c, ct, t) == ERR);
		u->submit(INS, 1, 1, 'k');
		u->submit(DEL, 2, 2, 'k');
		u->submit(INS, 3, 3, 'k');
		assert(u->get(r, c, ct, t) != ERR);
		assert(u->get(r, c, ct, t) != ERR);
		assert(u->get(r, c, ct, t) != ERR);
		assert(u->get(r, c, ct, t) == ERR);
			} break; case BASEUN +  6: {
		u->submit(INS, 10, 20, 'k');
		assert(u->get(r, c, ct, t) == DEL);
			} break; case BASEUN +  7: {
		u->submit(INS, 10, 20, 'k');
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 20-1  &&  ct == 1  &&  t.empty());
			} break; case BASEUN +  8: {
		u->submit(DEL, 10, 20, 'k');
		assert(u->get(r, c, ct, t) == INS);
			} break; case BASEUN +  9: {
		u->submit(DEL, 10, 20, 'k');
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 20  &&  ct == 1  &&  t == "k");
			} break; case BASEUN + 10: {
		u->submit(JOI, 10, 20, 'k');
		assert(u->get(r, c, ct, t) == SPL);
			} break; case BASEUN + 11: {
		u->submit(JOI, 10, 20, 'k');
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 20  &&  ct == 1  &&  t.empty());
			} break; case BASEUN + 12: {
		u->submit(SPL, 10, 20, 'k');
		assert(u->get(r, c, ct, t) == JOI);
			} break; case BASEUN + 13: {
		u->submit(SPL, 10, 20, 'k');
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 20  &&  ct == 1  &&  t.empty());
			} break; case BASEUN + 14: {
		u->submit(DEL, 10, 20, 'k');
		u->submit(INS, 10, 21, 'd');
		u->submit(INS, 10, 22, 'o');
		u->submit(INS, 10, 23, 'g');
		u->submit(DEL, 10, 24, 'm');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == DEL  &&  u->get(r, c, ct, t) == INS);
			} break; case BASEUN + 15: {
		u->submit(DEL, 10, 20, 'k');
		u->submit(INS, 10, 21, 'd');
		u->submit(INS, 10, 22, 'o');
		u->submit(INS, 10, 23, 'g');
		u->submit(DEL, 10, 24, 'm');
		u->get(r, c, ct, t);
		r = 999; c = 999; ct = 999; t = "ZZZZ";
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 21-1  &&  ct == 3  &&  t.empty());
			} break; case BASEUN + 16: {
		u->submit(DEL, 10, 19, 'k');
		u->submit(INS, 9, 20, 'x');
		u->submit(INS, 10, 21, 'd');
		u->submit(INS, 10, 22, 'o');
		u->submit(INS, 10, 22, 'g');
		u->submit(INS, 10, 23, 'm');
		u->submit(INS, 10, 24, 'a');
		u->submit(DEL, 10, 25, 'n');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == DEL  &&  ct == 3);
		ct = 999;
		assert(u->get(r, c, ct, t) == DEL  &&  ct == 2);
			} break; case BASEUN + 17: {
		u->submit(INS, 10, 20, 'k');
		u->submit(DEL, 10, 20, 'd');
		u->submit(DEL, 10, 20, 'o');
		u->submit(DEL, 10, 20, 'g');
		u->submit(INS, 10, 20, 'm');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == INS  &&  u->get(r, c, ct, t) == DEL);
			} break; case BASEUN + 18: {
		u->submit(INS, 10, 20, 'k');
		u->submit(DEL, 10, 20, 'd');
		u->submit(DEL, 10, 20, 'o');
		u->submit(DEL, 10, 20, 'g');
		u->submit(INS, 10, 20, 'm');
		u->get(r, c, ct, t);
		r = 999; c = 999; ct = 999; t = "ZZZZ";
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 20  &&  ct == 1  &&  t == "dog");
			} break; case BASEUN + 19: {
		u->submit(INS, 10, 20, 'k');
		u->submit(DEL, 9, 20, 'x');
		u->submit(DEL, 10, 20, 'd');
		u->submit(DEL, 10, 20, 'o');
		u->submit(DEL, 10, 21, 'g');
		u->submit(DEL, 10, 21, 'm');
		u->submit(DEL, 10, 21, 'a');
		u->submit(INS, 10, 21, 'n');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == INS  &&  t == "gma");
		assert(u->get(r, c, ct, t) == INS  &&  t == "do");
			} break; case BASEUN + 20: {
		u->submit(INS, 10, 24, 'm');
		u->submit(DEL, 10, 23, 'g');
		u->submit(DEL, 10, 22, 'o');
		u->submit(DEL, 10, 21, 'd');
		u->submit(INS, 10, 20, 'k');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == INS  &&  u->get(r, c, ct, t) == DEL);
			} break; case BASEUN + 21: {
		u->submit(INS, 10, 24, 'm');
		u->submit(DEL, 10, 23, 'g');
		u->submit(DEL, 10, 22, 'o');
		u->submit(DEL, 10, 21, 'd');
		u->submit(INS, 10, 20, 'k');
		u->get(r, c, ct, t);
		r = 999; c = 999; ct = 999; t = "ZZZZ";
		u->get(r, c, ct, t);
		assert(r == 10  &&  c == 21  &&  ct == 1  &&  t == "dog");
			} break; case BASEUN + 22: {
		u->submit(INS, 10, 25, 'n');
		u->submit(DEL, 9, 24, 'x');
		u->submit(DEL, 10, 23, 'a');
		u->submit(DEL, 10, 22, 'm');
		u->submit(DEL, 10, 23, 'g');
		u->submit(DEL, 10, 22, 'o');
		u->submit(DEL, 10, 21, 'd');
		u->submit(INS, 10, 20, 'k');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == INS  &&  t == "dog");
		assert(u->get(r, c, ct, t) == INS  &&  t == "ma");
			} break; case BASEUN + 23: {
		u->submit(INS, 10, 22, 'g');
		u->submit(DEL, 10, 22, 'g');
		u->submit(DEL, 10, 22, 'm');
		u->submit(DEL, 10, 21, 'o');
		u->submit(DEL, 10, 20, 'd');
		u->submit(DEL, 10, 20, 'a');
		u->submit(DEL, 10, 20, 's');
		u->submit(INS, 10, 22, 'k');
		u->get(r, c, ct, t);
		assert(u->get(r, c, ct, t) == INS);
		assert(t == "dogmas"  ||  // "dogmas" means batched mixed BS and DEL
 				(t == "as"  &&  u->get(r, c, ct, t) == INS  &&  t == "do"));
			}
	}
}

void testSpellCheck(int n)
{
	auto s = unique_ptr<SpellCheck>(createSpellCheck());

	vector<string> v;
	vector<SpellCheck::Position> probs;

	switch (n)
	{
				 default: {
	    cout << "Bad argument SP" << endl;
			} break; case BASESP +  1: {
		assert(!s->load("/this/file/does/not/exist"));
		assert(load(s, "dog\ncat\n"));
			} break; case BASESP +  2: {
		load(s, "cat\n");
		assert(s->spellCheck("cat", 0, v)  &&  !s->spellCheck("dog", 0, v));
			} break; case BASESP +  3: {
		load(s, "   c29^#%-a t@5,\n");
		assert(s->spellCheck("cat", 0, v)  &&  !s->spellCheck("dog", 0, v));
			} break; case BASESP +  4: {
		load(s, "can' --t\n");
		assert(s->spellCheck("can't", 0, v)  &&  !s->spellCheck("cant", 0, v));
			} break; case BASESP +  5: {
		load(s, "cant\n");
		assert(s->spellCheck("cant", 0, v)  &&  !s->spellCheck("can't", 0, v));
			} break; case BASESP +  6: {
		load(s, "CaT\n");
		assert(s->spellCheck("cAt", 0, v)  &&  !s->spellCheck("dog", 0, v));
			} break; case BASESP +  7: {
		load(s, "mouse\nfish\ncat\nhorse\n");
		assert(s->spellCheck("cat", 0, v)  &&  !s->spellCheck("dog", 0, v));
			} break; case BASESP +  8: {
		load(s, "cat\n");
		assert(s->spellCheck("cat", 0, v)  &&  !s->spellCheck("catch", 0, v));
			} break; case BASESP +  9: {
		load(s, "catch\n");
		assert(s->spellCheck("catch", 0, v)  &&  !s->spellCheck("cat", 0, v));
			} break; case BASESP + 10: {
		load(s, "catch\ncat\ncatcher\n");
		assert(s->spellCheck("cat", 0, v)  &&  s->spellCheck("catcher", 0, v) &&
				s->spellCheck("catch", 0, v)  &&  !s->spellCheck("c", 0, v)  &&
				!s->spellCheck("catc", 0, v)  &&  !s->spellCheck("catchers", 0, v));
			} break; case BASESP + 11: {
		load(s, "'tis\n'enry\nzap\nzoo\nzounds\nzonk\na\n\ant\nape\nany\nain't\n\npo'\n");
		assert(s->spellCheck("zoo", 0, v)  &&  s->spellCheck("a", 0, v)  &&
				s->spellCheck("zonk", 0, v)  &&  s->spellCheck("'enry", 0, v)  &&
				s->spellCheck("ain't", 0, v)  &&  s->spellCheck("po'", 0, v)  &&
				!s->spellCheck("apt", 0, v)  &&  !s->spellCheck("b", 0, v)  &&
				!s->spellCheck("'til", 0, v));
			} break; case BASESP + 12: {
		load(s, "cat\n");
		assert(s->spellCheck("cat", 1, v)  &&  v.empty());
			} break; case BASESP + 13: {
		string dict = "cat\n";
		load(s, dict);
		assert(!s->spellCheck("rat", 0, v)  &&  v.empty());
			} break; case BASESP + 14: {
		string dict = "cat\n";
		load(s, dict);
		assert(!s->spellCheck("rat", 1, v)  &&  v.size() == 1  &&
				goodsuggs("rat", v, dict));
			} break; case BASESP + 15: {
		string dict = "cat\nrate\nbrat\nrut\nrug\ncar\nset\ndog\nrag\n";
		load(s, dict);
		assert(!s->spellCheck("rat", 9, v)  &&  v.size() == 3  &&
				goodsuggs("rat", v, dict));
			} break; case BASESP + 16: {
		string dict = "cat\nrate\nbrat\nrut\nrug\ncar\nset\ndog\nrag\n";
		load(s, dict);
		assert(!s->spellCheck("rat", 12, v)  &&  v.size() == 3  &&
				goodsuggs("rat", v, dict));
			} break; case BASESP + 17: {
		string dict = "cat\nrate\nbrat\nrut\nrug\ncar\nset\ndog\nrag\n";
		load(s, dict);
		assert(!s->spellCheck("rat", 12, v)  &&  v.size() == 3  &&
				goodsuggs("rat", v, dict));
			} break; case BASESP + 18: {
		string dict = "cat\nrate\nbrat\nrut\nrug\ncar\nset\ndog\nrag\n";
		load(s, dict);
		assert(!s->spellCheck("rat", 2, v)  &&  v.size() == 2  &&
				goodsuggs("rat", v, dict));
			} break; case BASESP + 19: {
		string dict = "lease\nwe've\nwears\nleave\n";
		load(s, dict);
		assert(!s->spellCheck("weave", 2, v)  &&  v.size() == 2  &&
				goodsuggs("weave", v, dict));
			} break; case BASESP + 20: {
		string dict = "lease\nweave\nwears\nwe're\n";
		load(s, dict);
		assert(!s->spellCheck("we've", 2, v)  &&  v.size() == 2  &&
				goodsuggs("we've", v, dict));
			} break; case BASESP + 21: {
		load(s, "cat\ndog\n");
		s->spellCheckLine("cat dog dog cat cat", probs);
		assert(probs.empty());
			} break; case BASESP + 22: {
		load(s, "cat\ndog\n");
		s->spellCheckLine("cat dog rat dog bird cat cat caw", probs);
		assert(isPerm(probs, { { 8, 10 }, { 16, 19 }, { 29, 31 } }));
			} break; case BASESP + 23: {
		load(s, "cat\ndog\n");
		s->spellCheckLine(" cat@dog32%.~dog bird     cat:caw*", probs);
		assert(isPerm(probs, { { 17, 20 }, { 30, 32 } }));
			} break; case BASESP + 24: {
		load(s, "cat\ndog\n");
		s->spellCheckLine("CaT dOg rAt Dog BIrd CAT caT cAW", probs);
		assert(isPerm(probs, { { 8, 10 }, { 16, 19 }, { 29, 31 } }));
			} break; case BASESP + 25: {
		load(s, "can't\n'tis\n");
		s->spellCheckLine("I can't 'tis cant ", probs);
		assert(isPerm(probs, { { 0, 0 }, { 13, 16 } }));
			}
	}
}

int main()
{
	cout << "Enter test number: ";
	int n;
	cin >> n;
	if (n > BASESP)
		testSpellCheck(n);
	else if (n > BASEUN)
		testUndo(n);
	else
		testTextEditor(n);
	cout << "Passed" << endl;
}