Skip to content
Snippets Groups Projects
Commit 8e4e4b13 authored by Jirka Fink's avatar Jirka Fink
Browse files

Add more tests for the 1st and 2nd assignments

parent f045fcdd
No related branches found
No related tags found
No related merge requests found
#include <algorithm>
#include <functional>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "tree_successor.h"
using namespace std;
// If the condition is not true, report an error and halt.
#define EXPECT(condition, message) do { if (!(condition)) expect_failed(message); } while (0)
void expect_failed(const string& message);
void test(const vector<int>& sequence, Tree &tree) {
Node* node = tree.successor(nullptr);
for (const auto& element : sequence) {
EXPECT(node, "Expected successor " + to_string(element) + ", got nullptr");
EXPECT(node->key == element,
"Expected successor " + to_string(element) + ", got " + to_string(node->key));
node = tree.successor(node);
}
EXPECT(!node, "Expected no successor, got " + to_string(node->key));
}
vector<int> get_linear_sequence() {
vector<int> numbers;
for (int i = 0; i < 10000000; i++)
numbers.push_back((int)(7.13*i));
return numbers;
}
void test_path(bool right) {
vector<int> numbers = get_linear_sequence();
Tree tree;
Node *node = nullptr;
if (right)
for (int key : numbers)
node = tree.insert(key, node);
else
for (int index = numbers.size() - 1; index >= 0; --index)
node = tree.insert(numbers[index], node);
test(numbers, tree);
}
void test_two_paths() {
vector<int> numbers = get_linear_sequence();
Tree tree;
Node *node = nullptr;
for(size_t i = numbers.size()/2; i < numbers.size(); i++)
node = tree.insert(numbers[i], node);
node = nullptr;
for(int i = numbers.size()/2 - 1; i >= 0; i--)
node = tree.insert(numbers[i], node);
test(numbers, tree);
}
void test_sequence(vector<int> &&sequence) {
Tree tree;
for (const auto& element : sequence)
tree.insert(element);
sort(sequence.begin(), sequence.end());
test(sequence, tree);
}
void test_random() {
vector<int> sequence = {997};
for (int i = 2; i < 199999; i++)
sequence.push_back((sequence.back() * int64_t(997)) % 199999);
test_sequence(move(sequence));
}
void test_trivial() {
test_sequence({5});
test_sequence({7,9});
test_sequence({7,3});
test_sequence({5,3,7});
}
void test_comb() {
vector<int> numbers = get_linear_sequence();
Tree tree;
Node *node = nullptr;
for(size_t i = numbers.size()/2; i < numbers.size(); i++)
node = tree.insert(numbers[i], node);
node = nullptr;
for(int i = numbers.size()/2 - 1; i >= 0; i-=2) {
node = tree.insert(numbers[i-1], node);
tree.insert(numbers[i], node);
}
test(numbers, tree);
}
vector<pair<string, function<void()>>> tests = {
{ "trivial", test_trivial },
{ "right_path", []{ test_path(true); } },
{ "left_path", []{ test_path(false); } },
{ "random_tree", test_random },
{ "two_paths", test_two_paths },
{ "comb", test_comb }
};
#!/usr/bin/env python3
import sys
from tree_successor import Tree
def test_tree(tree, sequence):
node = tree.successor(None)
for element in sequence:
assert node is not None, "Expected successor {}, got None".format(element)
assert node.key == element, "Expected successor {}, got {}".format(element, node.key)
node = tree.successor(node)
assert node is None, "Expected no successor, got {}".format(node.key)
def test_sequence(sequence):
tree = Tree()
for i in sequence:
tree.insert(i)
sequence.sort()
test_tree(tree, sequence)
def test_trivial_tree():
test_sequence([5])
test_sequence([7,9])
test_sequence([7,3])
test_sequence([5,3,7])
def test_random_tree():
test_sequence([pow(997, i, 199999) for i in range(1, 199999)])
def test_path(right):
sequence = [int(7.13*i) for i in range(1000000)]
tree = Tree()
node = None
sequence_insert = sequence if right else reversed(sequence)
for key in sequence_insert:
node = tree.insert(key, node)
test_tree(tree, sequence)
def test_two_paths():
sequence_left = [int(7.13*i) for i in range(1000000)]
sequence_right = [int(7.13*i) for i in range(1000000, 2000000)]
tree = Tree()
node = None
for key in sequence_right:
node = tree.insert(key, node)
node = None
for key in reversed(sequence_left):
node = tree.insert(key, node)
test_tree(tree, sequence_left + sequence_right)
def test_comb():
sequence = [int(7.13*i) for i in range(1000000)]
tree = Tree()
node = None
for i in range(len(sequence)//2, len(sequence)):
node = tree.insert(sequence[i], node)
node = None
for i in range(len(sequence)//2-1, 0, -2):
node = tree.insert(sequence[i-1], node)
tree.insert(sequence[i], node)
test_tree(tree, sequence)
tests = [
("trivial", test_trivial_tree),
("random_tree", test_random_tree),
("right_path", lambda: test_path(True)),
("left_path", lambda: test_path(False)),
("two_paths", test_two_paths),
("comb", test_comb),
]
if __name__ == "__main__":
for required_test in sys.argv[1:] or [name for name, _ in tests]:
for name, test in tests:
if name == required_test:
print("Running test {}".format(name), file=sys.stderr)
test()
break
else:
raise ValueError("Unknown test {}".format(name))
...@@ -15,3 +15,8 @@ You should submit the file `tree_successor.*` (but not the ...@@ -15,3 +15,8 @@ You should submit the file `tree_successor.*` (but not the
`tree_successor_test.*`). `tree_successor_test.*`).
Source code templates can be found in [the git repository](https://gitlab.kam.mff.cuni.cz/datovky/assignments/-/tree/master). Source code templates can be found in [the git repository](https://gitlab.kam.mff.cuni.cz/datovky/assignments/-/tree/master).
Files tree_successor_more_tests.{cpp,py} contain additional tests
for bugs discovered in students' solutions during this semester.
They are not included on recodex, but your program should pass them
in few seconds.
#include <algorithm>
#include <cassert>
#include <fstream>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "splay_operation.h"
using namespace std;
// If the condition is not true, report an error and halt.
#define EXPECT(condition, message) do { if (!(condition)) expect_failed(message); } while (0)
void expect_failed(const string& message);
// Flatten the tree: return a sorted list of all keys in the tree.
vector<int> flatten(const Tree& tree) {
constexpr int L = 0, R = 1, F = 2;
Node* node = tree.root;
vector<int> flattened, stack = {L};
while (!stack.empty()) {
if (stack.back() == L) {
stack.back() = R;
if (node->left) {
node = node->left;
stack.push_back(L);
}
} else if (stack.back() == R) {
flattened.push_back(node->key);
stack.back() = F;
if (node->right) {
node = node->right;
stack.push_back(L);
}
} else {
node = node->parent;
stack.pop_back();
}
}
return flattened;
}
// Test for splay operation with required helpers
class TestSplay {
public:
static Node* deserialize_node(const string& text, int& index) {
EXPECT(text[index++] == '(', "Internal error during example deserialization");
if (text[index] == ')') {
index++;
return nullptr;
} else {
int comma = text.find(',', index);
int key = stoi(text.substr(index, comma - index));
Node* left = deserialize_node(text, (index=comma + 1));
Node* right = deserialize_node(text, ++index);
EXPECT(text[index++] == ')', "Internal error during example deserialization");
return new Node(key, nullptr, left, right);
}
}
static Node* deserialize_root(const string& text) {
int index = 0;
Node* root = deserialize_node(text, index);
assert(index == text.size());
return root;
}
static string compare(Node* system, Node* gold) {
if (!system && gold) {
return "expected node with key " + to_string(gold->key) + ", found None";
} else if (system && !gold) {
return "expected None, found node with key " + to_string(system->key);
} else if (system && gold) {
if (system->key != gold->key)
return "expected node with key " + to_string(gold->key) + ", found " + to_string(system->key);
auto result = compare(system->left, gold->left);
if (!result.empty()) return result;
return compare(system->right, gold->right);
}
return string();
}
static void test() {
ifstream splay_tests_file("splay_tests.txt");
EXPECT(splay_tests_file.is_open(), "Cannot open splay_tests.txt file with the tests");
string original, splayed;
int target;
while (splay_tests_file >> original >> target >> splayed) {
Tree original_tree(deserialize_root(original));
Tree splayed_tree(deserialize_root(splayed));
Node* target_node = original_tree.root;
while (target_node && target_node->key != target)
if (target < target_node->key)
target_node = target_node->left;
else
target_node = target_node->right;
EXPECT(target_node, "Internal error during finding the target node in the tree to splay");
original_tree.splay(target_node);
auto error = compare(original_tree.root, splayed_tree.root);
EXPECT(error.empty(), "Error running splay on key " + to_string(target) + " of " + original + ": " + error);
}
}
};
const int elements = 5000000;
void test_lookup() {
// Insert even numbers
Tree tree;
for (int i = 0; i < elements; i += 2)
tree.insert(i);
// Find non-existing
for (int i = 1; i < elements; i += 2)
for (int j = 0; j < 10; j++)
EXPECT(!tree.lookup(i), "Non-existing element was found");
// Find existing
for (int i = 0; i < elements; i += 2)
for (int j = 0; j < 10; j++)
EXPECT(tree.lookup(i), "Existing element was not found");
}
void test_insert() {
// Test validity first
{
Tree tree;
vector<int> sequence = {997};
for (int i = 2; i < 1999; i++)
sequence.push_back((sequence.back() * sequence.front()) % 1999);
for (const auto& i : sequence)
tree.insert(i);
vector<int> flattened = flatten(tree);
sort(sequence.begin(), sequence.end());
EXPECT(flattened == sequence, "Incorrect tree after a sequence of inserts");
}
// Test speed
{
Tree tree;
for (int i = 0; i < elements; i++)
for (int j = 0; j < 10; j++)
tree.insert(i);
}
{
Tree tree;
for (int i = elements; i >= 0; i--)
tree.insert(i);
for (int i = 0; i < elements; i++)
tree.insert(elements);
}
}
void test_remove() {
// Test validity first
{
Tree tree;
for (int i = 2; i < 1999 * 2; i++)
tree.insert(i);
vector<int> sequence = {2 * 997};
for (int i = 2; i < 1999; i++)
sequence.push_back(2 * ((sequence.back() * sequence.front() / 4) % 1999));
for (const auto& i : sequence)
tree.remove(i + 1);
vector<int> flattened = flatten(tree);
sort(sequence.begin(), sequence.end());
EXPECT(flattened == sequence, "Correct tree after a sequence of removes");
}
// Test speed
{
Tree tree;
for (int i = 0; i < elements; i++)
tree.insert(i);
// Non-existing elements
for (int i = 1; i < elements; i += 2)
for (int j = 0; j < 10; j++)
tree.remove(i);
// Existing elements
for (int i = 2; i < elements; i += 2)
for (int j = 0; j < 10; j++)
tree.remove(i);
}
{
Tree tree;
for (int i = 1; i < elements; i++)
tree.insert(i);
for (int i = 1; i < elements; i++)
tree.remove(0);
}
}
vector<pair<string, function<void()>>> tests = {
{ "splay", TestSplay::test },
{ "lookup", test_lookup },
{ "insert", test_insert },
{ "remove", test_remove },
};
#!/usr/bin/env python3
import itertools
import math
import sys
from splay_operation import Tree, Node
def flatten(tree):
"""Flatten given tree in ascending order."""
L, R, F = 0, 1, 2
node, stack, flattened = tree.root, [L], []
while node is not None:
if stack[-1] == L:
stack[-1] = R
if node.left is not None:
node = node.left
stack.append(L)
elif stack[-1] == R:
flattened.append(node.key)
stack[-1] = F
if node.right is not None:
node = node.right
stack.append(L)
else:
node = node.parent
stack.pop()
return flattened
def test_splay():
def deserialize_tree(string):
def deserialize_node(i):
assert string[i] == "("
i += 1
if string[i] == ")":
return i + 1, None
else:
comma = string.find(",", i)
comma2, left = deserialize_node(comma + 1)
rparen, right = deserialize_node(comma2 + 1)
assert string[rparen] == ")"
return rparen + 1, Node(int(string[i : comma]), left=left, right=right)
index, root = deserialize_node(0)
assert index == len(string)
return Tree(root)
def compare(system, gold):
if system is None and gold is not None:
return "expected node with key {}, found None".format(gold.key)
elif system is not None and gold is None:
return "expected None, found node with key {}".format(system.key)
elif system is not None and gold is not None:
if system.key != gold.key:
return "expected node with key {}, found {}".format(gold.key, system.key)
return compare(system.left, gold.left) or compare(system.right, gold.right)
with open("splay_tests.txt", "r") as splay_tests_file:
for line in splay_tests_file:
original_serialized, target_serialized, splayed_serialized = line.rstrip("\n").split()
original = deserialize_tree(original_serialized)
splayed = deserialize_tree(splayed_serialized)
target = int(target_serialized)
node = original.root
while node is not None and node.key != target:
if target < node.key: node = node.left
else: node = node.right
assert node is not None
original.splay(node)
error = compare(original.root, splayed.root)
assert not error, "Error running splay on key {} of {}: {}".format(node.key, original_serialized, error)
def test_lookup():
tree = Tree()
for elem in range(0, 100000, 2):
tree.insert(elem)
# Find non-existing
for elem in range(1, 100000, 2):
for _ in range(10):
assert tree.lookup(elem) is None, "Non-existing element was found"
# Find existing
for elem in range(0, 100000, 2):
for _ in range(10):
assert tree.lookup(elem) is not None, "Existing element was not found"
def test_insert():
# Test validity first
tree = Tree()
sequence = [pow(997, i, 1999) for i in range(1, 1999)]
for elem in sequence:
tree.insert(elem)
assert flatten(tree) == sorted(sequence), "Incorrect tree after a sequence of inserts"
# Test speed
elements = 200000
tree = Tree()
for elem in range(elements):
for _ in range(10):
tree.insert(elem)
tree = Tree()
for elem in reversed(range(elements)):
tree.insert(elem)
for elem in range(elements):
tree.insert(elements)
def test_remove():
# Test validity first
tree = Tree()
for elem in range(2, 1999 * 2):
tree.insert(elem)
sequence = [2 * pow(997, i, 1999) for i in range(1, 1999)]
for elem in sequence:
tree.remove(elem + 1)
assert flatten(tree) == sorted(sequence), "Incorrect tree after a sequence of removes"
# Test speed
elements = 200000
tree = Tree()
for elem in range(0, elements, 2):
tree.insert(elem)
# Non-existing elements
for elem in range(1, elements, 2):
for _ in range(10):
tree.remove(elem)
# Existing elements
for elem in range(2, elements, 2):
tree.remove(elem)
tree = Tree()
for elem in range(1, elements):
tree.insert(elem)
for elem in range(elements):
tree.remove(0)
tests = [
("splay", test_splay),
("lookup", test_lookup),
("insert", test_insert),
("remove", test_remove),
]
if __name__ == "__main__":
for required_test in sys.argv[1:] or [name for name, _ in tests]:
for name, test in tests:
if name == required_test:
print("Running test {}".format(name), file=sys.stderr)
test()
break
else:
raise ValueError("Unknown test {}".format(name))
...@@ -10,3 +10,8 @@ You should submit the `splay_operation.*` file (but not the ...@@ -10,3 +10,8 @@ You should submit the `splay_operation.*` file (but not the
`splay_operation_test.*`). `splay_operation_test.*`).
Source code templates can be found in [the git repository](https://gitlab.kam.mff.cuni.cz/datovky/assignments/-/tree/master). Source code templates can be found in [the git repository](https://gitlab.kam.mff.cuni.cz/datovky/assignments/-/tree/master).
Files splay_operation_more_tests.{cpp,py} contain additional tests
for bugs discovered in students' solutions during this semester.
They are not included on recodex, but your program should pass them
in few seconds.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment