diff --git a/01-tree_successor/cpp/Makefile b/01-tree_successor/cpp/Makefile deleted file mode 100644 index 9feafbe75fdce9e90992dd6e409d359fea098b3c..0000000000000000000000000000000000000000 --- a/01-tree_successor/cpp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -test: tree_successor_test - ./$< - -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare - -tree_successor_test: tree_successor.h tree_successor_test.cpp test_main.cpp - $(CXX) $(CXXFLAGS) $^ -o $@ - -clean: - rm -f tree_successor_test - -.PHONY: clean test diff --git a/01-tree_successor/cpp/test_main.cpp b/01-tree_successor/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/01-tree_successor/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/01-tree_successor/cpp/tree_successor.h b/01-tree_successor/cpp/tree_successor.h deleted file mode 100644 index d1306f6ea2e0fa3af6674da904e72898fe1dcc45..0000000000000000000000000000000000000000 --- a/01-tree_successor/cpp/tree_successor.h +++ /dev/null @@ -1,73 +0,0 @@ -// A node of the tree -class Node { - public: - int key; - Node* left; - Node* right; - Node* parent; - - // Constructor - Node(int key, Node* parent=nullptr) { - this->key = key; - this->parent = parent; - this->left = nullptr; - this->right = nullptr; - } -}; - -// Binary tree -class Tree { - public: - // Pointer to root of the tree; nullptr if the tree is empty. - Node* root = nullptr; - - // Insert a key into the tree. - // If the key is already present, nothing happens. - void insert(int key) { - if (!root) { - root = new Node(key); - return; - } - - Node* node = root; - while (node->key != key) { - if (key < node->key) { - if (!node->left) - node->left = new Node(key, node); - node = node->left; - } else { - if (!node->right) - node->right = new Node(key, node); - node = node->right; - } - } - } - - // Return successor of the given node. - // - // The successor of a node is the node with the next higher key. - // Return nullptr if there is no such node. - // If the argument is nullptr, return the node with the smallest key. - Node* successor(Node* node) { - // TODO: Implement - } - - // Destructor to free all allocated memory. - ~Tree() { - Node* node = root; - while (node) { - Node* next; - if (node->left) { - next = node->left; - node->left = nullptr; - } else if (node->right) { - next = node->right; - node->right = nullptr; - } else { - next = node->parent; - delete node; - } - node = next; - } - } -}; diff --git a/01-tree_successor/cpp/tree_successor_test.cpp b/01-tree_successor/cpp/tree_successor_test.cpp deleted file mode 100644 index b95810aca6a445dd09bee40c34dc08c2a37562ad..0000000000000000000000000000000000000000 --- a/01-tree_successor/cpp/tree_successor_test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#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; - for (const auto& element : sequence) - tree.insert(element); - - vector<int> sorted_sequence(sequence); - sort(sorted_sequence.begin(), sorted_sequence.end()); - - Node* node = tree.successor(nullptr); - for (const auto& element : sorted_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<pair<string, function<void()>>> tests = { - {"path", [] - { vector<int> numbers; - for (int i = 0; i < 10000; i++) numbers.push_back(i); - test(numbers); - } - }, - {"random_tree", [] - { vector<int> numbers = {997}; - for (int i = 2; i < 199999; i++) - numbers.push_back((numbers.back() * int64_t(997)) % 199999); - test(numbers); - } - }, -}; diff --git a/01-tree_successor/python/tree_successor.py b/01-tree_successor/python/tree_successor.py deleted file mode 100644 index d0377ab6cf0725f68a0f4542a97b78aadc1fcfa7..0000000000000000000000000000000000000000 --- a/01-tree_successor/python/tree_successor.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 - -class Node: - """Node in a binary tree `Tree`""" - - def __init__(self, key, left=None, right=None, parent=None): - self.key = key - self.left = left - self.right = right - self.parent = parent - -class Tree: - """A simple binary search tree""" - - def __init__(self, root=None): - self.root = root - - def insert(self, key): - """Insert key into the tree. - - If the key is already present, do nothing. - """ - if self.root is None: - self.root = Node(key) - return - - node = self.root - while node.key != key: - if key < node.key: - if node.left is None: - node.left = Node(key, parent=node) - node = node.left - else: - if node.right is None: - node.right = Node(key, parent=node) - node = node.right - - def successor(self, node=None): - """Return successor of the given node. - - The successor of a node is the node with the next greater key. - Return None if there is no such node. - If the argument is None, return the node with the smallest key. - """ - # TODO: Implement - raise NotImplementedError diff --git a/01-tree_successor/python/tree_successor_test.py b/01-tree_successor/python/tree_successor_test.py deleted file mode 100644 index 81d3cc182d5ff490eb4bf8cbdc82d5d81dc98996..0000000000000000000000000000000000000000 --- a/01-tree_successor/python/tree_successor_test.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 -import sys - -import tree_successor - -def test_sequence(sequence): - tree = tree_successor.Tree() - for i in sequence: tree.insert(i) - - node = tree.successor(None) - for element in sorted(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) - -tests = [ - ("path", lambda: test_sequence(range(3000))), - ("random_tree", lambda: test_sequence([pow(997, i, 199999) for i in range(1, 199999)])), -] - -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)) diff --git a/01-tree_successor/task.md b/01-tree_successor/task.md deleted file mode 100644 index 44b76a3e9fdb2159102037fac47113f9a72652f0..0000000000000000000000000000000000000000 --- a/01-tree_successor/task.md +++ /dev/null @@ -1,11 +0,0 @@ -Given an implementation of a simple binary search tree including parent -pointers, implement a `successor` method. The methods is given a node -and it should return another node of the tree with the next higher key -(i.e., the smallest of keys which are greater than the given one). - -- If there is no such node, it should return a null pointer. -- The given node can also be a null pointer, in which case the method should - return the smallest node in the tree. - -You should submit the file `tree_successor.*` (but not the -`tree_successor_test.*`). diff --git a/02-splay_operation/cpp/Makefile b/02-splay_operation/cpp/Makefile deleted file mode 100644 index 4ca82749b82280d73f3eb750ae3167fa1fbafd4f..0000000000000000000000000000000000000000 --- a/02-splay_operation/cpp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -test: splay_operation_test - ./$< - -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare - -splay_operation_test: splay_operation.h splay_operation_test.cpp test_main.cpp - $(CXX) $(CXXFLAGS) $^ -o $@ - -clean:: - rm -f splay_operation_test - -.PHONY: clean test diff --git a/02-splay_operation/cpp/splay_operation.h b/02-splay_operation/cpp/splay_operation.h deleted file mode 100644 index 5d7bd11c77c0fa068cb7d9a28ce8448dde9f042d..0000000000000000000000000000000000000000 --- a/02-splay_operation/cpp/splay_operation.h +++ /dev/null @@ -1,158 +0,0 @@ -// A node of the tree -class Node { - public: - int key; - Node* left; - Node* right; - Node* parent; - - // Constructor - Node(int key, Node* parent=nullptr, Node* left=nullptr, Node* right=nullptr) { - this->key = key; - this->parent = parent; - this->left = left; - this->right = right; - if (left) left->parent = this; - if (right) right->parent = this; - } -}; - -// Binary tree -class Tree { - public: - // Pointer to root of the tree; nullptr if the tree is empty. - Node* root; - - Tree(Node* root=nullptr) { - this->root = root; - } - - // Rotate the given `node` up. Perform a single rotation of the edge - // between the node and its parent, choosing left or right rotation - // appropriately. - virtual void rotate(Node* node) { - if (node->parent) { - if (node->parent->left == node) { - if (node->right) node->right->parent = node->parent; - node->parent->left = node->right; - node->right = node->parent; - } else { - if (node->left) node->left->parent = node->parent; - node->parent->right = node->left; - node->left = node->parent; - } - if (node->parent->parent) { - if (node->parent->parent->left == node->parent) - node->parent->parent->left = node; - else - node->parent->parent->right = node; - } else { - root = node; - } - - Node* original_parent = node->parent; - node->parent = node->parent->parent; - original_parent->parent = node; - } - } - - // Look up the given key in the tree, returning the - // the node with the requested key or nullptr. - Node* lookup(int key) { - // TODO: Utilize splay suitably. - Node* node = root; - while (node) { - if (node->key == key) { - return node; - } - if (key < node->key) - node = node->left; - else - node = node->right; - } - return nullptr; - } - - // Insert a key into the tree. - // If the key is already present, nothing happens. - void insert(int key) { - // TODO: Utilize splay suitably. - if (!root) { - root = new Node(key); - return; - } - - Node* node = root; - while (node->key != key) { - if (key < node->key) { - if (!node->left) - node->left = new Node(key, node); - node = node->left; - } else { - if (!node->right) - node->right = new Node(key, node); - node = node->right; - } - } - } - - // Delete given key from the tree. - // It the key is not present, do nothing. - void remove(int key) { - // TODO: Utilize splay suitably. - Node* node = root; - while (node && node->key != key) { - if (key < node->key) - node = node->left; - else - node = node->right; - } - - if (node) { - if (node->left && node->right) { - Node* replacement = node->right; - while (replacement->left) - replacement = replacement->left; - node->key = replacement->key; - node = replacement; - } - - Node* replacement = node->left ? node->left : node->right; - if (node->parent) { - if (node->parent->left == node) node->parent->left = replacement; - else node->parent->right = replacement; - } else { - root = replacement; - } - if (replacement) - replacement->parent = node->parent; - delete node; - } - } - - // Splay the given node. - // If a single rotation needs to be performed, perform it as the last rotation - // (i.e., to move the splayed node to the root of the tree). - virtual void splay(Node* node) { - // TODO: Implement - } - - // Destructor to free all allocated memory. - ~Tree() { - Node* node = root; - while (node) { - Node* next; - if (node->left) { - next = node->left; - node->left = nullptr; - } else if (node->right) { - next = node->right; - node->right = nullptr; - } else { - next = node->parent; - delete node; - } - node = next; - } - } -}; diff --git a/02-splay_operation/cpp/splay_operation_test.cpp b/02-splay_operation/cpp/splay_operation_test.cpp deleted file mode 100644 index d1e6c996b56b28dd37db3d85ddd7d817c82269b9..0000000000000000000000000000000000000000 --- a/02-splay_operation/cpp/splay_operation_test.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#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); - } - } -}; - -void test_lookup() { - // Insert even numbers - Tree tree; - for (int i = 0; i < 5000000; i += 2) - tree.insert(i); - - // Find non-existing - for (int i = 1; i < 5000000; 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 < 5000000; 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 < 5000000; i++) - for (int j = 0; j < 10; j++) - tree.insert(i); - } -} - -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 < 5000000; i++) - tree.insert(i); - - // Non-existing elements - for (int i = 1; i < 5000000; i += 2) - for (int j = 0; j < 10; j++) - tree.remove(i); - - // Existing elements - for (int i = 2; i < 5000000; i += 2) - for (int j = 0; j < 10; j++) - tree.remove(i); - } -} - -vector<pair<string, function<void()>>> tests = { - { "splay", TestSplay::test }, - { "lookup", test_lookup }, - { "insert", test_insert }, - { "remove", test_remove }, -}; diff --git a/02-splay_operation/cpp/splay_tests.txt b/02-splay_operation/cpp/splay_tests.txt deleted file mode 100644 index 7f3259e5d6a4689f8e7cc608e450905b82647b7b..0000000000000000000000000000000000000000 --- a/02-splay_operation/cpp/splay_tests.txt +++ /dev/null @@ -1,62 +0,0 @@ -(3,(1,(0,(),()),(2,(),())),(4,(),())) 4 (4,(3,(1,(0,(),()),(2,(),())),()),()) -(6,(5,(),()),(8,(7,(),()),(9,(),()))) 5 (5,(),(6,(),(8,(7,(),()),(9,(),())))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(8,(),()))) 8 (8,(7,(3,(1,(0,(),()),(2,(),())),(5,(4,(),()),(6,(),()))),()),()) -(11,(9,(7,(6,(),()),(8,(),())),(10,(),())),(13,(12,(),()),(14,(),()))) 10 (10,(9,(7,(6,(),()),(8,(),())),()),(11,(),(13,(12,(),()),(14,(),())))) -(8,(6,(5,(),()),(7,(),())),(10,(9,(),()),(12,(11,(),()),(13,(),())))) 9 (9,(8,(6,(5,(),()),(7,(),())),()),(10,(),(12,(11,(),()),(13,(),())))) -(16,(12,(11,(),()),(14,(13,(),()),(15,(),()))),(18,(17,(),()),(19,(),()))) 11 (11,(),(12,(),(16,(14,(13,(),()),(15,(),())),(18,(17,(),()),(19,(),()))))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(11,(9,(8,(),()),(10,(),())),(12,(),())))) 12 (12,(3,(1,(0,(),()),(2,(),())),(11,(7,(5,(4,(),()),(6,(),())),(9,(8,(),()),(10,(),()))),())),()) -(17,(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(16,(),()))),(19,(18,(),()),(20,(),()))) 16 (16,(15,(11,(9,(8,(),()),(10,(),())),(13,(12,(),()),(14,(),()))),()),(17,(),(19,(18,(),()),(20,(),())))) -(9,(7,(6,(),()),(8,(),())),(15,(13,(11,(10,(),()),(12,(),())),(14,(),())),(17,(16,(),()),(18,(),())))) 14 (14,(9,(7,(6,(),()),(8,(),())),(13,(11,(10,(),()),(12,(),())),())),(15,(),(17,(16,(),()),(18,(),())))) -(23,(19,(17,(15,(14,(),()),(16,(),())),(18,(),())),(21,(20,(),()),(22,(),()))),(25,(24,(),()),(26,(),()))) 18 (18,(17,(15,(14,(),()),(16,(),())),()),(23,(19,(),(21,(20,(),()),(22,(),()))),(25,(24,(),()),(26,(),())))) -(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),(14,(13,(),()),(16,(15,(),()),(17,(),()))))) 13 (13,(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),())),(14,(),(16,(15,(),()),(17,(),())))) -(22,(16,(14,(13,(),()),(15,(),())),(18,(17,(),()),(20,(19,(),()),(21,(),())))),(24,(23,(),()),(25,(),()))) 17 (17,(16,(14,(13,(),()),(15,(),())),()),(22,(18,(),(20,(19,(),()),(21,(),()))),(24,(23,(),()),(25,(),())))) -(14,(12,(11,(),()),(13,(),())),(20,(16,(15,(),()),(18,(17,(),()),(19,(),()))),(22,(21,(),()),(23,(),())))) 15 (15,(14,(12,(11,(),()),(13,(),())),()),(16,(),(20,(18,(17,(),()),(19,(),())),(22,(21,(),()),(23,(),()))))) -(28,(24,(20,(19,(),()),(22,(21,(),()),(23,(),()))),(26,(25,(),()),(27,(),()))),(30,(29,(),()),(31,(),()))) 19 (19,(),(28,(20,(),(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(27,(),())))),(30,(29,(),()),(31,(),())))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(16,(),()))))) 16 (16,(7,(3,(1,(0,(),()),(2,(),())),(5,(4,(),()),(6,(),()))),(15,(11,(9,(8,(),()),(10,(),())),(13,(12,(),()),(14,(),()))),())),()) -(25,(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(23,(21,(20,(),()),(22,(),())),(24,(),())))),(27,(26,(),()),(28,(),()))) 24 (24,(15,(13,(12,(),()),(14,(),())),(23,(19,(17,(16,(),()),(18,(),())),(21,(20,(),()),(22,(),()))),())),(25,(),(27,(26,(),()),(28,(),())))) -(11,(9,(8,(),()),(10,(),())),(21,(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(20,(),()))),(23,(22,(),()),(24,(),())))) 20 (20,(11,(9,(8,(),()),(10,(),())),(19,(15,(13,(12,(),()),(14,(),())),(17,(16,(),()),(18,(),()))),())),(21,(),(23,(22,(),()),(24,(),())))) -(33,(29,(23,(21,(20,(),()),(22,(),())),(27,(25,(24,(),()),(26,(),())),(28,(),()))),(31,(30,(),()),(32,(),()))),(35,(34,(),()),(36,(),()))) 28 (28,(27,(23,(21,(20,(),()),(22,(),())),(25,(24,(),()),(26,(),()))),()),(29,(),(33,(31,(30,(),()),(32,(),())),(35,(34,(),()),(36,(),()))))) -(9,(7,(6,(),()),(8,(),())),(13,(11,(10,(),()),(12,(),())),(19,(17,(15,(14,(),()),(16,(),())),(18,(),())),(21,(20,(),()),(22,(),()))))) 18 (18,(13,(9,(7,(6,(),()),(8,(),())),(11,(10,(),()),(12,(),()))),(17,(15,(14,(),()),(16,(),())),())),(19,(),(21,(20,(),()),(22,(),())))) -(31,(21,(19,(18,(),()),(20,(),())),(27,(25,(23,(22,(),()),(24,(),())),(26,(),())),(29,(28,(),()),(30,(),())))),(33,(32,(),()),(34,(),()))) 26 (26,(21,(19,(18,(),()),(20,(),())),(25,(23,(22,(),()),(24,(),())),())),(31,(27,(),(29,(28,(),()),(30,(),()))),(33,(32,(),()),(34,(),())))) -(17,(15,(14,(),()),(16,(),())),(27,(23,(21,(19,(18,(),()),(20,(),())),(22,(),())),(25,(24,(),()),(26,(),()))),(29,(28,(),()),(30,(),())))) 22 (22,(17,(15,(14,(),()),(16,(),())),(21,(19,(18,(),()),(20,(),())),())),(27,(23,(),(25,(24,(),()),(26,(),()))),(29,(28,(),()),(30,(),())))) -(39,(35,(31,(29,(27,(26,(),()),(28,(),())),(30,(),())),(33,(32,(),()),(34,(),()))),(37,(36,(),()),(38,(),()))),(41,(40,(),()),(42,(),()))) 30 (30,(29,(27,(26,(),()),(28,(),())),()),(35,(31,(),(33,(32,(),()),(34,(),()))),(39,(37,(36,(),()),(38,(),())),(41,(40,(),()),(42,(),()))))) -(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),(16,(14,(13,(),()),(15,(),())),(18,(17,(),()),(20,(19,(),()),(21,(),())))))) 17 (17,(12,(8,(6,(5,(),()),(7,(),())),(10,(9,(),()),(11,(),()))),(16,(14,(13,(),()),(15,(),())),())),(18,(),(20,(19,(),()),(21,(),())))) -(30,(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(28,(27,(),()),(29,(),()))))),(32,(31,(),()),(33,(),()))) 25 (25,(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),())),(30,(26,(),(28,(27,(),()),(29,(),()))),(32,(31,(),()),(33,(),())))) -(16,(14,(13,(),()),(15,(),())),(26,(20,(18,(17,(),()),(19,(),())),(22,(21,(),()),(24,(23,(),()),(25,(),())))),(28,(27,(),()),(29,(),())))) 21 (21,(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),())),(26,(22,(),(24,(23,(),()),(25,(),()))),(28,(27,(),()),(29,(),())))) -(38,(34,(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(32,(31,(),()),(33,(),())))),(36,(35,(),()),(37,(),()))),(40,(39,(),()),(41,(),()))) 29 (29,(28,(26,(25,(),()),(27,(),())),()),(34,(30,(),(32,(31,(),()),(33,(),()))),(38,(36,(35,(),()),(37,(),())),(40,(39,(),()),(41,(),()))))) -(14,(12,(11,(),()),(13,(),())),(18,(16,(15,(),()),(17,(),())),(24,(20,(19,(),()),(22,(21,(),()),(23,(),()))),(26,(25,(),()),(27,(),()))))) 19 (19,(18,(14,(12,(11,(),()),(13,(),())),(16,(15,(),()),(17,(),()))),()),(20,(),(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(27,(),()))))) -(36,(26,(24,(23,(),()),(25,(),())),(32,(28,(27,(),()),(30,(29,(),()),(31,(),()))),(34,(33,(),()),(35,(),())))),(38,(37,(),()),(39,(),()))) 27 (27,(26,(24,(23,(),()),(25,(),())),()),(36,(28,(),(32,(30,(29,(),()),(31,(),())),(34,(33,(),()),(35,(),())))),(38,(37,(),()),(39,(),())))) -(22,(20,(19,(),()),(21,(),())),(32,(28,(24,(23,(),()),(26,(25,(),()),(27,(),()))),(30,(29,(),()),(31,(),()))),(34,(33,(),()),(35,(),())))) 23 (23,(22,(20,(19,(),()),(21,(),())),()),(32,(24,(),(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(31,(),())))),(34,(33,(),()),(35,(),())))) -(44,(40,(36,(32,(31,(),()),(34,(33,(),()),(35,(),()))),(38,(37,(),()),(39,(),()))),(42,(41,(),()),(43,(),()))),(46,(45,(),()),(47,(),()))) 31 (31,(),(40,(32,(),(36,(34,(33,(),()),(35,(),())),(38,(37,(),()),(39,(),())))),(44,(42,(41,(),()),(43,(),())),(46,(45,(),()),(47,(),()))))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(20,(),())))))) 20 (20,(3,(1,(0,(),()),(2,(),())),(11,(7,(5,(4,(),()),(6,(),())),(9,(8,(),()),(10,(),()))),(19,(15,(13,(12,(),()),(14,(),())),(17,(16,(),()),(18,(),()))),()))),()) -(37,(23,(21,(20,(),()),(22,(),())),(27,(25,(24,(),()),(26,(),())),(31,(29,(28,(),()),(30,(),())),(35,(33,(32,(),()),(34,(),())),(36,(),()))))),(39,(38,(),()),(40,(),()))) 36 (36,(27,(23,(21,(20,(),()),(22,(),())),(25,(24,(),()),(26,(),()))),(35,(31,(29,(28,(),()),(30,(),())),(33,(32,(),()),(34,(),()))),())),(37,(),(39,(38,(),()),(40,(),())))) -(15,(13,(12,(),()),(14,(),())),(29,(19,(17,(16,(),()),(18,(),())),(23,(21,(20,(),()),(22,(),())),(27,(25,(24,(),()),(26,(),())),(28,(),())))),(31,(30,(),()),(32,(),())))) 28 (28,(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(27,(23,(21,(20,(),()),(22,(),())),(25,(24,(),()),(26,(),()))),()))),(29,(),(31,(30,(),()),(32,(),())))) -(49,(45,(35,(33,(32,(),()),(34,(),())),(39,(37,(36,(),()),(38,(),())),(43,(41,(40,(),()),(42,(),())),(44,(),())))),(47,(46,(),()),(48,(),()))),(51,(50,(),()),(52,(),()))) 44 (44,(35,(33,(32,(),()),(34,(),())),(43,(39,(37,(36,(),()),(38,(),())),(41,(40,(),()),(42,(),()))),())),(49,(45,(),(47,(46,(),()),(48,(),()))),(51,(50,(),()),(52,(),())))) -(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(25,(19,(17,(16,(),()),(18,(),())),(23,(21,(20,(),()),(22,(),())),(24,(),()))),(27,(26,(),()),(28,(),()))))) 24 (24,(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(23,(19,(17,(16,(),()),(18,(),())),(21,(20,(),()),(22,(),()))),()))),(25,(),(27,(26,(),()),(28,(),())))) -(45,(31,(29,(28,(),()),(30,(),())),(41,(35,(33,(32,(),()),(34,(),())),(39,(37,(36,(),()),(38,(),())),(40,(),()))),(43,(42,(),()),(44,(),())))),(47,(46,(),()),(48,(),()))) 40 (40,(31,(29,(28,(),()),(30,(),())),(39,(35,(33,(32,(),()),(34,(),())),(37,(36,(),()),(38,(),()))),())),(45,(41,(),(43,(42,(),()),(44,(),()))),(47,(46,(),()),(48,(),())))) -(23,(21,(20,(),()),(22,(),())),(37,(33,(27,(25,(24,(),()),(26,(),())),(31,(29,(28,(),()),(30,(),())),(32,(),()))),(35,(34,(),()),(36,(),()))),(39,(38,(),()),(40,(),())))) 32 (32,(23,(21,(20,(),()),(22,(),())),(31,(27,(25,(24,(),()),(26,(),())),(29,(28,(),()),(30,(),()))),())),(33,(),(37,(35,(34,(),()),(36,(),())),(39,(38,(),()),(40,(),()))))) -(57,(53,(49,(43,(41,(40,(),()),(42,(),())),(47,(45,(44,(),()),(46,(),())),(48,(),()))),(51,(50,(),()),(52,(),()))),(55,(54,(),()),(56,(),()))),(59,(58,(),()),(60,(),()))) 48 (48,(47,(43,(41,(40,(),()),(42,(),())),(45,(44,(),()),(46,(),()))),()),(57,(49,(),(53,(51,(50,(),()),(52,(),())),(55,(54,(),()),(56,(),())))),(59,(58,(),()),(60,(),())))) -(9,(7,(6,(),()),(8,(),())),(13,(11,(10,(),()),(12,(),())),(17,(15,(14,(),()),(16,(),())),(23,(21,(19,(18,(),()),(20,(),())),(22,(),())),(25,(24,(),()),(26,(),())))))) 22 (22,(9,(7,(6,(),()),(8,(),())),(17,(13,(11,(10,(),()),(12,(),())),(15,(14,(),()),(16,(),()))),(21,(19,(18,(),()),(20,(),())),()))),(23,(),(25,(24,(),()),(26,(),())))) -(43,(29,(27,(26,(),()),(28,(),())),(33,(31,(30,(),()),(32,(),())),(39,(37,(35,(34,(),()),(36,(),())),(38,(),())),(41,(40,(),()),(42,(),()))))),(45,(44,(),()),(46,(),()))) 38 (38,(33,(29,(27,(26,(),()),(28,(),())),(31,(30,(),()),(32,(),()))),(37,(35,(34,(),()),(36,(),())),())),(43,(39,(),(41,(40,(),()),(42,(),()))),(45,(44,(),()),(46,(),())))) -(21,(19,(18,(),()),(20,(),())),(35,(25,(23,(22,(),()),(24,(),())),(31,(29,(27,(26,(),()),(28,(),())),(30,(),())),(33,(32,(),()),(34,(),())))),(37,(36,(),()),(38,(),())))) 30 (30,(21,(19,(18,(),()),(20,(),())),(25,(23,(22,(),()),(24,(),())),(29,(27,(26,(),()),(28,(),())),()))),(35,(31,(),(33,(32,(),()),(34,(),()))),(37,(36,(),()),(38,(),())))) -(55,(51,(41,(39,(38,(),()),(40,(),())),(47,(45,(43,(42,(),()),(44,(),())),(46,(),())),(49,(48,(),()),(50,(),())))),(53,(52,(),()),(54,(),()))),(57,(56,(),()),(58,(),()))) 46 (46,(41,(39,(38,(),()),(40,(),())),(45,(43,(42,(),()),(44,(),())),())),(55,(51,(47,(),(49,(48,(),()),(50,(),()))),(53,(52,(),()),(54,(),()))),(57,(56,(),()),(58,(),())))) -(17,(15,(14,(),()),(16,(),())),(21,(19,(18,(),()),(20,(),())),(31,(27,(25,(23,(22,(),()),(24,(),())),(26,(),())),(29,(28,(),()),(30,(),()))),(33,(32,(),()),(34,(),()))))) 26 (26,(17,(15,(14,(),()),(16,(),())),(21,(19,(18,(),()),(20,(),())),(25,(23,(22,(),()),(24,(),())),()))),(31,(27,(),(29,(28,(),()),(30,(),()))),(33,(32,(),()),(34,(),())))) -(51,(37,(35,(34,(),()),(36,(),())),(47,(43,(41,(39,(38,(),()),(40,(),())),(42,(),())),(45,(44,(),()),(46,(),()))),(49,(48,(),()),(50,(),())))),(53,(52,(),()),(54,(),()))) 42 (42,(37,(35,(34,(),()),(36,(),())),(41,(39,(38,(),()),(40,(),())),())),(51,(47,(43,(),(45,(44,(),()),(46,(),()))),(49,(48,(),()),(50,(),()))),(53,(52,(),()),(54,(),())))) -(29,(27,(26,(),()),(28,(),())),(43,(39,(35,(33,(31,(30,(),()),(32,(),())),(34,(),())),(37,(36,(),()),(38,(),()))),(41,(40,(),()),(42,(),()))),(45,(44,(),()),(46,(),())))) 34 (34,(29,(27,(26,(),()),(28,(),())),(33,(31,(30,(),()),(32,(),())),())),(39,(35,(),(37,(36,(),()),(38,(),()))),(43,(41,(40,(),()),(42,(),())),(45,(44,(),()),(46,(),()))))) -(63,(59,(55,(51,(49,(47,(46,(),()),(48,(),())),(50,(),())),(53,(52,(),()),(54,(),()))),(57,(56,(),()),(58,(),()))),(61,(60,(),()),(62,(),()))),(65,(64,(),()),(66,(),()))) 50 (50,(49,(47,(46,(),()),(48,(),())),()),(63,(55,(51,(),(53,(52,(),()),(54,(),()))),(59,(57,(56,(),()),(58,(),())),(61,(60,(),()),(62,(),())))),(65,(64,(),()),(66,(),())))) -(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),(22,(21,(),()),(24,(23,(),()),(25,(),()))))))) 21 (21,(8,(6,(5,(),()),(7,(),())),(16,(12,(10,(9,(),()),(11,(),())),(14,(13,(),()),(15,(),()))),(20,(18,(17,(),()),(19,(),())),()))),(22,(),(24,(23,(),()),(25,(),())))) -(42,(28,(26,(25,(),()),(27,(),())),(32,(30,(29,(),()),(31,(),())),(36,(34,(33,(),()),(35,(),())),(38,(37,(),()),(40,(39,(),()),(41,(),())))))),(44,(43,(),()),(45,(),()))) 37 (37,(32,(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(31,(),()))),(36,(34,(33,(),()),(35,(),())),())),(42,(38,(),(40,(39,(),()),(41,(),()))),(44,(43,(),()),(45,(),())))) -(20,(18,(17,(),()),(19,(),())),(34,(24,(22,(21,(),()),(23,(),())),(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(32,(31,(),()),(33,(),()))))),(36,(35,(),()),(37,(),())))) 29 (29,(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),(28,(26,(25,(),()),(27,(),())),()))),(34,(30,(),(32,(31,(),()),(33,(),()))),(36,(35,(),()),(37,(),())))) -(54,(50,(40,(38,(37,(),()),(39,(),())),(44,(42,(41,(),()),(43,(),())),(46,(45,(),()),(48,(47,(),()),(49,(),()))))),(52,(51,(),()),(53,(),()))),(56,(55,(),()),(57,(),()))) 45 (45,(40,(38,(37,(),()),(39,(),())),(44,(42,(41,(),()),(43,(),())),())),(54,(50,(46,(),(48,(47,(),()),(49,(),()))),(52,(51,(),()),(53,(),()))),(56,(55,(),()),(57,(),())))) -(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),(30,(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(28,(27,(),()),(29,(),())))),(32,(31,(),()),(33,(),()))))) 25 (25,(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),()))),(30,(26,(),(28,(27,(),()),(29,(),()))),(32,(31,(),()),(33,(),())))) -(50,(36,(34,(33,(),()),(35,(),())),(46,(40,(38,(37,(),()),(39,(),())),(42,(41,(),()),(44,(43,(),()),(45,(),())))),(48,(47,(),()),(49,(),())))),(52,(51,(),()),(53,(),()))) 41 (41,(36,(34,(33,(),()),(35,(),())),(40,(38,(37,(),()),(39,(),())),())),(50,(46,(42,(),(44,(43,(),()),(45,(),()))),(48,(47,(),()),(49,(),()))),(52,(51,(),()),(53,(),())))) -(28,(26,(25,(),()),(27,(),())),(42,(38,(32,(30,(29,(),()),(31,(),())),(34,(33,(),()),(36,(35,(),()),(37,(),())))),(40,(39,(),()),(41,(),()))),(44,(43,(),()),(45,(),())))) 33 (33,(28,(26,(25,(),()),(27,(),())),(32,(30,(29,(),()),(31,(),())),())),(38,(34,(),(36,(35,(),()),(37,(),()))),(42,(40,(39,(),()),(41,(),())),(44,(43,(),()),(45,(),()))))) -(62,(58,(54,(48,(46,(45,(),()),(47,(),())),(50,(49,(),()),(52,(51,(),()),(53,(),())))),(56,(55,(),()),(57,(),()))),(60,(59,(),()),(61,(),()))),(64,(63,(),()),(65,(),()))) 49 (49,(48,(46,(45,(),()),(47,(),())),()),(62,(54,(50,(),(52,(51,(),()),(53,(),()))),(58,(56,(55,(),()),(57,(),())),(60,(59,(),()),(61,(),())))),(64,(63,(),()),(65,(),())))) -(14,(12,(11,(),()),(13,(),())),(18,(16,(15,(),()),(17,(),())),(22,(20,(19,(),()),(21,(),())),(28,(24,(23,(),()),(26,(25,(),()),(27,(),()))),(30,(29,(),()),(31,(),())))))) 23 (23,(14,(12,(11,(),()),(13,(),())),(22,(18,(16,(15,(),()),(17,(),())),(20,(19,(),()),(21,(),()))),())),(24,(),(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(31,(),()))))) -(48,(34,(32,(31,(),()),(33,(),())),(38,(36,(35,(),()),(37,(),())),(44,(40,(39,(),()),(42,(41,(),()),(43,(),()))),(46,(45,(),()),(47,(),()))))),(50,(49,(),()),(51,(),()))) 39 (39,(38,(34,(32,(31,(),()),(33,(),())),(36,(35,(),()),(37,(),()))),()),(48,(40,(),(44,(42,(41,(),()),(43,(),())),(46,(45,(),()),(47,(),())))),(50,(49,(),()),(51,(),())))) -(26,(24,(23,(),()),(25,(),())),(40,(30,(28,(27,(),()),(29,(),())),(36,(32,(31,(),()),(34,(33,(),()),(35,(),()))),(38,(37,(),()),(39,(),())))),(42,(41,(),()),(43,(),())))) 31 (31,(26,(24,(23,(),()),(25,(),())),(30,(28,(27,(),()),(29,(),())),())),(40,(32,(),(36,(34,(33,(),()),(35,(),())),(38,(37,(),()),(39,(),())))),(42,(41,(),()),(43,(),())))) -(60,(56,(46,(44,(43,(),()),(45,(),())),(52,(48,(47,(),()),(50,(49,(),()),(51,(),()))),(54,(53,(),()),(55,(),())))),(58,(57,(),()),(59,(),()))),(62,(61,(),()),(63,(),()))) 47 (47,(46,(44,(43,(),()),(45,(),())),()),(60,(56,(48,(),(52,(50,(49,(),()),(51,(),())),(54,(53,(),()),(55,(),())))),(58,(57,(),()),(59,(),()))),(62,(61,(),()),(63,(),())))) -(22,(20,(19,(),()),(21,(),())),(26,(24,(23,(),()),(25,(),())),(36,(32,(28,(27,(),()),(30,(29,(),()),(31,(),()))),(34,(33,(),()),(35,(),()))),(38,(37,(),()),(39,(),()))))) 27 (27,(22,(20,(19,(),()),(21,(),())),(26,(24,(23,(),()),(25,(),())),())),(36,(28,(),(32,(30,(29,(),()),(31,(),())),(34,(33,(),()),(35,(),())))),(38,(37,(),()),(39,(),())))) -(56,(42,(40,(39,(),()),(41,(),())),(52,(48,(44,(43,(),()),(46,(45,(),()),(47,(),()))),(50,(49,(),()),(51,(),()))),(54,(53,(),()),(55,(),())))),(58,(57,(),()),(59,(),()))) 43 (43,(42,(40,(39,(),()),(41,(),())),()),(56,(52,(44,(),(48,(46,(45,(),()),(47,(),())),(50,(49,(),()),(51,(),())))),(54,(53,(),()),(55,(),()))),(58,(57,(),()),(59,(),())))) -(34,(32,(31,(),()),(33,(),())),(48,(44,(40,(36,(35,(),()),(38,(37,(),()),(39,(),()))),(42,(41,(),()),(43,(),()))),(46,(45,(),()),(47,(),()))),(50,(49,(),()),(51,(),())))) 35 (35,(34,(32,(31,(),()),(33,(),())),()),(44,(36,(),(40,(38,(37,(),()),(39,(),())),(42,(41,(),()),(43,(),())))),(48,(46,(45,(),()),(47,(),())),(50,(49,(),()),(51,(),()))))) -(68,(64,(60,(56,(52,(51,(),()),(54,(53,(),()),(55,(),()))),(58,(57,(),()),(59,(),()))),(62,(61,(),()),(63,(),()))),(66,(65,(),()),(67,(),()))),(70,(69,(),()),(71,(),()))) 51 (51,(),(68,(60,(52,(),(56,(54,(53,(),()),(55,(),())),(58,(57,(),()),(59,(),())))),(64,(62,(61,(),()),(63,(),())),(66,(65,(),()),(67,(),())))),(70,(69,(),()),(71,(),())))) diff --git a/02-splay_operation/cpp/test_main.cpp b/02-splay_operation/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/02-splay_operation/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/02-splay_operation/python/splay_operation.py b/02-splay_operation/python/splay_operation.py deleted file mode 100644 index db824661b5849f276a105cf8069eee4ca0e1f8a3..0000000000000000000000000000000000000000 --- a/02-splay_operation/python/splay_operation.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 - -class Node: - """Node in a binary tree `Tree`""" - - def __init__(self, key, left=None, right=None, parent=None): - self.key = key - self.parent = parent - self.left = left - self.right = right - if left is not None: left.parent = self - if right is not None: right.parent = self - -class Tree: - """A simple binary search tree""" - - def __init__(self, root=None): - self.root = root - - def rotate(self, node): - """ Rotate the given `node` up. - - Performs a single rotation of the edge between the given node - and its parent, choosing left or right rotation appropriately. - """ - if node.parent is not None: - if node.parent.left == node: - if node.right is not None: node.right.parent = node.parent - node.parent.left = node.right - node.right = node.parent - else: - if node.left is not None: node.left.parent = node.parent - node.parent.right = node.left - node.left = node.parent - if node.parent.parent is not None: - if node.parent.parent.left == node.parent: - node.parent.parent.left = node - else: - node.parent.parent.right = node - else: - self.root = node - node.parent.parent, node.parent = node, node.parent.parent - - def lookup(self, key): - """Look up the given key in the tree. - - Returns the node with the requested key or `None`. - """ - # TODO: Utilize splay suitably. - node = self.root - while node is not None: - if node.key == key: - return node - if key < node.key: - node = node.left - else: - node = node.right - return None - - def insert(self, key): - """Insert key into the tree. - - If the key is already present, do nothing. - """ - # TODO: Utilize splay suitably. - if self.root is None: - self.root = Node(key) - return - - node = self.root - while node.key != key: - if key < node.key: - if node.left is None: - node.left = Node(key, parent=node) - node = node.left - else: - if node.right is None: - node.right = Node(key, parent=node) - node = node.right - - def remove(self, key): - """Remove given key from the tree. - - It the key is not present, do nothing. - """ - # TODO: Utilize splay suitably. - node = self.root - while node is not None and node.key != key: - if key < node.key: - node = node.left - else: - node = node.right - - if node is not None: - if node.left is not None and node.right is not None: - replacement = node.right - while replacement.left is not None: - replacement = replacement.left - node.key = replacement.key - node = replacement - - replacement = node.left if node.left is not None else node.right - if node.parent is not None: - if node.parent.left == node: node.parent.left = replacement - else: node.parent.right = replacement - else: - self.root = replacement - if replacement is not None: - replacement.parent = node.parent - - def splay(self, node): - """Splay the given node. - - If a single rotation needs to be performed, perform it as the last rotation - (i.e., to move the splayed node to the root of the tree). - """ - # TODO: Implement - raise NotImplementedError diff --git a/02-splay_operation/python/splay_operation_test.py b/02-splay_operation/python/splay_operation_test.py deleted file mode 100644 index b342520d608ef65eec87adce7f927187defc6205..0000000000000000000000000000000000000000 --- a/02-splay_operation/python/splay_operation_test.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/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 - tree = Tree() - for elem in range(200000): - for _ in range(10): - tree.insert(elem) - -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 - tree = Tree() - for elem in range(0, 100000, 2): - tree.insert(elem) - - # Non-existing elements - for elem in range(1, 100000, 2): - for _ in range(10): - tree.remove(elem) - - # Existing elements - for elem in range(2, 100000, 2): - tree.remove(elem) - -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)) diff --git a/02-splay_operation/python/splay_tests.txt b/02-splay_operation/python/splay_tests.txt deleted file mode 100644 index 7f3259e5d6a4689f8e7cc608e450905b82647b7b..0000000000000000000000000000000000000000 --- a/02-splay_operation/python/splay_tests.txt +++ /dev/null @@ -1,62 +0,0 @@ -(3,(1,(0,(),()),(2,(),())),(4,(),())) 4 (4,(3,(1,(0,(),()),(2,(),())),()),()) -(6,(5,(),()),(8,(7,(),()),(9,(),()))) 5 (5,(),(6,(),(8,(7,(),()),(9,(),())))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(8,(),()))) 8 (8,(7,(3,(1,(0,(),()),(2,(),())),(5,(4,(),()),(6,(),()))),()),()) -(11,(9,(7,(6,(),()),(8,(),())),(10,(),())),(13,(12,(),()),(14,(),()))) 10 (10,(9,(7,(6,(),()),(8,(),())),()),(11,(),(13,(12,(),()),(14,(),())))) -(8,(6,(5,(),()),(7,(),())),(10,(9,(),()),(12,(11,(),()),(13,(),())))) 9 (9,(8,(6,(5,(),()),(7,(),())),()),(10,(),(12,(11,(),()),(13,(),())))) -(16,(12,(11,(),()),(14,(13,(),()),(15,(),()))),(18,(17,(),()),(19,(),()))) 11 (11,(),(12,(),(16,(14,(13,(),()),(15,(),())),(18,(17,(),()),(19,(),()))))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(11,(9,(8,(),()),(10,(),())),(12,(),())))) 12 (12,(3,(1,(0,(),()),(2,(),())),(11,(7,(5,(4,(),()),(6,(),())),(9,(8,(),()),(10,(),()))),())),()) -(17,(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(16,(),()))),(19,(18,(),()),(20,(),()))) 16 (16,(15,(11,(9,(8,(),()),(10,(),())),(13,(12,(),()),(14,(),()))),()),(17,(),(19,(18,(),()),(20,(),())))) -(9,(7,(6,(),()),(8,(),())),(15,(13,(11,(10,(),()),(12,(),())),(14,(),())),(17,(16,(),()),(18,(),())))) 14 (14,(9,(7,(6,(),()),(8,(),())),(13,(11,(10,(),()),(12,(),())),())),(15,(),(17,(16,(),()),(18,(),())))) -(23,(19,(17,(15,(14,(),()),(16,(),())),(18,(),())),(21,(20,(),()),(22,(),()))),(25,(24,(),()),(26,(),()))) 18 (18,(17,(15,(14,(),()),(16,(),())),()),(23,(19,(),(21,(20,(),()),(22,(),()))),(25,(24,(),()),(26,(),())))) -(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),(14,(13,(),()),(16,(15,(),()),(17,(),()))))) 13 (13,(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),())),(14,(),(16,(15,(),()),(17,(),())))) -(22,(16,(14,(13,(),()),(15,(),())),(18,(17,(),()),(20,(19,(),()),(21,(),())))),(24,(23,(),()),(25,(),()))) 17 (17,(16,(14,(13,(),()),(15,(),())),()),(22,(18,(),(20,(19,(),()),(21,(),()))),(24,(23,(),()),(25,(),())))) -(14,(12,(11,(),()),(13,(),())),(20,(16,(15,(),()),(18,(17,(),()),(19,(),()))),(22,(21,(),()),(23,(),())))) 15 (15,(14,(12,(11,(),()),(13,(),())),()),(16,(),(20,(18,(17,(),()),(19,(),())),(22,(21,(),()),(23,(),()))))) -(28,(24,(20,(19,(),()),(22,(21,(),()),(23,(),()))),(26,(25,(),()),(27,(),()))),(30,(29,(),()),(31,(),()))) 19 (19,(),(28,(20,(),(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(27,(),())))),(30,(29,(),()),(31,(),())))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(16,(),()))))) 16 (16,(7,(3,(1,(0,(),()),(2,(),())),(5,(4,(),()),(6,(),()))),(15,(11,(9,(8,(),()),(10,(),())),(13,(12,(),()),(14,(),()))),())),()) -(25,(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(23,(21,(20,(),()),(22,(),())),(24,(),())))),(27,(26,(),()),(28,(),()))) 24 (24,(15,(13,(12,(),()),(14,(),())),(23,(19,(17,(16,(),()),(18,(),())),(21,(20,(),()),(22,(),()))),())),(25,(),(27,(26,(),()),(28,(),())))) -(11,(9,(8,(),()),(10,(),())),(21,(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(20,(),()))),(23,(22,(),()),(24,(),())))) 20 (20,(11,(9,(8,(),()),(10,(),())),(19,(15,(13,(12,(),()),(14,(),())),(17,(16,(),()),(18,(),()))),())),(21,(),(23,(22,(),()),(24,(),())))) -(33,(29,(23,(21,(20,(),()),(22,(),())),(27,(25,(24,(),()),(26,(),())),(28,(),()))),(31,(30,(),()),(32,(),()))),(35,(34,(),()),(36,(),()))) 28 (28,(27,(23,(21,(20,(),()),(22,(),())),(25,(24,(),()),(26,(),()))),()),(29,(),(33,(31,(30,(),()),(32,(),())),(35,(34,(),()),(36,(),()))))) -(9,(7,(6,(),()),(8,(),())),(13,(11,(10,(),()),(12,(),())),(19,(17,(15,(14,(),()),(16,(),())),(18,(),())),(21,(20,(),()),(22,(),()))))) 18 (18,(13,(9,(7,(6,(),()),(8,(),())),(11,(10,(),()),(12,(),()))),(17,(15,(14,(),()),(16,(),())),())),(19,(),(21,(20,(),()),(22,(),())))) -(31,(21,(19,(18,(),()),(20,(),())),(27,(25,(23,(22,(),()),(24,(),())),(26,(),())),(29,(28,(),()),(30,(),())))),(33,(32,(),()),(34,(),()))) 26 (26,(21,(19,(18,(),()),(20,(),())),(25,(23,(22,(),()),(24,(),())),())),(31,(27,(),(29,(28,(),()),(30,(),()))),(33,(32,(),()),(34,(),())))) -(17,(15,(14,(),()),(16,(),())),(27,(23,(21,(19,(18,(),()),(20,(),())),(22,(),())),(25,(24,(),()),(26,(),()))),(29,(28,(),()),(30,(),())))) 22 (22,(17,(15,(14,(),()),(16,(),())),(21,(19,(18,(),()),(20,(),())),())),(27,(23,(),(25,(24,(),()),(26,(),()))),(29,(28,(),()),(30,(),())))) -(39,(35,(31,(29,(27,(26,(),()),(28,(),())),(30,(),())),(33,(32,(),()),(34,(),()))),(37,(36,(),()),(38,(),()))),(41,(40,(),()),(42,(),()))) 30 (30,(29,(27,(26,(),()),(28,(),())),()),(35,(31,(),(33,(32,(),()),(34,(),()))),(39,(37,(36,(),()),(38,(),())),(41,(40,(),()),(42,(),()))))) -(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),(16,(14,(13,(),()),(15,(),())),(18,(17,(),()),(20,(19,(),()),(21,(),())))))) 17 (17,(12,(8,(6,(5,(),()),(7,(),())),(10,(9,(),()),(11,(),()))),(16,(14,(13,(),()),(15,(),())),())),(18,(),(20,(19,(),()),(21,(),())))) -(30,(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(28,(27,(),()),(29,(),()))))),(32,(31,(),()),(33,(),()))) 25 (25,(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),())),(30,(26,(),(28,(27,(),()),(29,(),()))),(32,(31,(),()),(33,(),())))) -(16,(14,(13,(),()),(15,(),())),(26,(20,(18,(17,(),()),(19,(),())),(22,(21,(),()),(24,(23,(),()),(25,(),())))),(28,(27,(),()),(29,(),())))) 21 (21,(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),())),(26,(22,(),(24,(23,(),()),(25,(),()))),(28,(27,(),()),(29,(),())))) -(38,(34,(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(32,(31,(),()),(33,(),())))),(36,(35,(),()),(37,(),()))),(40,(39,(),()),(41,(),()))) 29 (29,(28,(26,(25,(),()),(27,(),())),()),(34,(30,(),(32,(31,(),()),(33,(),()))),(38,(36,(35,(),()),(37,(),())),(40,(39,(),()),(41,(),()))))) -(14,(12,(11,(),()),(13,(),())),(18,(16,(15,(),()),(17,(),())),(24,(20,(19,(),()),(22,(21,(),()),(23,(),()))),(26,(25,(),()),(27,(),()))))) 19 (19,(18,(14,(12,(11,(),()),(13,(),())),(16,(15,(),()),(17,(),()))),()),(20,(),(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(27,(),()))))) -(36,(26,(24,(23,(),()),(25,(),())),(32,(28,(27,(),()),(30,(29,(),()),(31,(),()))),(34,(33,(),()),(35,(),())))),(38,(37,(),()),(39,(),()))) 27 (27,(26,(24,(23,(),()),(25,(),())),()),(36,(28,(),(32,(30,(29,(),()),(31,(),())),(34,(33,(),()),(35,(),())))),(38,(37,(),()),(39,(),())))) -(22,(20,(19,(),()),(21,(),())),(32,(28,(24,(23,(),()),(26,(25,(),()),(27,(),()))),(30,(29,(),()),(31,(),()))),(34,(33,(),()),(35,(),())))) 23 (23,(22,(20,(19,(),()),(21,(),())),()),(32,(24,(),(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(31,(),())))),(34,(33,(),()),(35,(),())))) -(44,(40,(36,(32,(31,(),()),(34,(33,(),()),(35,(),()))),(38,(37,(),()),(39,(),()))),(42,(41,(),()),(43,(),()))),(46,(45,(),()),(47,(),()))) 31 (31,(),(40,(32,(),(36,(34,(33,(),()),(35,(),())),(38,(37,(),()),(39,(),())))),(44,(42,(41,(),()),(43,(),())),(46,(45,(),()),(47,(),()))))) -(3,(1,(0,(),()),(2,(),())),(7,(5,(4,(),()),(6,(),())),(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(20,(),())))))) 20 (20,(3,(1,(0,(),()),(2,(),())),(11,(7,(5,(4,(),()),(6,(),())),(9,(8,(),()),(10,(),()))),(19,(15,(13,(12,(),()),(14,(),())),(17,(16,(),()),(18,(),()))),()))),()) -(37,(23,(21,(20,(),()),(22,(),())),(27,(25,(24,(),()),(26,(),())),(31,(29,(28,(),()),(30,(),())),(35,(33,(32,(),()),(34,(),())),(36,(),()))))),(39,(38,(),()),(40,(),()))) 36 (36,(27,(23,(21,(20,(),()),(22,(),())),(25,(24,(),()),(26,(),()))),(35,(31,(29,(28,(),()),(30,(),())),(33,(32,(),()),(34,(),()))),())),(37,(),(39,(38,(),()),(40,(),())))) -(15,(13,(12,(),()),(14,(),())),(29,(19,(17,(16,(),()),(18,(),())),(23,(21,(20,(),()),(22,(),())),(27,(25,(24,(),()),(26,(),())),(28,(),())))),(31,(30,(),()),(32,(),())))) 28 (28,(15,(13,(12,(),()),(14,(),())),(19,(17,(16,(),()),(18,(),())),(27,(23,(21,(20,(),()),(22,(),())),(25,(24,(),()),(26,(),()))),()))),(29,(),(31,(30,(),()),(32,(),())))) -(49,(45,(35,(33,(32,(),()),(34,(),())),(39,(37,(36,(),()),(38,(),())),(43,(41,(40,(),()),(42,(),())),(44,(),())))),(47,(46,(),()),(48,(),()))),(51,(50,(),()),(52,(),()))) 44 (44,(35,(33,(32,(),()),(34,(),())),(43,(39,(37,(36,(),()),(38,(),())),(41,(40,(),()),(42,(),()))),())),(49,(45,(),(47,(46,(),()),(48,(),()))),(51,(50,(),()),(52,(),())))) -(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(25,(19,(17,(16,(),()),(18,(),())),(23,(21,(20,(),()),(22,(),())),(24,(),()))),(27,(26,(),()),(28,(),()))))) 24 (24,(11,(9,(8,(),()),(10,(),())),(15,(13,(12,(),()),(14,(),())),(23,(19,(17,(16,(),()),(18,(),())),(21,(20,(),()),(22,(),()))),()))),(25,(),(27,(26,(),()),(28,(),())))) -(45,(31,(29,(28,(),()),(30,(),())),(41,(35,(33,(32,(),()),(34,(),())),(39,(37,(36,(),()),(38,(),())),(40,(),()))),(43,(42,(),()),(44,(),())))),(47,(46,(),()),(48,(),()))) 40 (40,(31,(29,(28,(),()),(30,(),())),(39,(35,(33,(32,(),()),(34,(),())),(37,(36,(),()),(38,(),()))),())),(45,(41,(),(43,(42,(),()),(44,(),()))),(47,(46,(),()),(48,(),())))) -(23,(21,(20,(),()),(22,(),())),(37,(33,(27,(25,(24,(),()),(26,(),())),(31,(29,(28,(),()),(30,(),())),(32,(),()))),(35,(34,(),()),(36,(),()))),(39,(38,(),()),(40,(),())))) 32 (32,(23,(21,(20,(),()),(22,(),())),(31,(27,(25,(24,(),()),(26,(),())),(29,(28,(),()),(30,(),()))),())),(33,(),(37,(35,(34,(),()),(36,(),())),(39,(38,(),()),(40,(),()))))) -(57,(53,(49,(43,(41,(40,(),()),(42,(),())),(47,(45,(44,(),()),(46,(),())),(48,(),()))),(51,(50,(),()),(52,(),()))),(55,(54,(),()),(56,(),()))),(59,(58,(),()),(60,(),()))) 48 (48,(47,(43,(41,(40,(),()),(42,(),())),(45,(44,(),()),(46,(),()))),()),(57,(49,(),(53,(51,(50,(),()),(52,(),())),(55,(54,(),()),(56,(),())))),(59,(58,(),()),(60,(),())))) -(9,(7,(6,(),()),(8,(),())),(13,(11,(10,(),()),(12,(),())),(17,(15,(14,(),()),(16,(),())),(23,(21,(19,(18,(),()),(20,(),())),(22,(),())),(25,(24,(),()),(26,(),())))))) 22 (22,(9,(7,(6,(),()),(8,(),())),(17,(13,(11,(10,(),()),(12,(),())),(15,(14,(),()),(16,(),()))),(21,(19,(18,(),()),(20,(),())),()))),(23,(),(25,(24,(),()),(26,(),())))) -(43,(29,(27,(26,(),()),(28,(),())),(33,(31,(30,(),()),(32,(),())),(39,(37,(35,(34,(),()),(36,(),())),(38,(),())),(41,(40,(),()),(42,(),()))))),(45,(44,(),()),(46,(),()))) 38 (38,(33,(29,(27,(26,(),()),(28,(),())),(31,(30,(),()),(32,(),()))),(37,(35,(34,(),()),(36,(),())),())),(43,(39,(),(41,(40,(),()),(42,(),()))),(45,(44,(),()),(46,(),())))) -(21,(19,(18,(),()),(20,(),())),(35,(25,(23,(22,(),()),(24,(),())),(31,(29,(27,(26,(),()),(28,(),())),(30,(),())),(33,(32,(),()),(34,(),())))),(37,(36,(),()),(38,(),())))) 30 (30,(21,(19,(18,(),()),(20,(),())),(25,(23,(22,(),()),(24,(),())),(29,(27,(26,(),()),(28,(),())),()))),(35,(31,(),(33,(32,(),()),(34,(),()))),(37,(36,(),()),(38,(),())))) -(55,(51,(41,(39,(38,(),()),(40,(),())),(47,(45,(43,(42,(),()),(44,(),())),(46,(),())),(49,(48,(),()),(50,(),())))),(53,(52,(),()),(54,(),()))),(57,(56,(),()),(58,(),()))) 46 (46,(41,(39,(38,(),()),(40,(),())),(45,(43,(42,(),()),(44,(),())),())),(55,(51,(47,(),(49,(48,(),()),(50,(),()))),(53,(52,(),()),(54,(),()))),(57,(56,(),()),(58,(),())))) -(17,(15,(14,(),()),(16,(),())),(21,(19,(18,(),()),(20,(),())),(31,(27,(25,(23,(22,(),()),(24,(),())),(26,(),())),(29,(28,(),()),(30,(),()))),(33,(32,(),()),(34,(),()))))) 26 (26,(17,(15,(14,(),()),(16,(),())),(21,(19,(18,(),()),(20,(),())),(25,(23,(22,(),()),(24,(),())),()))),(31,(27,(),(29,(28,(),()),(30,(),()))),(33,(32,(),()),(34,(),())))) -(51,(37,(35,(34,(),()),(36,(),())),(47,(43,(41,(39,(38,(),()),(40,(),())),(42,(),())),(45,(44,(),()),(46,(),()))),(49,(48,(),()),(50,(),())))),(53,(52,(),()),(54,(),()))) 42 (42,(37,(35,(34,(),()),(36,(),())),(41,(39,(38,(),()),(40,(),())),())),(51,(47,(43,(),(45,(44,(),()),(46,(),()))),(49,(48,(),()),(50,(),()))),(53,(52,(),()),(54,(),())))) -(29,(27,(26,(),()),(28,(),())),(43,(39,(35,(33,(31,(30,(),()),(32,(),())),(34,(),())),(37,(36,(),()),(38,(),()))),(41,(40,(),()),(42,(),()))),(45,(44,(),()),(46,(),())))) 34 (34,(29,(27,(26,(),()),(28,(),())),(33,(31,(30,(),()),(32,(),())),())),(39,(35,(),(37,(36,(),()),(38,(),()))),(43,(41,(40,(),()),(42,(),())),(45,(44,(),()),(46,(),()))))) -(63,(59,(55,(51,(49,(47,(46,(),()),(48,(),())),(50,(),())),(53,(52,(),()),(54,(),()))),(57,(56,(),()),(58,(),()))),(61,(60,(),()),(62,(),()))),(65,(64,(),()),(66,(),()))) 50 (50,(49,(47,(46,(),()),(48,(),())),()),(63,(55,(51,(),(53,(52,(),()),(54,(),()))),(59,(57,(56,(),()),(58,(),())),(61,(60,(),()),(62,(),())))),(65,(64,(),()),(66,(),())))) -(8,(6,(5,(),()),(7,(),())),(12,(10,(9,(),()),(11,(),())),(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),(22,(21,(),()),(24,(23,(),()),(25,(),()))))))) 21 (21,(8,(6,(5,(),()),(7,(),())),(16,(12,(10,(9,(),()),(11,(),())),(14,(13,(),()),(15,(),()))),(20,(18,(17,(),()),(19,(),())),()))),(22,(),(24,(23,(),()),(25,(),())))) -(42,(28,(26,(25,(),()),(27,(),())),(32,(30,(29,(),()),(31,(),())),(36,(34,(33,(),()),(35,(),())),(38,(37,(),()),(40,(39,(),()),(41,(),())))))),(44,(43,(),()),(45,(),()))) 37 (37,(32,(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(31,(),()))),(36,(34,(33,(),()),(35,(),())),())),(42,(38,(),(40,(39,(),()),(41,(),()))),(44,(43,(),()),(45,(),())))) -(20,(18,(17,(),()),(19,(),())),(34,(24,(22,(21,(),()),(23,(),())),(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(32,(31,(),()),(33,(),()))))),(36,(35,(),()),(37,(),())))) 29 (29,(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),(28,(26,(25,(),()),(27,(),())),()))),(34,(30,(),(32,(31,(),()),(33,(),()))),(36,(35,(),()),(37,(),())))) -(54,(50,(40,(38,(37,(),()),(39,(),())),(44,(42,(41,(),()),(43,(),())),(46,(45,(),()),(48,(47,(),()),(49,(),()))))),(52,(51,(),()),(53,(),()))),(56,(55,(),()),(57,(),()))) 45 (45,(40,(38,(37,(),()),(39,(),())),(44,(42,(41,(),()),(43,(),())),())),(54,(50,(46,(),(48,(47,(),()),(49,(),()))),(52,(51,(),()),(53,(),()))),(56,(55,(),()),(57,(),())))) -(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),(30,(24,(22,(21,(),()),(23,(),())),(26,(25,(),()),(28,(27,(),()),(29,(),())))),(32,(31,(),()),(33,(),()))))) 25 (25,(16,(14,(13,(),()),(15,(),())),(20,(18,(17,(),()),(19,(),())),(24,(22,(21,(),()),(23,(),())),()))),(30,(26,(),(28,(27,(),()),(29,(),()))),(32,(31,(),()),(33,(),())))) -(50,(36,(34,(33,(),()),(35,(),())),(46,(40,(38,(37,(),()),(39,(),())),(42,(41,(),()),(44,(43,(),()),(45,(),())))),(48,(47,(),()),(49,(),())))),(52,(51,(),()),(53,(),()))) 41 (41,(36,(34,(33,(),()),(35,(),())),(40,(38,(37,(),()),(39,(),())),())),(50,(46,(42,(),(44,(43,(),()),(45,(),()))),(48,(47,(),()),(49,(),()))),(52,(51,(),()),(53,(),())))) -(28,(26,(25,(),()),(27,(),())),(42,(38,(32,(30,(29,(),()),(31,(),())),(34,(33,(),()),(36,(35,(),()),(37,(),())))),(40,(39,(),()),(41,(),()))),(44,(43,(),()),(45,(),())))) 33 (33,(28,(26,(25,(),()),(27,(),())),(32,(30,(29,(),()),(31,(),())),())),(38,(34,(),(36,(35,(),()),(37,(),()))),(42,(40,(39,(),()),(41,(),())),(44,(43,(),()),(45,(),()))))) -(62,(58,(54,(48,(46,(45,(),()),(47,(),())),(50,(49,(),()),(52,(51,(),()),(53,(),())))),(56,(55,(),()),(57,(),()))),(60,(59,(),()),(61,(),()))),(64,(63,(),()),(65,(),()))) 49 (49,(48,(46,(45,(),()),(47,(),())),()),(62,(54,(50,(),(52,(51,(),()),(53,(),()))),(58,(56,(55,(),()),(57,(),())),(60,(59,(),()),(61,(),())))),(64,(63,(),()),(65,(),())))) -(14,(12,(11,(),()),(13,(),())),(18,(16,(15,(),()),(17,(),())),(22,(20,(19,(),()),(21,(),())),(28,(24,(23,(),()),(26,(25,(),()),(27,(),()))),(30,(29,(),()),(31,(),())))))) 23 (23,(14,(12,(11,(),()),(13,(),())),(22,(18,(16,(15,(),()),(17,(),())),(20,(19,(),()),(21,(),()))),())),(24,(),(28,(26,(25,(),()),(27,(),())),(30,(29,(),()),(31,(),()))))) -(48,(34,(32,(31,(),()),(33,(),())),(38,(36,(35,(),()),(37,(),())),(44,(40,(39,(),()),(42,(41,(),()),(43,(),()))),(46,(45,(),()),(47,(),()))))),(50,(49,(),()),(51,(),()))) 39 (39,(38,(34,(32,(31,(),()),(33,(),())),(36,(35,(),()),(37,(),()))),()),(48,(40,(),(44,(42,(41,(),()),(43,(),())),(46,(45,(),()),(47,(),())))),(50,(49,(),()),(51,(),())))) -(26,(24,(23,(),()),(25,(),())),(40,(30,(28,(27,(),()),(29,(),())),(36,(32,(31,(),()),(34,(33,(),()),(35,(),()))),(38,(37,(),()),(39,(),())))),(42,(41,(),()),(43,(),())))) 31 (31,(26,(24,(23,(),()),(25,(),())),(30,(28,(27,(),()),(29,(),())),())),(40,(32,(),(36,(34,(33,(),()),(35,(),())),(38,(37,(),()),(39,(),())))),(42,(41,(),()),(43,(),())))) -(60,(56,(46,(44,(43,(),()),(45,(),())),(52,(48,(47,(),()),(50,(49,(),()),(51,(),()))),(54,(53,(),()),(55,(),())))),(58,(57,(),()),(59,(),()))),(62,(61,(),()),(63,(),()))) 47 (47,(46,(44,(43,(),()),(45,(),())),()),(60,(56,(48,(),(52,(50,(49,(),()),(51,(),())),(54,(53,(),()),(55,(),())))),(58,(57,(),()),(59,(),()))),(62,(61,(),()),(63,(),())))) -(22,(20,(19,(),()),(21,(),())),(26,(24,(23,(),()),(25,(),())),(36,(32,(28,(27,(),()),(30,(29,(),()),(31,(),()))),(34,(33,(),()),(35,(),()))),(38,(37,(),()),(39,(),()))))) 27 (27,(22,(20,(19,(),()),(21,(),())),(26,(24,(23,(),()),(25,(),())),())),(36,(28,(),(32,(30,(29,(),()),(31,(),())),(34,(33,(),()),(35,(),())))),(38,(37,(),()),(39,(),())))) -(56,(42,(40,(39,(),()),(41,(),())),(52,(48,(44,(43,(),()),(46,(45,(),()),(47,(),()))),(50,(49,(),()),(51,(),()))),(54,(53,(),()),(55,(),())))),(58,(57,(),()),(59,(),()))) 43 (43,(42,(40,(39,(),()),(41,(),())),()),(56,(52,(44,(),(48,(46,(45,(),()),(47,(),())),(50,(49,(),()),(51,(),())))),(54,(53,(),()),(55,(),()))),(58,(57,(),()),(59,(),())))) -(34,(32,(31,(),()),(33,(),())),(48,(44,(40,(36,(35,(),()),(38,(37,(),()),(39,(),()))),(42,(41,(),()),(43,(),()))),(46,(45,(),()),(47,(),()))),(50,(49,(),()),(51,(),())))) 35 (35,(34,(32,(31,(),()),(33,(),())),()),(44,(36,(),(40,(38,(37,(),()),(39,(),())),(42,(41,(),()),(43,(),())))),(48,(46,(45,(),()),(47,(),())),(50,(49,(),()),(51,(),()))))) -(68,(64,(60,(56,(52,(51,(),()),(54,(53,(),()),(55,(),()))),(58,(57,(),()),(59,(),()))),(62,(61,(),()),(63,(),()))),(66,(65,(),()),(67,(),()))),(70,(69,(),()),(71,(),()))) 51 (51,(),(68,(60,(52,(),(56,(54,(53,(),()),(55,(),())),(58,(57,(),()),(59,(),())))),(64,(62,(61,(),()),(63,(),())),(66,(65,(),()),(67,(),())))),(70,(69,(),()),(71,(),())))) diff --git a/02-splay_operation/task.md b/02-splay_operation/task.md deleted file mode 100644 index ea234ca633f4dfd9ff1da16039e477c5eaa34ad6..0000000000000000000000000000000000000000 --- a/02-splay_operation/task.md +++ /dev/null @@ -1,7 +0,0 @@ -Given an implementation of a binary search tree including parent pointers: -- implement `splay` method, possibly utilizing the provided `rotate` operation - performing a single rotation; -- update `lookup`, `insert` and `remove` methods to utilize it correctly. - -You should submit the `splay_operation.*` file (but not the -`splay_operation_test.*`). diff --git a/03-splay_experiment/cpp/Makefile b/03-splay_experiment/cpp/Makefile deleted file mode 100644 index 565566bf6994e0d3c31ecd0d9f6d02d5e5c14e70..0000000000000000000000000000000000000000 --- a/03-splay_experiment/cpp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -test: splay_experiment - -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -I$(INCLUDE) - -splay_experiment: splay_operation.h splay_experiment.cpp $(INCLUDE)/random.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $^ -o $@ - -clean: - rm -f splay_experiment - -.PHONY: clean test diff --git a/03-splay_experiment/cpp/random.h b/03-splay_experiment/cpp/random.h deleted file mode 100644 index 5ef10aeb1fe7e58a48277fb3565169ec267d43d9..0000000000000000000000000000000000000000 --- a/03-splay_experiment/cpp/random.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef DS1_RANDOM_H -#define DS1_RANDOM_H - -#include <cstdint> - -/* - * This is the xoroshiro128+ random generator, designed in 2016 by David Blackman - * and Sebastiano Vigna, distributed under the CC-0 license. For more details, - * see http://vigna.di.unimi.it/xorshift/. - * - * Rewritten to C++ by Martin Mares, also placed under CC-0. - */ - -class RandomGen { - uint64_t state[2]; - - uint64_t rotl(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - public: - // Initialize the generator, set its seed and warm it up. - RandomGen(unsigned int seed) - { - state[0] = seed * 0xdeadbeef; - state[1] = seed ^ 0xc0de1234; - for (int i=0; i<100; i++) - next_u64(); - } - - // Generate a random 64-bit number. - uint64_t next_u64(void) - { - uint64_t s0 = state[0], s1 = state[1]; - uint64_t result = s0 + s1; - s1 ^= s0; - state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); - state[1] = rotl(s1, 36); - return result; - } - - // Generate a random 32-bit number. - uint32_t next_u32(void) - { - return next_u64() >> 11; - } - - // Generate a number between 0 and range-1. - unsigned int next_range(unsigned int range) - { - /* - * This is not perfectly uniform, unless the range is a power of two. - * However, for 64-bit random values and 32-bit ranges, the bias is - * insignificant. - */ - return next_u64() % range; - } -}; - -#endif diff --git a/03-splay_experiment/cpp/splay_experiment.cpp b/03-splay_experiment/cpp/splay_experiment.cpp deleted file mode 100644 index a47da8b11bcff20db58805a330f8b05d0a6c15b5..0000000000000000000000000000000000000000 --- a/03-splay_experiment/cpp/splay_experiment.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include <algorithm> -#include <functional> -#include <string> -#include <utility> -#include <vector> -#include <iostream> -#include <cmath> - -#include "splay_operation.h" -#include "random.h" - -using namespace std; - -/* - * A modified Splay tree for benchmarking. - * - * We inherit the implementation of operations from the Tree class - * and extend it by keeping statistics on the number of splay operations - * and the total number of rotations. Also, if naive is turned on, - * splay uses only single rotations. - * - * Please make sure that your Tree class defines the rotate() and splay() - * methods as virtual. - */ - -class BenchmarkingTree : public Tree { -public: - int num_operations; - int num_rotations; - bool do_naive; - - BenchmarkingTree(bool naive=false) - { - do_naive = naive; - reset(); - } - - void reset() - { - num_operations = 0; - num_rotations = 0; - } - - void rotate(Node *node) override - { - num_rotations++; - Tree::rotate(node); - } - - void splay(Node *node) override - { - num_operations++; - if (do_naive) { - while (node->parent) - rotate(node); - } else { - Tree::splay(node); - } - } - - // Return the average number of rotations per operation. - double rot_per_op() - { - if (num_operations > 0) - return (double) num_rotations / num_operations; - else - return 0; - } -}; - -bool naive; // Use of naive rotations requested -RandomGen *rng; // Random generator object - -void test_sequential() -{ - for (int n=100; n<=3000; n+=100) { - BenchmarkingTree tree = BenchmarkingTree(naive); - - for (int x=0; x<n; x++) - tree.insert(x); - - for (int i=0; i<5; i++) - for (int x=0; x<n; x++) - tree.lookup(x); - - cout << n << " " << tree.rot_per_op() << endl; - } -} - -// An auxiliary function for generating a random permutation. -vector<int> random_permutation(int n) -{ - vector<int> perm; - for (int i=0; i<n; i++) - perm.push_back(i); - for (int i=0; i<n-1; i++) - swap(perm[i], perm[i + rng->next_range(n-i)]); - return perm; -} - -void test_random() -{ - for (int e=32; e<=64; e++) { - int n = (int) pow(2, e/4.); - BenchmarkingTree tree = BenchmarkingTree(naive); - - vector<int> perm = random_permutation(n); - for (int x : perm) - tree.insert(x); - - for (int i=0; i<5*n; i++) - tree.lookup(rng->next_range(n)); - - cout << n << " " << tree.rot_per_op() << endl; - } -} - -/* - * An auxiliary function for constructing arithmetic progressions. - * The vector seq will be modified to contain an arithmetic progression - * of elements in interval [A,B] starting from position s with step inc. - */ -void make_progression(vector<int> &seq, int A, int B, int s, int inc) -{ - for (int i=0; i<seq.size(); i++) - while (seq[i] >= A && seq[i] <= B && s + inc*(seq[i]-A) != i) - swap(seq[i], seq[s + inc*(seq[i] - A)]); -} - -void test_subset_s(int sub) -{ - for (int e=32; e<=64; e++) { - int n = (int) pow(2, e/4.); - if (n < sub) - continue; - - // We will insert elements in order, which contain several - // arithmetic progressions interspersed with random elements. - vector<int> seq = random_permutation(n); - make_progression(seq, n/4, n/4 + n/20, n/10, 1); - make_progression(seq, n/2, n/2 + n/20, n/10, -1); - make_progression(seq, 3*n/4, 3*n/4 + n/20, n/2, -4); - make_progression(seq, 17*n/20, 17*n/20 + n/20, 2*n/5, 5); - - BenchmarkingTree tree = BenchmarkingTree(naive); - for (int x : seq) - tree.insert(x); - tree.reset(); - - for (int i=0; i<10000; i++) - tree.lookup(seq[rng->next_range(sub)]); - - cout << sub << " " << n << " " << tree.rot_per_op() << endl; - } -} - -void test_subset() -{ - test_subset_s(10); - test_subset_s(100); - test_subset_s(1000); -} - -vector<pair<string, function<void()>>> tests = { - { "sequential", test_sequential }, - { "random", test_random }, - { "subset", test_subset }, -}; - -int main(int argc, char **argv) -{ - if (argc != 4) { - cerr << "Usage: " << argv[0] << " <test> <student-id> (std|naive)" << endl; - return 1; - } - - string which_test = argv[1]; - string id_str = argv[2]; - string mode = argv[3]; - - try { - rng = new RandomGen(stoi(id_str)); - } catch (...) { - cerr << "Invalid student ID" << endl; - return 1; - } - - if (mode == "std") - naive = false; - else if (mode == "naive") - naive = true; - else - { - cerr << "Last argument must be either 'std' or 'naive'" << endl; - return 1; - } - - for (const auto& test : tests) { - if (test.first == which_test) - { - cout.precision(12); - test.second(); - return 0; - } - } - cerr << "Unknown test " << which_test << endl; - return 1; -} diff --git a/03-splay_experiment/python/splay_experiment.py b/03-splay_experiment/python/splay_experiment.py deleted file mode 100644 index 8cf3d6d6af1564973bd17a43f2fa731cce083da7..0000000000000000000000000000000000000000 --- a/03-splay_experiment/python/splay_experiment.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import random - -from splay_operation import Tree - -class BenchmarkingTree(Tree): - """ A modified Splay tree for benchmarking. - - We inherit the implementation of operations from the Tree class - and extend it by keeping statistics on the number of splay operations - and the total number of rotations. Also, if naive is turned on, - splay uses only single rotations. - """ - - def __init__(self, naive=False): - Tree.__init__(self) - self.do_naive = naive - self.reset() - - def reset(self): - """Reset statistics.""" - self.num_rotations = 0; - self.num_operations = 0; - - def rotate(self, node): - self.num_rotations += 1 - Tree.rotate(self, node) - - def splay(self, node): - self.num_operations += 1 - if self.do_naive: - while node.parent is not None: - self.rotate(node) - else: - Tree.splay(self, node) - - def rot_per_op(self): - """Return the average number of rotations per operation.""" - if self.num_operations > 0: - return self.num_rotations / self.num_operations - else: - return 0 - -def test_sequential(): - for n in range(100, 3001, 100): - tree = BenchmarkingTree(naive) - for elem in range(n): - tree.insert(elem) - - for _ in range(5): - for elem in range(n): - tree.lookup(elem) - - print(n, tree.rot_per_op()) - -def test_random(): - for exp in range(32, 64): - n = int(2**(exp/4)) - tree = BenchmarkingTree(naive) - - for elem in random.sample(range(n), n): - tree.insert(elem) - - for _ in range(5*n): - tree.lookup(random.randrange(n)) - - print(n, tree.rot_per_op()) - -def make_progression(seq, A, B, s, inc): - """An auxiliary function for constructing arithmetic progressions. - - The array seq will be modified to contain an arithmetic progression - of elements in interval [A,B] starting from position s with step inc. - """ - for i in range(len(seq)): - while seq[i] >= A and seq[i] <= B and s + inc*(seq[i]-A) != i: - pos = s + inc*(seq[i]-A) - seq[i], seq[pos] = seq[pos], seq[i] - -def test_subset(): - for sub in [10, 100, 1000]: - for exp in range(32,64): - n = int(2**(exp/4)) - if n < sub: - continue - - # We will insert elements in order, which contain several - # arithmetic progressions interspersed with random elements. - seq = random.sample(range(n), n) - make_progression(seq, n//4, n//4 + n//20, n//10, 1) - make_progression(seq, n//2, n//2 + n//20, n//10, -1) - make_progression(seq, 3*n//4, 3*n//4 + n//20, n//2, -4) - make_progression(seq, 17*n//20, 17*n//20 + n//20, 2*n//5, 5) - - tree = BenchmarkingTree(naive) - for elem in seq: - tree.insert(elem) - tree.reset() - - for _ in range(10000): - tree.lookup(seq[random.randrange(sub)]) - - print(sub, n, tree.rot_per_op()) - -tests = { - "sequential": test_sequential, - "random": test_random, - "subset": test_subset, -} - -if len(sys.argv) == 4: - test, student_id = sys.argv[1], sys.argv[2] - if sys.argv[3] == "std": - naive = False - elif sys.argv[3] == "naive": - naive = True - else: - raise ValueError("Last argument must be either 'std' or 'naive'") - random.seed(student_id) - if test in tests: - tests[test]() - else: - raise ValueError("Unknown test {}".format(test)) -else: - raise ValueError("Usage: {} <test> <student-id> (std|naive)".format(sys.argv[0])) diff --git a/03-splay_experiment/task.md b/03-splay_experiment/task.md deleted file mode 100644 index 5b5bdcc89be2bd5d1cf7ee7e7fc186ac8d1678c9..0000000000000000000000000000000000000000 --- a/03-splay_experiment/task.md +++ /dev/null @@ -1,86 +0,0 @@ -## Goal - -The goal of this assignment is to evaluate your implementation of Splay trees -experimentally and to compare it with a "naive" implementation which splays -using single rotations only. - -You are given a test program (`splay_experiment`) which calls your -implementation from the previous assignment to perform the following -experiments: - -- _Sequential test:_ Insert _n_ elements sequentially and then repeatedly - find them all in sequential order. -- _Random test:_ Insert _n_ elements in random order and then find _5n_ - random elements. -- _Subset test:_ Insert a sequence of _n_ elements, which contains arithmetic - progressions interspersed with random elements. Then repeatedly access - a small subset of these elements in random order. Try this with subsets of - different cardinalities. - -The program tries each experiment with different values of _n_. In each try, -it prints the average number of rotations per splay operation. - -You should perform these experiments and write a report, which contains the following -plots of the measured data. Each plot should show the dependence of the average -number of rotations on the set size _n_. - -- The sequential test: one curve for the standard implementation, one for the naive one. -- The random test: one curve for the standard implementation, one for the naive one. -- The subset test: three curves for the standard implementation with different sizes - of the subset, three for the naive implementation with the same sizes. - -The report should discuss the experimental results and try to explain the observed -behavior using theory from the lectures. (If you want, you can carry out further -experiments to gain better understanding of the data structure and include these -in the report. This is strictly optional.) - -You should submit a PDF file with the report (and no source code). -You will get 1 temporary point upon submission if the file is syntantically correct; -proper points will be assigned later. - -## Test program - -The test program is given three arguments: -- The name of the test (`sequential`, `random`, `subset`). -- The random seed: you should use the last 2 digits of your student ID (you can find - it in the Study Information System – just click on the Personal data icon). Please - include the random seed in your report. -- The implementation to test (`std` or `naive`). - -The output of the program contains one line per experiment, which consists of: -- For the sequential and random test: the set size and the average number of rotations. -- For the subset test: the subset size, the set size, and the average number of rotations - per find. The initial insertions of the full set are not counted. - -## Your implementation - -Please use your implementation from the previous exercise. If you used C++, -you should declare the `splay()` and `rotate()` methods as virtual (they will -be overriden by the test program). Also, if you are performing a double rotation -directly instead of composing it from single rotations, you need to adjust -the `BenchmarkingTree` class accordingly. - -## Hints - -The following tools can be useful for producing nice plots: -- [pandas](https://pandas.pydata.org/) -- [matplotlib](https://matplotlib.org/) -- [gnuplot](http://www.gnuplot.info/) - -A quick checklist for plots: -- Is there a caption explaining what is plotted? -- Are the axes clearly labelled? Do they have value ranges and units? -- Have you mentioned that this axis has logarithmic scale? (Logarithmic graphs - are more fitting in some cases, but you should tell.) -- Is it clear which curve means what? -- Is it clear what are the measured points and what is an interpolated - curve between them? -- Are there any overlaps? (E.g., the most interesting part of the curve - hidden underneath a label?) - -In your discussion, please distinguish the following kinds of claims. -It should be always clear which is which: -- Experimental results (i.e., the raw data you obtained from the experiments) -- Theoretical facts (i.e., claims we have proved mathematically) -- Your hypotheses (e.g., when you claim that the graph looks like something is true, - but you are not able to prove rigorously that it always holds) diff --git a/04-ab_tree/cpp/Makefile b/04-ab_tree/cpp/Makefile deleted file mode 100644 index e6ab22807f87d47388cd149817ae6cc99fd3ee00..0000000000000000000000000000000000000000 --- a/04-ab_tree/cpp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -test: ab_tree_test - ./$< - -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare - -ab_tree_test: ab_tree_test.cpp ab_tree.h test_main.cpp - $(CXX) $(CXXFLAGS) $^ -o $@ - -clean: - rm -f ab_tree_test - -.PHONY: clean test diff --git a/04-ab_tree/cpp/ab_tree.h b/04-ab_tree/cpp/ab_tree.h deleted file mode 100644 index cec339122f9c094eb09f650eda2a04957d9cb4eb..0000000000000000000000000000000000000000 --- a/04-ab_tree/cpp/ab_tree.h +++ /dev/null @@ -1,124 +0,0 @@ -#include <limits> -#include <vector> -#include <iostream> - -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); - -/*** One node ***/ - -class ab_node { - public: - // Keys stored in this node and the corresponding children - // The vectors are large enough to accomodate one extra entry - // in overflowing nodes. - vector<ab_node *> children; - vector<int> keys; - - // If this node contains the given key, return true and set i to key's position. - // Otherwise return false and set i to the first key greater than the given one. - bool find_branch(int key, int &i) - { - i = 0; - while (i < keys.size() && keys[i] <= key) { - if (keys[i] == key) - return true; - i++; - } - return false; - } - - // Insert a new key at posision i and add a new child between keys i and i+1. - void insert_branch(int i, int key, ab_node *child) - { - keys.insert(keys.begin() + i, key); - children.insert(children.begin() + i + 1, child); - } - - // An auxiliary function for displaying a sub-tree under this node. - void show(int indent); -}; - -/*** Tree ***/ - -class ab_tree { - public: - int a; // Minimum allowed number of children - int b; // Maximum allowed number of children - ab_node *root; // Root node (even a tree with no keys has a root) - int num_nodes; // We keep track of how many nodes the tree has - - // Create a new node and return a pointer to it. - ab_node *new_node() - { - ab_node *n = new ab_node; - n->keys.reserve(b); - n->children.reserve(b+1); - num_nodes++; - return n; - } - - // Delete a given node, assuming that its children have been already unlinked. - void delete_node(ab_node *n) - { - num_nodes--; - delete n; - } - - // Constructor: initialize an empty tree with just the root. - ab_tree(int a, int b) - { - EXPECT(a >= 2 && b >= 2*a - 1, "Invalid values of a,b"); - this->a = a; - this->b = b; - num_nodes = 0; - // The root has no keys and one null child pointer. - root = new_node(); - root->children.push_back(nullptr); - } - - // An auxiliary function for deleting a subtree recursively. - void delete_tree(ab_node *n) - { - for (int i=0; i < n->children.size(); i++) - if (n->children[i]) - delete_tree(n->children[i]); - delete_node(n); - } - - // Destructor: delete all nodes. - ~ab_tree() - { - delete_tree(root); - EXPECT(num_nodes == 0, "Memory leak detected: some nodes were not deleted"); - } - - // Find a key: returns true if it is present in the tree. - bool find(int key) - { - ab_node *n = root; - while (n) { - int i; - if (n->find_branch(key, i)) - return true; - n = n->children[i]; - } - return false; - } - - // Display the tree on standard output in human-readable form. - void show(); - - // Check that the data structure satisfies all invariants. - void audit(); - - // Insert: add key to the tree (unless it was already present). - void insert(int key) - { - // FIXME: Implement - } -}; diff --git a/04-ab_tree/cpp/ab_tree_test.cpp b/04-ab_tree/cpp/ab_tree_test.cpp deleted file mode 100644 index 8b1651f5c2e5b33a367f49d033ecaa2da3b00c64..0000000000000000000000000000000000000000 --- a/04-ab_tree/cpp/ab_tree_test.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include <functional> -#include <cstdlib> -#include <vector> - -#include "ab_tree.h" - -// Debugging output: showing trees prettily on standard output. - -void ab_tree::show() -{ - root->show(0); - for (int i=0; i<70; i++) - cout << '='; - cout << endl; -} - -void ab_node::show(int indent) -{ - for (int i = children.size() - 1; i >= 0 ; i--) { - if (i < keys.size()) { - for (int j = 0; j < indent; j++) - cout << " "; - cout << keys[i] << endl; - } - if (children[i]) - children[i]->show(indent+1); - } -} - -// Invariant checks - -void audit_subtree(ab_tree *tree, ab_node *n, int key_min, int key_max, int depth, int &leaf_depth) -{ - if (!n) { - // Check that all leaves are on the same level. - if (leaf_depth < 0) - leaf_depth = depth; - else - EXPECT(depth == leaf_depth, "Leaves are not on the same level"); - return; - } - - // The number of children must be in the allowed range. - if (depth > 0) - EXPECT(n->children.size() >= tree->a, "Too few children"); - EXPECT(n->children.size() <= tree->b, "Too many children"); - - // We must have one more children than keys. - EXPECT(n->children.size() == n->keys.size() + 1, "Number of keys does not match number of children"); - - // Allow degenerate trees with 0 keys in the root. - if (n->children.size() == 1) - return; - - // Check order of keys: they must be increasing and bounded by the keys on the higher levels. - for (int i = 0; i < n->keys.size(); i++) { - EXPECT(n->keys[i] >= key_min && n->keys[i] <= key_max, "Wrong key order"); - EXPECT(i == 0 || n->keys[i-1] < n->keys[i], "Wrong key order"); - } - - // Call on children recursively. - for (int i = 0; i < n->children.size(); i++) { - int tmin, tmax; - if (i == 0) - tmin = key_min; - else - tmin = n->keys[i-1] + 1; - if (i < n->keys.size()) - tmax = n->keys[i] - 1; - else - tmax = key_max; - audit_subtree(tree, n->children[i], tmin, tmax, depth+1, leaf_depth); - } -} - -void ab_tree::audit() -{ - EXPECT(root, "Tree has no root"); - int leaf_depth = -1; - audit_subtree(this, root, numeric_limits<int>::min(), numeric_limits<int>::max(), 0, leaf_depth); -} - -// A basic test: insert a couple of keys and show how the tree evolves. - -void test_basic() -{ - cout << "## Basic test" << endl; - - ab_tree t(2, 3); - vector<int> keys = { 3, 1, 4, 5, 9, 2, 6, 8, 7, 0 }; - for (int k : keys) { - t.insert(k); - t.show(); - t.audit(); - EXPECT(t.find(k), "Inserted key disappeared"); - } - - for (int k : keys) - EXPECT(t.find(k), "Some keys are missing at the end"); -} - -// The main test: inserting a lot of keys and checking that they are really there. -// We will insert num_items keys from the set {1,...,range-1}, where range is a prime. - -void test_main(int a, int b, int range, int num_items) -{ - // Create a new tree. - cout << "## Test: a=" << a << " b=" << b << " range=" << range << " num_items=" << num_items << endl; - ab_tree t(a, b); - - int key = 1; - int step = (int)(range * 1.618); - int audit_time = 1; - - // Insert keys. - for (int i=1; i <= num_items; i++) { - t.insert(key); - // Audit the tree occasionally. - if (i == audit_time || i == num_items) { - // cout << "== Audit at " << i << endl; - // t.show(); - t.audit(); - audit_time = (int)(audit_time * 1.33) + 1; - } - key = (key + step) % range; - } - - // Check that the tree contains exactly the items it should contain. - key = 1; - for (int i=1; i < range; i++) { - bool found = t.find(key); - // cout << "Step #" << i << ": find(" << key << ") = " << found << endl; - EXPECT(found == (i <= num_items), "Tree contains wrong keys"); - key = (key + step) % range; - } -} - -/*** A list of all tests ***/ - -vector<pair<string, function<void()>>> tests = { - { "basic", [] { test_basic(); } }, - { "small-2,3", [] { test_main(2, 3, 997, 700); } }, - { "small-2,4", [] { test_main(2, 4, 997, 700); } }, - { "big-2,3", [] { test_main(2, 3, 999983, 700000); } }, - { "big-2,4", [] { test_main(2, 4, 999983, 700000); } }, - { "big-10,20", [] { test_main(10, 20, 999983, 700000); } }, - { "big-100,200", [] { test_main(100, 200, 999983, 700000); } }, -}; diff --git a/04-ab_tree/cpp/test_main.cpp b/04-ab_tree/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/04-ab_tree/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/04-ab_tree/python/ab_tree.py b/04-ab_tree/python/ab_tree.py deleted file mode 100644 index 83c17c490cb3d08b490f7ccfbed24e14235cf579..0000000000000000000000000000000000000000 --- a/04-ab_tree/python/ab_tree.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 - -class ABNode: - """Single node in an ABTree. - - Each node contains keys and childrens - (with one more children than there are keys). - """ - def __init__(self, keys = None, children = None): - self.keys = keys if keys is not None else [] - self.children = children if children is not None else [] - - def find_branch(self, key): - """ Try finding given key in this node. - - If this node contains the given key, returns (True, key_position). - If not, returns (False, first_position_with_key_greater_than_the_given). - """ - i = 0 - while (i < len(self.keys) and self.keys[i] < key): - i += 1 - - return (i < len(self.keys) and self.keys[i] == key, i) - - def insert_branch(self, i, key, child): - """ Insert a new key and a given child between keys i and i+1.""" - self.keys.insert(i, key) - self.children.insert(i + 1, child) - -class ABTree: - """A class representing the whole ABTree.""" - def __init__(self, a, b): - assert a >= 2 and b >= 2 * a - 1, "Invalid values of a, b: {}, {}".format(a, b) - self.a = a - self.b = b - self.root = ABNode(children=[None]) - - def find(self, key): - """Find a key in the tree. - - Returns True if the key is present, False otherwise. - """ - node = self.root - while node: - found, i = node.find_branch(key) - if found: return True - node = node.children[i] - return False - - def insert(self, key): - """Add a given key to the tree, unless already present.""" - # TODO: Implement - raise NotImplementedError diff --git a/04-ab_tree/python/ab_tree_test.py b/04-ab_tree/python/ab_tree_test.py deleted file mode 100644 index 5444dd9f6ad67eab9db825e531f682a411101cc8..0000000000000000000000000000000000000000 --- a/04-ab_tree/python/ab_tree_test.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python3 -import math -import sys - -from ab_tree import ABNode, ABTree - -def show(tree): - """Show a tree.""" - def show_node(node, indent): - for i in reversed(range(len(node.children))): - if i < len(node.keys): - print(" " * indent, node.keys[i], sep="") - if node.children[i]: - show_node(node.children[i], indent + 1) - - show_node(tree.root, 0) - print("=" * 70) - -def audit(tree): - """Invariant check for the given tree.""" - def audit_node(node, key_min, key_max, depth, leaf_depth): - if not node: - # Check that all leaves are on the same level. - if leaf_depth is None: - leaf_depth = depth - assert depth == leaf_depth, "Leaves are not on the same level" - - else: - # The number of children must be in the allowed range. - assert depth == 0 or len(node.children) >= tree.a, "Too few children" - assert len(node.children) <= tree.b, "Too many children" - - # We must have one more children than keys - assert len(node.children) == len(node.keys) + 1, "Number of keys does not match number of children" - - # Check that keys are increasing and in (key_min, key_max) range. - for i in range(len(node.keys)): - assert node.keys[i] > key_min and node.keys[i] < key_max, "Wrong key order" - assert i == 0 or node.keys[i - 1] < node.keys[i], "Wrong key order" - - # Check children recursively - for i in range(len(node.children)): - child_min = node.keys[i - 1] if i > 0 else key_min - child_max = node.keys[i] if i < len(node.keys) else key_max - leaf_depth = audit_node(node.children[i], child_min, child_max, depth + 1, leaf_depth) - - return leaf_depth - - assert tree.root, "Tree has no root" - audit_node(tree.root, -math.inf, math.inf, 0, None) - -def test_basic(): - """Insert a couple of keys and show how the tree evolves.""" - print("## Basic test") - - tree = ABTree(2, 3) - keys = [3, 1, 4, 5, 9, 2, 6, 8, 7, 0] - for key in keys: - tree.insert(key) - show(tree) - audit(tree) - assert tree.find(key), "Inserted key disappeared" - - for key in keys: - assert tree.find(key), "Some keys are missing at the end" - -def test_main(a, b, limit, num_items): - print("## Test: a={} b={} range={} num_items={}".format(a, b, limit, num_items)) - - tree = ABTree(a, b) - - # Insert keys - step = int(limit * 1.618) - key, audit_time = 1, 1 - for i in range(num_items): - tree.insert(key) - key = (key + step) % limit - - # Audit the tree occasionally - if i == audit_time or i + 1 == num_items: - audit(tree) - audit_time = int(audit_time * 1.33) + 1 - - # Check the content of the tree - key = 1 - for i in range(limit): - assert tree.find(key) == (i < num_items), "Tree contains wrong keys" - key = (key + step) % limit - -tests = [ - ("basic", test_basic), - ("small-2,3", lambda: test_main(2, 3, 997, 700)), - ("small-2,4", lambda: test_main(2, 4, 997, 700)), - ("big-2,3", lambda: test_main(2, 3, 99991, 70000)), - ("big-2,4", lambda: test_main(2, 4, 99991, 70000)), - ("big-10,20", lambda: test_main(10, 20, 99991, 70000)), - ("big-100,200", lambda: test_main(100, 200, 99991, 70000)), -] - -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)) diff --git a/04-ab_tree/task.md b/04-ab_tree/task.md deleted file mode 100644 index d204f3b036bb63a62061fdeaf4cc948c966b892e..0000000000000000000000000000000000000000 --- a/04-ab_tree/task.md +++ /dev/null @@ -1,7 +0,0 @@ -You are given a representation of _(a, b)-tree_ with a `find` operation, -and a representation of an _(a, b)-tree node_. - -Your goal is to implement an `insert` operation, which inserts the given -key in the tree (or does nothing if the key is already present). - -You should submit the `ab_tree.*` file (but not `ab_tree_test.*` files). diff --git a/05-fibonacci/cpp/Makefile b/05-fibonacci/cpp/Makefile deleted file mode 100644 index 9cab32dbc10ed125e3c9d371588e3dc5b3352658..0000000000000000000000000000000000000000 --- a/05-fibonacci/cpp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -test: fibonacci_test - ./$< - -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare - -fibonacci_test: fibonacci.h fibonacci_test.cpp test_main.cpp - $(CXX) $(CXXFLAGS) fibonacci_test.cpp test_main.cpp -o $@ - -clean:: - rm -f fibonacci_test - -.PHONY: clean test diff --git a/05-fibonacci/cpp/fibonacci.h b/05-fibonacci/cpp/fibonacci.h deleted file mode 100644 index ddee892c1ca87ca619775dbf284da3730b0be49a..0000000000000000000000000000000000000000 --- a/05-fibonacci/cpp/fibonacci.h +++ /dev/null @@ -1,204 +0,0 @@ -#include <vector> - -// 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 std::string& message); - -/* - * payload_t: Type used to allow storing user data in each heap node. - * - * priority_t: Type used to represent priority of the node. It must - * support comparison. - * - * In proper C++, the heap should be a template parametrized by these - * types, but we decided to keep the code as simple as possible. - */ -typedef int payload_t; -typedef int priority_t; - -struct FibonacciHeap; - -// Node of FibonacciHeap -struct Node { - payload_t payload; - priority_t priority() { return prio; } - - private: - priority_t prio; - - Node *parent = nullptr, *first_child = nullptr; - - // Pointers in a cyclic list of children of the parent. - Node *prev_sibling, *next_sibling; - - int rank = 0; - - Node(priority_t p, payload_t payload) : payload(payload), prio(p) { - next_sibling = prev_sibling = this; - } - - ~Node() {} - - friend class FibonacciHeap; - friend class HeapTests; -}; - -/* FibonacciHeap - * - * Implemented using a meta root to avoid special handling of the - * linked list of heap trees. - */ -struct FibonacciHeap { - // Check whether the heap is empty - bool is_empty() { return size == 0; } - - /* Insert a new node with given priority and payload and return it. - * - * `payload` can be used to store any data in the node. - */ - Node *insert(priority_t prio, payload_t payload = payload_t()) { - Node *n = new Node(prio, payload); - add_child(&meta_root, n); - size++; - return n; - } - - /* Extract node with minimum priority. - * - * Must not be called when the heap is empty. - * Returns pair (payload, priority) of the removed node. - */ - std::pair<payload_t, priority_t> extract_min() { - EXPECT(meta_root.rank > 0, "Cannot extract minimum of an empty heap."); - - // Find the tree whose root is minimal. - Node *min = meta_root.first_child; - Node *node = min->next_sibling; - while (node != meta_root.first_child) { - if (node->prio < min->prio) min = node; - node = node->next_sibling; - } - - // Add all its subtrees to the heap. - while (min->rank > 0) - add_child(&meta_root, remove(min->first_child)); - - // Remove the root. - std::pair<payload_t, priority_t> ret = { min->payload, min->prio }; - delete remove(min); - size--; - - // Finally, consolidate the heap. - consolidate(); - - return ret; - } - - /* Decrease priority of node to new_prio. - * - * new_prio must not be higher than node.prio. - */ - void decrease(Node *node, priority_t new_prio) { - EXPECT(node, "Cannot decrase null pointer."); - EXPECT(node->prio >= new_prio, "Decrase: new priority larget than old one."); - // TODO: Implement - } - - FibonacciHeap() : size(0), meta_root(priority_t(), payload_t()) {} - - ~FibonacciHeap() { - if (size == 0) return; - Node *next; - Node *n = meta_root.first_child; - do { - while (n->first_child) n = n->first_child; - next = (n->next_sibling != n) ? n->next_sibling : n->parent; - delete remove(n); - } while ((n = next) != &meta_root); - } - - protected: - // Consolidate heap during extract_min. - void consolidate() { - std::vector<Node*> buckets(max_rank(), nullptr); - while (meta_root.first_child) { - Node *node = remove(meta_root.first_child); - while (Node *&b = buckets.at(node->rank)) { - node = pair_trees(b, node); - b = nullptr; - } - buckets.at(node->rank) = node; - } - - for (Node *node : buckets) - if (node) add_child(&meta_root, node); - } - - // Join two trees of the same rank. - Node *pair_trees(Node *a, Node *b) { - if (a->prio > b->prio) std::swap(a, b); - add_child(a, b); - return a; - } - - // Return maximum possible rank of a tree in a heap of the current size. - virtual size_t max_rank() { - size_t ret = 1; - while ((1 << ret) <= size) ret++; - return ret; - } - - // Check whether a node is the root. - bool is_root(Node *n) { - return n->parent == &meta_root; - } - - // Link together two elements of linked list -- a and b. - void join(Node *a, Node *b) { - a->next_sibling = b; - b->prev_sibling = a; - } - - // Add node as a child of a given parent. - virtual void add_child(Node* parent, Node *node) { - EXPECT(parent, "add_child: Parent cannot be nullptr."); - EXPECT(node, "add_child: Node cannot be nullptr."); - EXPECT(!node->parent, "add_child: Node already has a parent."); - EXPECT(parent == &meta_root || node->rank == parent->rank, - "add_child: Ranks of node and parent are different."); - EXPECT(node->prev_sibling == node && node->next_sibling == node, - "add_child: Node has a sibling."); - EXPECT(parent == &meta_root || parent->prio <= node->prio, - "add_child: Parent has bigger priority than node."); - - if (parent->rank == 0) parent->first_child = node; - else { - join(parent->first_child->prev_sibling, node); - join(node, parent->first_child); - } - - node->parent = parent; - parent->rank++; - } - - // Disconnect a node from its parent. - virtual Node *remove(Node *n) { - EXPECT(n->parent, "remove: Cannot disconnect node without parent."); - EXPECT(n != &meta_root, "remove: Cannot remove meta root."); - - n->parent->rank--; - if (n->parent->first_child == n) n->parent->first_child = n->next_sibling; - if (n->parent->rank == 0) n->parent->first_child = nullptr; - - n->parent = nullptr; - join(n->prev_sibling, n->next_sibling); - join(n, n); - - return n; - } - - friend class HeapTests; - - size_t size; - Node meta_root; -}; diff --git a/05-fibonacci/cpp/fibonacci_test.cpp b/05-fibonacci/cpp/fibonacci_test.cpp deleted file mode 100644 index 1a650799a6d16d6eb48fc3bb1e306e627044973e..0000000000000000000000000000000000000000 --- a/05-fibonacci/cpp/fibonacci_test.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include <stdio.h> -#include <utility> -#include <functional> -#include <string> -#include <limits> -#include <algorithm> - -#include "fibonacci.h" - -struct HeapTests { - using Heap = FibonacciHeap; - - // Good enough infinity for our tests. - static const int infty = 1000*1000*1000; - - /* Iterator over all children of given node. - * Also implements begin() and end() so it can - * be plugged into for-each cycle. - */ - class NodeIterator { - Node *root; - Node *node; - bool recursive; - - public: - NodeIterator begin() { return *this; } - NodeIterator end() { return NodeIterator(); } - bool operator !=(const NodeIterator& b) { return node != b.node; } - - Node *operator*() { return node; } - NodeIterator &operator++() { - if (recursive && node->rank > 0) { - node = node->first_child; - } else { - while (node->next_sibling == node->parent->first_child) { - node = node->parent; - if (node == root) { - node = nullptr; - return *this; - } - } - node = node->next_sibling; - } - return *this; - } - - NodeIterator(Node *root = nullptr, bool recursive = true) : root(root), - node(root ? root->first_child : nullptr), recursive(recursive) {} - }; - - // Helper of show_heap. - static void show_node(Node *node, int indent) { - for (int i = 0; i < indent; i++) printf(" "); - printf("- %i (payload: %i)\n", node->priority(), node->payload); - for (Node* child : NodeIterator(node, false)) - show_node(child, indent + 1); - } - - // Show heap in human-readable form. - static void show_heap(Heap* H) { - printf("Heap of size %i\n", (int)H->size); - for (Node* root : NodeIterator(&H->meta_root, false)) - show_node(root, 0); - printf("\n"); - } - - // Check heap invariants. - static void audit_heap(Heap* H) { - int size = 0; - for (Node* node : NodeIterator(&H->meta_root)) { - size++; - EXPECT(H->is_root(node) || node->parent->priority() <= node->priority(), - "Parent has bigger priority than its child."); - std::vector<int> children; - for (Node *c : NodeIterator(node, false)) { - children.push_back(c->rank); - EXPECT(c->parent == node, "Inconsistent parent pointer."); - EXPECT(c->next_sibling->prev_sibling == c, "Inconsistent sibling pointers."); - EXPECT(c->prev_sibling->next_sibling == c, "Inconsistent sibling pointers."); - } - std::sort(children.begin(), children.end()); - EXPECT(children.size() == node->rank, - "Rank of node does not match its real number of children."); - for (int i = 0; i < children.size(); i++) - EXPECT(children[i] >= i - 1, "Child of the node has too small rank."); - } - EXPECT(size == H->size, "Size of the heap does not match real number of its nodes."); - } - - // Remove given node from heap. - static void remove(Heap* H, Node *n) { - H->decrease(n, -infty); - H->extract_min(); - } - - // Force consolidation of the heap. - static void consolidate(Heap* H) { - remove(H, H->insert(0)); - } - - /* Remove the subtree rooted in node from the heap. - * - * Note that this method slightly cheats because it - * learns what nodes are in the subtree by walking over it. - */ - static void remove_subtree(Heap* H, Node *node) { - std::vector<Node*> to_remove; - - for (Node* n : NodeIterator(node)) to_remove.push_back(n); - to_remove.push_back(node); - - for (Node* n : to_remove) remove(H, n); - } - - /* Build a tree of a given rank with as few nodes as possible. - * - * Return pair (root of the tree, its child with the largest rank). - */ - static std::pair<Node*,Node*> build_sparse_tree(Heap *H, int rank, int prio) { - if (rank == 0) return { H->insert(prio), nullptr }; - - auto A = build_sparse_tree(H, rank - 1, prio); - auto B = build_sparse_tree(H, rank - 1, prio + 1); - - consolidate(H); - if (B.second) remove_subtree(H, B.second); - - return { A.first, B.first }; - } - - // Check whether H contains exacly one path and nothing else. - static void check_path(Heap* H) { - Node *node = &H->meta_root; - while (node->rank == 1) node = node->first_child; - EXPECT(node->rank == 0, "Expected path but found node of rank larger than 1."); - } - - /* Build a path of a given length. - * - * Return lowest node on this path. All nodes on path are marked. - */ - static Node* build_path(Heap *H, int length) { - Node *a, *b; - std::vector<Node*> to_remove; - H->insert(length + 1); - Node *end = H->insert(length + 2); - consolidate(H); - to_remove.push_back(H->insert(length + 2)); - a = H->insert(length + 3); - consolidate(H); - remove(H, a); - - for (int i = length; i > 0; i--) { - H->insert(i); - to_remove.push_back(H->insert(i + 1)); - consolidate(H); - a = H->insert(i + 1); - b = H->insert(i + 2); - consolidate(H); - remove(H, b); - remove(H, a); - } - - for (Node *n : to_remove) remove(H, n); - - check_path(H); - - return end; - } - - /* A simple random test. - * - * It does in order: - * - randomly insert elements, - * - do random decreases, - * - extract_min until the heap is empty. - */ - static void test_random(int size, bool print) { - Heap H; - std::vector<std::pair<Node*, int>> node_map; - - for (int i = 0; i < size; i++) { - int prio = (1009 * i) % 2099; - node_map.push_back({ H.insert(prio, i), prio }); - } - - consolidate(&H); - if (print) show_heap(&H); - - for (int i = 0; i < size; i++) { - if (i % (size / 10) == 0) audit_heap(&H); - int idx = (i * 839) % node_map.size(); - auto& X = node_map[idx]; - EXPECT(X.first->priority() == X.second, - "Priority of node changed but its was not decreased."); - int new_prio = X.second - ((i * 131 + 15) % 239); - H.decrease(X.first, new_prio); - EXPECT(X.first->priority() == new_prio, - "Decrease failed to change priority of the node."); - if (print) { - printf("Decrease %i -> %i (payload %i)\n", X.second, new_prio, X.first->payload); - show_heap(&H); - } - X.second = new_prio; - } - - int last = std::numeric_limits<int>::min(); - audit_heap(&H); - while (!H.is_empty()) { - auto x = H.extract_min(); - payload_t payload = x.first; - priority_t prio = x.second; - EXPECT(last <= prio, "extract_min is not monotone."); - EXPECT(node_map[payload].first, - "Extracted node was already deleted or something changed its payload."); - EXPECT(prio == node_map[payload].second, - "Priority of extracted node is wrong."); - last = prio; - node_map[payload].first = nullptr; - if (print) { - printf("Extract min %i (payload %i)\n", prio, payload); - show_heap(&H); - } - } - - for (auto& X : node_map) EXPECT(X.first == nullptr, "Not all nodes were deleted."); - } - - // Build a sparse tree of given rank and then empty the heap. - static void test_sparse_tree(int rank) { - Heap H; - build_sparse_tree(&H, rank, 1); - audit_heap(&H); - while (!H.is_empty()) H.extract_min(); - } - - // Build a path, decrease it lowest node, and empty the heap. - static void test_path(int length) { - Heap H; - H.decrease(build_path(&H, length), -infty); - for (Node *n : NodeIterator(&H.meta_root)) - EXPECT(n->rank == 0, "Decrease of lowest node should have broken " - "the path into forest of isolated vertices but it has not."); - audit_heap(&H); - while (!H.is_empty()) H.extract_min(); - } -}; - -std::vector<std::pair<std::string, std::function<void()>>> tests = { - { "small", [] { HeapTests::test_random(11, true); } }, - { "random", [] { HeapTests::test_random(10000, false); } }, - { "sparse", [] { HeapTests::test_sparse_tree(16); } }, - { "path", [] { HeapTests::test_path(5000); } }, -}; diff --git a/05-fibonacci/cpp/test_main.cpp b/05-fibonacci/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/05-fibonacci/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/05-fibonacci/python/fibonacci.py b/05-fibonacci/python/fibonacci.py deleted file mode 100644 index e05c1567c8cb9287bc7be16cc959544f652150a5..0000000000000000000000000000000000000000 --- a/05-fibonacci/python/fibonacci.py +++ /dev/null @@ -1,165 +0,0 @@ -class Node: - """Node of `FibonacciHeap`. - - Property `payload` can be used to store any information - the user needs. - """ - - def __init__(self, prio, payload = None): - self.payload = payload - - self._prio = prio - self._parent = None - - self._rank = 0 - self._first_child = None - - # Pointers in a cyclic list of children of the parent. - self._prev_sibling = self - self._next_sibling = self - - def priority(self): - """Return priority of the node.""" - return self._prio - -class FibonacciHeap: - """Fibonacci heap. - - Implemented using a meta root to avoid special handling of the - linked list of heap trees. - """ - def __init__(self): - self._size = 0 - self._meta_root = Node(None) - - def is_empty(self): - """Check whether the heap is empty.""" - return self._size == 0 - - def insert(self, prio, payload = None): - """Insert a new node with given priority and payload and return it. - - `payload` can be used to store any data in the node. - """ - n = Node(prio, payload) - self._add_child(self._meta_root, n) - self._size += 1 - return n - - def extract_min(self): - """Extract node with minimum priority. - - Must not be called when the heap is empty. - Returns tuple (payload, priority) of the removed node. - """ - assert self._size > 0, "Cannot extract minimum of an empty heap." - - # Find the tree whose root is minimal. - minimum = self._meta_root._first_child - node = self._meta_root._first_child._next_sibling - while node is not self._meta_root._first_child: - if node._prio < minimum._prio: - minimum = node - node = node._next_sibling - - self._remove(minimum) - self._size -= 1 - - # Add all its subtrees to the heap. - while minimum._rank > 0: - self._add_child(self._meta_root, self._remove(minimum._first_child)) - - # Finally, consolidate the heap. - self._consolidate() - - return (minimum.payload, minimum._prio) - - def _consolidate(self): - """INTERNAL: Consolidate heap during extract_min. - """ - buckets = [ None ] * self._max_rank() - while self._meta_root._rank > 0: - node = self._remove(self._meta_root._first_child) - while buckets[node._rank] is not None: - b = node._rank - node = self._pair_trees(node, buckets[b]) - buckets[b] = None - buckets[node._rank] = node - - for node in buckets: - if node is not None: - self._add_child(self._meta_root, node) - - def _pair_trees(self, a, b): - """INTERNAL: Join two trees of the same rank. - """ - if a._prio > b._prio: a, b = b, a - self._add_child(a, b) - return a - - def _max_rank(self): - """INTERNAL: Return maximum possible rank of a tree in a heap of the current size. - """ - ret = 1 - while (1 << ret) <= self._size: ret += 1 - return ret - - def decrease(self, node, new_prio): - """Decrease priority of node to new_prio. - - new_prio must not be higher than node._prio. - """ - assert node is not None, "Cannot decrease None." - assert node is not self._meta_root, "Cannot decrease meta root." - assert new_prio <= node._prio, "Decrease: new priority is bigger than old one." - # TODO: Implement - raise NotImplementedError - - def _is_root(self, node): - """Check wheter node is root.""" - return node._parent is self._meta_root - - def _add_child(self, parent, node): - """INTERNAL: Add node as child of parent. - """ - assert parent is not None, "_add_child: Parent cannot be None." - assert node is not None, "_add_child: Cannot add None as a child." - assert node._parent is None, "_add_child: node already has a parent." - assert parent is self._meta_root or node._rank == parent._rank, \ - "_add_child: ranks of node and parent are different" - assert node._prev_sibling is node and node._next_sibling is node, \ - "_add_child: node has a sibling" - assert parent is self._meta_root or parent._prio <= node._prio, \ - "_add_child: parent has bigger priority than node" - - if parent._rank == 0: - parent._first_child = node - else: - self._join(parent._first_child._prev_sibling, node) - self._join(node, parent._first_child) - - node._parent = parent - parent._rank += 1 - - def _remove(self, node): - """INTERNAL: Disconect node from parent. - """ - assert node._parent is not None, "_remove: Cannot disconnect node without parent." - assert node is not self._meta_root, "_remove: Cannot remove meta root." - - node._parent._rank -= 1 - if node._parent._rank == 0: - node._parent._first_child = None - elif node._parent._first_child is node: - node._parent._first_child = node._next_sibling - - node._parent = None - self._join(node._prev_sibling, node._next_sibling) - self._join(node, node) - return node - - def _join(self, a, b): - """INTERNAL: Link together two elements of linked list -- a and b. - """ - a._next_sibling = b - b._prev_sibling = a diff --git a/05-fibonacci/python/fibonacci_test.py b/05-fibonacci/python/fibonacci_test.py deleted file mode 100644 index aeaf8bbe0c7891163c95b83e44d7e73d4817e487..0000000000000000000000000000000000000000 --- a/05-fibonacci/python/fibonacci_test.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env python3 - -from fibonacci import FibonacciHeap as Heap -from math import log - -def children(root, recursive = True): - """Iterate over all children of `root`. - """ - if root._first_child is None: return - - node = root._first_child - while True: - yield node - if recursive and node._rank > 0: - node = node._first_child - else: - while node._next_sibling is node._parent._first_child: - node = node._parent - if node is root: return - node = node._next_sibling - -def show_node(node, indent): - """Helper of show_heap. - """ - print("%s- %s (payload: %s)" % (" " * indent, node.priority(), node.payload)) - for child in children(node, False): - show_node(child, indent + 1) - -def show_heap(heap): - """Show heap in human-readable form. - """ - print("Heap of size %i" % heap._size) - for root in children(heap._meta_root, False): - show_node(root, 0) - print("") - -def audit_heap(heap): - """Check various heap invariants. - """ - assert heap._meta_root is not None, "Meta root is None" - assert heap._size == sum( 1 for _ in children(heap._meta_root) ), \ - "Size of the heap does not match real number of its nodes." - - for root in children(heap._meta_root): - assert heap._is_root(root) or root._parent.priority() <= root.priority(), \ - "Parent has bigger priority than its child." - - child_sizes = [] - for c in children(root, False): - child_sizes.append(c._rank) - assert c._parent is root, "Inconsistent parent pointer." - assert c._next_sibling._prev_sibling is c, "Inconsistent sibling pointers." - assert c._prev_sibling._next_sibling is c, "Inconsistent sibling pointers." - - assert len(child_sizes) == root._rank, \ - "Rank of node does not match its real number of children." - for i, s in enumerate(sorted(child_sizes)): - assert s >= i - 1, "Child of the node has too small rank." - - -def remove(H, node): - """Remove node from heap H. - """ - H.decrease(node, -10**9) - H.extract_min() - -def consolidate(H): - """Force consolidation of heap H. - """ - remove(H, H.insert(0)) - -def remove_subtree(H, node): - """Remove subtree rooted in node from the heap. - - Note that this method slightly cheats because it - learns what nodes are in the subtree by walking over it. - """ - to_remove = list(children(node)) - to_remove.append(node) - - for node in to_remove: remove(H, node) - -def build_sparse_tree(H, rank, prio): - """Build a tree of a given rank with as few nodes as possible. - - Return pair (root of the tree, its child with largest rank). - """ - if rank == 0: - return (H.insert(prio), None) - - a, _ = build_sparse_tree(H, rank - 1, prio) - b, to_remove = build_sparse_tree(H, rank - 1, prio + 1) - - consolidate(H) - if to_remove is not None: - remove_subtree(H, to_remove) - - return (a, b) - -def check_path(H): - """Check whether H contains exactly one path and nothing else. - """ - node = H._meta_root - while node._rank == 1: node = node._first_child - assert node._rank == 0, "Expected path but found node of rank larger than 1." - -def build_path(H, length): - """Build a path of given length. - - Return lowest node on this path. All nodes on path are marked. - """ - to_remove = [] - H.insert(length + 1) - end = H.insert(length + 2) - consolidate(H) - to_remove.append(H.insert(length + 2)) - a = H.insert(length + 3) - consolidate(H) - remove(H, a) - - for i in range(length, 0, -1): - H.insert(i) - to_remove.append(H.insert(i + 1)) - consolidate(H) - a = H.insert(i + 1) - b = H.insert(i + 2) - consolidate(H) - remove(H, b) - remove(H, a) - - for n in to_remove: - remove(H, n) - - check_path(H) - - return end - -def test_random(size, do_print): - """A simple random test. - - It does in order: - - randomly insert elements, - - do random decreases, - - extract_min until the heap is empty. - """ - H = Heap() - node_map = [] - - for i in range(size): - prio = (1009 * i) % 2099 - node_map.append([H.insert(prio, payload=i), prio]) - - consolidate(H) - if do_print: show_heap(H) - - for i in range(size): - if i % (size // 10) == 0: - audit_heap(H) - idx = (i * 839) % len(node_map) - n, prio = node_map[idx] - assert n.priority() == prio, "Priority of node changed but its was not decreased." - new_prio = prio - ((i * 131 + 15) % 239) - H.decrease(n, new_prio) - assert n.priority() == new_prio, "Decrease failed to change priority of the node." - node_map[idx][1] = new_prio - if do_print: - print("Decrease %s -> %s (payload %s)" % (prio, new_prio, n.payload)) - show_heap(H) - - last = None - audit_heap(H) - while not H.is_empty(): - payload, prio = H.extract_min() - assert last is None or prio >= last, "extract_min is not monotone." - assert node_map[payload][0], \ - "Extracted node was already deleted or something changed its payload." - assert prio == node_map[payload][1], "Priority of extracted node is wrong." - last = prio - node_map[payload] = None - if do_print: - print("Extract min %s (payload %s)" % (prio, payload)) - show_heap(H) - - assert all( x is None for x in node_map ), "Not all nodes were deleted." - -def test_sparse_tree(rank): - """Build a sparse tree of given rank and then empty the heap.""" - H = Heap() - build_sparse_tree(H, rank, 1) - audit_heap(H) - while not H.is_empty(): - H.extract_min() - -def test_path(length): - """Build a path, decrease it lowest node, and empty the heap.""" - H = Heap() - H.decrease(build_path(H, length), -1000) - assert all( n._rank == 0 for n in children(H._meta_root, False) ), \ - "Decrease of lowest node should have broken the path into forest " + \ - "of isolated vertices but it did not." - audit_heap(H) - while not H.is_empty(): - H.extract_min() - -tests = [ - ("small", lambda: test_random(11, True)), - ("random", lambda: test_random(10000, False)), - ("sparse", lambda: test_sparse_tree(16)), - ("path", lambda: test_path(5000)), -] - -if __name__ == "__main__": - import sys - 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)) diff --git a/05-fibonacci/task.md b/05-fibonacci/task.md deleted file mode 100644 index 2e04802a1311a08e5184ec0af83db3c4b1745de8..0000000000000000000000000000000000000000 --- a/05-fibonacci/task.md +++ /dev/null @@ -1,4 +0,0 @@ -You are given an implementation of a lazy binomial heap, which supports -Insert and ExtractMin operations. - -Turn it into a Fibonacci heap and implement Decrease. diff --git a/06-fibonacci_experiment/cpp/Makefile b/06-fibonacci_experiment/cpp/Makefile deleted file mode 100644 index 6b343dbbb88fe2e9683b07943433dc85040cd6a6..0000000000000000000000000000000000000000 --- a/06-fibonacci_experiment/cpp/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -.PHONY: test -test: fibonacci_experiment - @for exp in star random; do \ - for impl in std naive ; do \ - echo "t-$$exp-$$impl" ; \ - time ./fibonacci_experiment $$exp 42 $$impl >t-$$exp-$$impl ; \ - done ; \ - done - @for b in biased-10 biased-40 biased-70 biased-100 ; do \ - echo "t-$$b" ; \ - time ./fibonacci_experiment $$b 42 std >t-$$b ; \ - done - -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -ggdb -I$(INCLUDE) - -fibonacci_experiment: fibonacci.h fibonacci_experiment.cpp $(INCLUDE)/random.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) fibonacci_experiment.cpp -o $@ - -.PHONY: clean -clean: - rm -f fibonacci_experiment diff --git a/06-fibonacci_experiment/cpp/fibonacci_experiment.cpp b/06-fibonacci_experiment/cpp/fibonacci_experiment.cpp deleted file mode 100644 index 3e8d9b5b307c4baacc2ecf3ca29fae75eacdfb8d..0000000000000000000000000000000000000000 --- a/06-fibonacci_experiment/cpp/fibonacci_experiment.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include <algorithm> -#include <functional> -#include <string> -#include <utility> -#include <vector> -#include <iostream> -#include <cmath> - -#include "fibonacci.h" -#include "random.h" - -void expect_failed(const std::string& message) { - fprintf(stderr, "Test error: %s\n", message.c_str()); - exit(1); -} - - -/* - * A modified Fibonacci heap for benchmarking. - * - * We inherit the implementation of operations from the FibonacciHeap class - * and extend it by keeping statistics on the number of operations - * and the total number of structural changes. Also, if naive is turned on, - * the decrease does not mark parent which just lost a child. - */ - -class HeapTests : public FibonacciHeap { - int ops = 0; - int steps = 0; - bool naive; - size_t max_size = 0; - - public: - HeapTests(bool naive = false) : naive(naive) {} - - void stats() { - printf(" %.3lf\n", (double)(steps * 1.0 / std::max(1, ops))); - } - - Node *insert(priority_t prio, payload_t payload = payload_t()) { - ops++; - return FibonacciHeap::insert(prio, payload); - } - - std::pair<payload_t, priority_t> extract_min() { - ops++; - return FibonacciHeap::extract_min(); - } - - void decrease(Node *node, priority_t new_prio) { - ops++; - if (naive) { - EXPECT(node, "Cannot decrease null pointer."); - EXPECT(node->prio >= new_prio, "Decrase: new priority larger than old one."); - - node->prio = new_prio; - if (is_root(node) || new_prio >= node->parent->prio) return; - - add_child(&meta_root, remove(node)); - } else FibonacciHeap::decrease(node, new_prio); - } - - protected: - size_t max_rank() override { - max_size = std::max(max_size, size); - if (naive) return sqrt(2*max_size) + 2; - else return FibonacciHeap::max_rank(); - } - - void add_child(Node* parent, Node *node) override { - steps++; - FibonacciHeap::add_child(parent, node); - } - - Node *remove(Node *n) override { - steps++; - return FibonacciHeap::remove(n); - } -}; - -using Heap = HeapTests; - -RandomGen *rng; // Random generator object - - -std::vector<int> log_range(int b, int e, int n) { - std::vector<int> ret; - for (int i = 0; i < n; i++) - ret.push_back(b * exp(log(e / b) / (n - 1) * i)); - return ret; -} - -// An auxiliary function for generating a random permutation. -template < typename Vector > -void random_permutation(Vector& v) -{ - for (int i = 0; i < v.size(); i++) - std::swap(v[i], v[i + rng->next_range(v.size() - i)]); -} - -// Remove given node from heap. -void remove(Heap* H, Node *n) { - H->decrease(n, -1000*1000*1000); - H->extract_min(); -} - -// Force consolidation of the heap. -void consolidate(Heap* H) { - remove(H, H->insert(0)); -} - -// Construct a star with n nodes and root index r. -void star(Heap *H, Node **node_map, int n, int r, bool consolidate_) { - if (n == 1) { - EXPECT(!node_map[r], ""); - node_map[r] = H->insert(r); - if (consolidate_) consolidate(H); - } else { - star(H, node_map, n - 1, r, false); - star(H, node_map, n - 1, r + n - 1, true); - - for (int i = r + n; i < r + 2*n - 2; i++) { - remove(H, node_map[i]); - node_map[i] = nullptr; - } - } -} - -/* Generates a sequence on which non-cascading heaps need lots of time. - * Source: "Replacing Mark Bits with Randomness in Fibonacci Heaps" Jerry Li and John Peebles, MIT - * -> modified so that only a quadratic number of elements are needed in the star construction. - */ -void test_star(bool naive) { - for (int i = 1; i < 17; i++) { - int start = 3; - int N = start + i * (i+1) / 2; - Heap H(naive); - Node **node_map = new Node*[N]; - for (int i = 0; i < N; i++) node_map[i] = nullptr; - - for (int j = i; j > 0; j--) { - star(&H, node_map, j, start, false); - start += j; - } - - for (int j = 0; j < (1 << i); j++) { - H.insert(1); - H.insert(1); - H.extract_min(); - H.extract_min(); - } - - printf("%i", N); - H.stats(); - - delete[] node_map; - } -} - -/* A random test. - * - * The test does 2N insertions first, and then N random operations insert / decrease / - * extract_min, each with probability proprotional to its weight ins / dec / rem. - */ -void test_random(int ins, int dec, int rem, bool naive) { - for (int N : log_range(50, 80000, 30)) { - Heap H(naive); - std::vector<Node*> node_map; - int s = ins + dec + rem; - - std::vector<int> ops; - for (int i = 0; i < (N*ins) / s; i++) ops.push_back(0); - for (int i = 0; i < (N*dec) / s; i++) ops.push_back(1); - for (int i = 0; i < (N*rem) / s; i++) ops.push_back(2); - random_permutation(ops); - -# define INSERT() node_map.push_back(H.insert(rng->next_range(5*N), node_map.size())) - - for (int i = 0; i < 2*N; i++) INSERT(); - - for (int op : ops) switch (op) { - case 0: - INSERT(); - break; - case 1: { - Node *node = node_map[rng->next_range(node_map.size())]; - int p = node->priority() - rng->next_range(N / 5) - 1; - H.decrease(node, p); - break; - } - default: { - auto ret = H.extract_min(); - if (ret.first + 1 != node_map.size()) { - node_map[ret.first] = node_map.back(); - node_map[ret.first]->payload = ret.first; - } - node_map.pop_back(); - } - } - - printf("%i", N); - H.stats(); - -# undef INSERT - } -} - - -std::vector<std::pair<std::string, std::function<void(bool)>>> tests = { - { "star", test_star }, - { "random", [](bool n){ test_random(10, 10, 10, n); } }, - { "biased-10", [](bool n){ test_random(10, 10, 20, n); } }, - { "biased-40", [](bool n){ test_random(10, 40, 20, n); } }, - { "biased-70", [](bool n){ test_random(10, 70, 20, n); } }, - { "biased-100", [](bool n){ test_random(10, 100, 20, n); } }, -}; - -int main(int argc, char **argv) -{ - if (argc != 4) { - fprintf(stderr, "Usage: %s <test> <student-id> (std|naive)\n", argv[0]); - return 1; - } - - std::string which_test = argv[1]; - std::string id_str = argv[2]; - std::string mode = argv[3]; - - try { - rng = new RandomGen(stoi(id_str)); - } catch (...) { - fprintf(stderr, "Invalid student ID\n"); - return 1; - } - - bool naive; - if (mode == "std") - naive = false; - else if (mode == "naive") - naive = true; - else - { - fprintf(stderr, "Last argument must be either 'std' or 'naive'\n"); - return 1; - } - - for (const auto& test : tests) { - if (test.first == which_test) { - test.second(naive); - return 0; - } - } - - fprintf(stderr, "Unknown test %s\n", which_test.c_str()); - return 1; -} diff --git a/06-fibonacci_experiment/cpp/random.h b/06-fibonacci_experiment/cpp/random.h deleted file mode 100644 index 5ef10aeb1fe7e58a48277fb3565169ec267d43d9..0000000000000000000000000000000000000000 --- a/06-fibonacci_experiment/cpp/random.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef DS1_RANDOM_H -#define DS1_RANDOM_H - -#include <cstdint> - -/* - * This is the xoroshiro128+ random generator, designed in 2016 by David Blackman - * and Sebastiano Vigna, distributed under the CC-0 license. For more details, - * see http://vigna.di.unimi.it/xorshift/. - * - * Rewritten to C++ by Martin Mares, also placed under CC-0. - */ - -class RandomGen { - uint64_t state[2]; - - uint64_t rotl(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - public: - // Initialize the generator, set its seed and warm it up. - RandomGen(unsigned int seed) - { - state[0] = seed * 0xdeadbeef; - state[1] = seed ^ 0xc0de1234; - for (int i=0; i<100; i++) - next_u64(); - } - - // Generate a random 64-bit number. - uint64_t next_u64(void) - { - uint64_t s0 = state[0], s1 = state[1]; - uint64_t result = s0 + s1; - s1 ^= s0; - state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); - state[1] = rotl(s1, 36); - return result; - } - - // Generate a random 32-bit number. - uint32_t next_u32(void) - { - return next_u64() >> 11; - } - - // Generate a number between 0 and range-1. - unsigned int next_range(unsigned int range) - { - /* - * This is not perfectly uniform, unless the range is a power of two. - * However, for 64-bit random values and 32-bit ranges, the bias is - * insignificant. - */ - return next_u64() % range; - } -}; - -#endif diff --git a/06-fibonacci_experiment/python/Makefile b/06-fibonacci_experiment/python/Makefile deleted file mode 100644 index 656f937ea4450086769654769d296437659610bc..0000000000000000000000000000000000000000 --- a/06-fibonacci_experiment/python/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -.PHONY: plot -test: - @for exp in star random ; do \ - for impl in std naive ; do \ - echo "t-$$exp-$$impl" ; \ - time ./fibonacci_experiment.py $$exp 42 $$impl >t-$$exp-$$impl ; \ - done ; \ - done - @for b in biased-10 biased-40 biased-70 biased-100 ; do \ - echo "t-$$b" ; \ - time ./fibonacci_experiment.py $$b 42 std >t-$$b ; \ - done - diff --git a/06-fibonacci_experiment/python/fibonacci_experiment.py b/06-fibonacci_experiment/python/fibonacci_experiment.py deleted file mode 100644 index eb7a09127272e3bf4bb28d155677871b52aba2b5..0000000000000000000000000000000000000000 --- a/06-fibonacci_experiment/python/fibonacci_experiment.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 - -import sys, itertools, random -from math import sqrt, log, exp -from fibonacci import FibonacciHeap as Heap - -class BenchmarkingHeap(Heap): - """A modified Fibonacci heap for benchmarking. - - We inherit the implementation of operations from the FibonacciHeap class - and extend it by keeping statistics on the number of operations - and the total number of structural changes. Also, if naive is turned on, - the decrease does not mark parent which just lost a child. - """ - - def __init__(self, naive=False): - Heap.__init__(self) - self.naive = naive - self._ops = 0 - self._steps = 0 - self._max_size = 0 # used to get upper bound on number of buckets in naive version - - def stats(self): - return " %.3f" % (self._steps / max(self._ops, 1)) - - def insert(self, *args): - self._ops += 1 - return Heap.insert(self, *args) - - def decrease(self, node, new_prio): - self._ops += 1 - if self.naive: - assert node is not None, "Cannot decrease None." - assert node is not self._meta_root, "Cannot decrease meta root." - assert new_prio <= node._prio, \ - "Decrease: new priority is bigger than old one." - - node._prio = new_prio - if self._is_root(node) or node._prio >= node._parent._prio: - return - - self._add_child(self._meta_root, self._remove(node)) - else: - Heap.decrease(self, node, new_prio) - - def extract_min(self): - self._ops += 1 - return Heap.extract_min(self) - - def _add_child(self, *args): - self._steps += 1 - Heap._add_child(self, *args) - - def _remove(self, *args): - self._steps += 1 - return Heap._remove(self, *args) - - def _max_rank(self): - self._max_size = max(self._max_size, self._size) - if self.naive: return int(sqrt(2 * self._max_size)) + 2 - return Heap._max_rank(self) - -def remove(H, node): - H.decrease(node, -10**9) - H.extract_min() - -def consolidate(H): - remove(H, H.insert(0)) - -def log_range(b, e, n): - assert 0 < b < e - for i in range(n): - yield int(b * exp(log(e / b) / (n - 1) * i)) - -def star(H, node_map, n, r, consolidate_): - """Construct a star with n nodes and root index r. - """ - if n == 1: - assert node_map[r] is None - node_map[r] = H.insert(r) - if consolidate_: - consolidate(H) - else: - star(H, node_map, n - 1, r, False) - star(H, node_map, n - 1, r + n - 1, True) - - for i in range(r + n, r + 2*n - 2): - remove(H, node_map[i]) - node_map[i] = None - -def test_star(naive): - """Generates a sequence on which non-cascading heaps need lots of time. - Source: "Replacing Mark Bits with Randomness in Fibonacci Heaps" Jerry Li and John Peebles, MIT - -> modified so that only a quadratic number of elements are needed in the star construction. - """ - for i in range(1, 17): # was 26 - start = 3 - N = start + i * (i+1) // 2 - H = BenchmarkingHeap(naive) - node_map = [None] * N - - for j in range(i, 0, -1): - star(H, node_map, j, start, False) - start += j - - for i in range(1 << i): - H.insert(1) - H.insert(1) - H.extract_min() - H.extract_min() - - print(N, H.stats()) - -def test_random(ins, dec, rem, naive): - """A random test. - - The test does 2N insertions first, and then N random operations insert / decrease / - extract_min each with probability proprotional to its weight ins / dec / rem. - """ - for N in log_range(50, 80000, 30): - H = BenchmarkingHeap(naive) - node_map = [] - s = ins + dec + rem - ops = [ 0 ] * ((N*ins) // s) + [ 1 ] * ((N*dec) // s) + [ 2 ] * ((N*rem) // s) - random.shuffle(ops) - - def insert(): - node_map.append(H.insert(random.randrange(5 * N), len(node_map))) - - for _ in range(2*N): insert() - - for op in ops: - if op == 0: insert() - elif op == 1: - node = random.choice(node_map) - p = node.priority() - random.randrange(N // 5) - 1 - H.decrease(node, p) - else: - i, _ = H.extract_min() - if i + 1 == len(node_map): - node_map.pop() - else: - node_map[i] = node_map.pop() - node_map[i].payload = i - - print(N, H.stats()) - -tests = { - "star": test_star, - "random": lambda n: test_random(10, 10, 10, n), - "biased-10": lambda n: test_random(10, 10, 20, n), - "biased-40": lambda n: test_random(10, 40, 20, n), - "biased-70": lambda n: test_random(10, 70, 20, n), - "biased-100": lambda n: test_random(10, 100, 20, n), -} - -if len(sys.argv) == 4: - test, student_id = sys.argv[1], sys.argv[2] - if sys.argv[3] == "std": - naive = False - elif sys.argv[3] == "naive": - naive = True - else: - raise ValueError("Last argument must be either 'std' or 'naive'") - random.seed(int(student_id)) - if test in tests: - tests[test](naive) - else: - raise ValueError("Unknown test {}".format(test)) -else: - raise ValueError("Usage: {} <test> <student-id> (std|naive)".format(sys.argv[0])) - diff --git a/06-fibonacci_experiment/task.md b/06-fibonacci_experiment/task.md deleted file mode 100644 index 4e834ba21a20a85ec5d6409fed688eb743f3f3d7..0000000000000000000000000000000000000000 --- a/06-fibonacci_experiment/task.md +++ /dev/null @@ -1,93 +0,0 @@ -## Goal - -The goal of this assignment is to evaluate your implementation of Fibonacci heap -experimentally and to compare it with a "naive" implementation which does not -mark vertices after cutting a child. - -You are given a test program (`fibonacci_experiment`) which calls your -implementation from the previous assignment to perform the following -experiments: - -- _Star test:_ Specific sequence of operations on which naive implementation - creates $Θ(\sqrt{n})$ stars of size 1 up to $Θ(\sqrt{n})$. -- _Random test:_ Test of size $n$ first inserts $2n$ elements and then it does - $n$ operations of which one third are insertions, one third are decreases - and one third are extract\_mins in random order. -- _Biased tests:_ Like random test but the weights of operations are different. - First the test inserts $2n$ elements and then it does random $n$ operations. - Number of operations of each type is proportional to their weight. - Weight of insert is 10, weight of extract\_min 20 and weight of decrease is - the number in the name of the test, and implemented possibilities are 10, 40, - 70 and 100. - -The program tries each experiment with different values of $n$. In each try, -it prints the average number of structural changes (adding and removing a child -of a node) per one operation (insert, decrease and extract\_min). - -You should perform these experiments and write a report, which contains one -plot of the measured data for each test. The plots should be following: - -- _Star test:_ One plot with two curves – one for standard and the other one for naive - version. -- _Random test:_ One plot with two curves – one for standard and the other one for naive - version. -- _Biased tests:_ One plot with 4 curves – one for each value of bias, all of them tested - with standard implementation. Note that we do not do biased test naive implementation. - -Each plot should show the dependence -of the average number of structural changes on the test size $n$. - -The report should discuss the experimental results and try to explain the observed -behavior using theory from the lectures. (If you want, you can carry out further -experiments to gain better understanding of the data structure and include these -in the report. This is strictly optional.) - -You should submit a PDF file with the report (and no source code). -You will get 1 temporary point upon submission if the file is syntactically correct; -proper points will be assigned later. - -## Test program - -The test program is given three arguments: -- The name of the test (`star`, `random`, `biased-10`, `biased-40`, `biased-70`, `biased-100`). -- The random seed: you should use the last 2 digits of your student ID (you can find - it in the Study Information System – just click on the Personal data icon). Please - include the random seed in your report. -- The implementation to test (`std` or `naive`). - -The output of the program contains one line per experiment, which consists of -the set size and the average number of structural changes. - -## Your implementation - -Please use your implementation from the previous exercise. If you used C++, -make sure that the `add_child()`, -`remove()` and `max_rank()` methods are declared virtual (they will -be overriden by the test program). Also, if you are performing any structural changes -without using the `add_child()` and `remove()` helper methods, you need to adjust -the `BenchmarkingHeap` class accordingly. - -## Hints - -The following tools can be useful for producing nice plots: -- [pandas](https://pandas.pydata.org/) -- [matplotlib](https://matplotlib.org/) -- [gnuplot](http://www.gnuplot.info/) - -A quick checklist for plots: -- Is there a caption explaining what is plotted? -- Are the axes clearly labelled? Do they have value ranges and units? -- Have you mentioned that this axis has logarithmic scale? (Logarithmic graphs - are more fitting in some cases, but you should tell.) -- Is it clear which curve means what? -- Is it clear what are the measured points and what is an interpolated - curve between them? -- Are there any overlaps? (E.g., the most interesting part of the curve - hidden underneath a label?) - -In your discussion, please distinguish the following kinds of claims. -It should be always clear which is which: -- Experimental results (i.e., the raw data you obtained from the experiments) -- Theoretical facts (i.e., claims we have proved mathematically) -- Your hypotheses (e.g., when you claim that the graph looks like something is true, - but you are not able to prove rigorously that it always holds) diff --git a/07-matrix_transpose/cpp/Makefile b/07-matrix_transpose/cpp/Makefile deleted file mode 100644 index d21f42477e1a907efa2fab6c1611f67a8641f017..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/cpp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -test: matrix_transpose_test - ./$< - -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare - -matrix_transpose_test: matrix_transpose_test.cpp matrix_transpose.h matrix_tests.h test_main.cpp - $(CXX) $(CXXFLAGS) $(filter-out %.h,$^) -o $@ - -clean: - rm -f matrix_transpose_test - -.PHONY: clean test diff --git a/07-matrix_transpose/cpp/matrix_tests.h b/07-matrix_transpose/cpp/matrix_tests.h deleted file mode 100644 index 6110f8efc37782f04f949fb30f9e18392cc325e2..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/cpp/matrix_tests.h +++ /dev/null @@ -1,213 +0,0 @@ -#include <string> -#include <vector> -#include <iostream> -#include <algorithm> - -/* A matrix stored in a simulated cache */ - -class CachedMatrix { - unsigned B; // Block size - unsigned mem_blocks; // Memory size in blocks - unsigned cache_blocks; // Cache size in blocks - unsigned cache_used; // How many blocks of cache we already used - - // We store the matrix as a one-dimensional array - vector<unsigned> items; - unsigned pos(unsigned i, unsigned j) { return i*N + j; } - - /* - * For each memory block, we keep the following structure. - * If the block is currently cached, we set cached == true - * and lru_prev/next point to neighboring blocks in the cyclic LRU list. - * Otherwise, cached == false and the block is not in the LRU. - */ - - class MemoryBlock { - public: - unsigned lru_prev, lru_next; - bool cached; - MemoryBlock() - { - lru_prev = lru_next = 0; - cached = false; - } - }; - - vector<MemoryBlock> blocks; - - // One block at the end of "blocks" serves as a head of the LRU list. - unsigned lru_head; - - public: - // Number of rows and columns of the matrix - unsigned N; - - int debug_level; // Verbosity - - CachedMatrix(unsigned N, unsigned M, unsigned B, int debug_level=0) - { - EXPECT(N > 0, "CachedMatrix must be non-empty."); - EXPECT(B > 0, "Blocks must be non-empty."); - EXPECT(!(M % B), "Cache size must be divisible by block size."); - EXPECT(M >= 2*B, "Cache must have at least 2 blocks."); - - unsigned NN = N*N; - items.resize(NN, 0); - - this->N = N; - this->B = B; - this->debug_level = debug_level; - mem_blocks = (NN+B-1) / B; - cache_blocks = M / B; - cache_used = 0; - - // Initialize the LRU list - blocks.resize(mem_blocks + 1); - lru_head = mem_blocks; - blocks[lru_head].lru_prev = lru_head; - blocks[lru_head].lru_next = lru_head; - - if (debug_level > 0) - cout << "\tMemory: " << mem_blocks << " blocks of " << B << " items, " << cache_blocks << " cached\n"; - } - - // Read value at position (i,j), used only in testing code - unsigned read(unsigned i, unsigned j) - { - EXPECT(i < N && j < N, "Read out of range: " + coord_string(i, j) + "."); - unsigned addr = pos(i, j); - access(addr); - return items[addr]; - } - - // Write value at position (i,j), used only in testing code - void write(unsigned i, unsigned j, unsigned data) - { - EXPECT(i < N && j < N, "Write out of range: " + coord_string(i, j) + "."); - unsigned addr = pos(i, j); - access(addr); - items[addr] = data; - } - - // Swap items (i1,j1) and (i2,j2) - void swap(unsigned i1, unsigned j1, unsigned i2, unsigned j2) - { - EXPECT(i1 < N && j1 < N && i2 < N && j2 < N, "Swap out of range: " + coord_string(i1, j1) + " with " + coord_string(i2, j2) + "."); - if (debug_level > 1) - cout << "\tSwap " << coord_string(i1, j1) << " " << coord_string(i2, j2) << endl; - unsigned addr1 = pos(i1, j1), addr2 = pos(i2, j2); - access(addr1); - access(addr2); - std::swap(items[addr1], items[addr2]); - } - - unsigned stat_cache_misses; - unsigned stat_accesses; - - // Reset statistic counters. - void reset_stats() - { - stat_cache_misses = 0; - stat_accesses = 0; - } - - static string coord_string(unsigned i, unsigned j) - { - return "(" + to_string(i) + "," + to_string(j) + ")"; - } - -#include "matrix_transpose.h" - - private: - // Bring the given address to the cache. - void access(unsigned addr) - { - int i = addr / B; // Which block to bring - if (blocks[i].cached) { - lru_remove(i); - } else { - if (cache_used < cache_blocks) { - // We still have room in the cache. - cache_used++; - if (debug_level > 1) - cout << "\t\tLoading block " << i << endl; - } else { - // We need to evict the least-recently used block to make space. - unsigned replace = blocks[lru_head].lru_prev; - lru_remove(replace); - EXPECT(blocks[replace].cached, "Internal error: Buggy LRU list."); - blocks[replace].cached = false; - if (debug_level > 1) - cout << "\t\tLoading block " << i << ", replacing " << replace << endl; - } - blocks[i].cached = true; - stat_cache_misses++; - } - lru_add_after(i, lru_head); - stat_accesses++; - } - - // Remove block from the LRU list. - void lru_remove(unsigned i) - { - unsigned prev = blocks[i].lru_prev; - unsigned next = blocks[i].lru_next; - blocks[prev].lru_next = next; - blocks[next].lru_prev = prev; - } - - // Add block at the given position in the LRU list. - void lru_add_after(unsigned i, unsigned after) - { - unsigned next = blocks[after].lru_next; - blocks[next].lru_prev = i; - blocks[after].lru_next = i; - blocks[i].lru_next = next; - blocks[i].lru_prev = after; - } -}; - -/* A cached matrix extended by methods for testing */ - -class TestMatrix : public CachedMatrix { - public: - TestMatrix(unsigned N, unsigned M, unsigned B, int debug_level = 0) : CachedMatrix(N, M, B, debug_level) { } - - // Fill matrix with a testing pattern. - void fill_matrix() - { - if (debug_level > 1) - cout << "\tInitializing\n"; - for (unsigned i = 0; i < N; i++) - for (unsigned j = 0; j < N; j++) - write(i, j, i*N + j); - } - - // Check that the pattern corresponds to the properly transposed matrix. - void check_result() - { - if (debug_level > 1) - cout << "\tChecking\n"; - for (unsigned i = 0; i < N; i++) { - for (unsigned j = 0; j < N; j++) { - unsigned want = j*N + i; - unsigned found = read(i, j); - unsigned found_i = found / N; - unsigned found_j = found % N; - EXPECT(found == want, - "Mismatch at position " + coord_string(i, j) + - ": expected element from " + coord_string(j, i) + - ", found element from " + coord_string(found_i, found_j) + - "."); - } - } - } - - // Transpose the matrix naively. - void naive_transpose() - { - for (unsigned i=0; i<N; i++) - for (unsigned j=0; j<i; j++) - swap(i, j, j, i); - } -}; diff --git a/07-matrix_transpose/cpp/matrix_transpose.h b/07-matrix_transpose/cpp/matrix_transpose.h deleted file mode 100644 index ddc58b5a02da9a79eb62b3ef02288a022253fe8d..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/cpp/matrix_transpose.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is #include'd inside the definition of a matrix class - * like this: - * - * class ClassName { - * // Number of rows and columns of the matrix - * unsigned N; - * - * // Swap elements (i1,j1) and (i2,j2) - * void swap(unsigned i1, unsigned j1, unsigned i2, unsigned j2); - * - * // Your code - * #include "matrix_transpose.h" - * } - */ - -void transpose() -{ - // TODO: Implement this efficiently - - for (unsigned i=0; i<N; i++) - for (unsigned j=0; j<i; j++) - swap(i, j, j, i); -} diff --git a/07-matrix_transpose/cpp/matrix_transpose_test.cpp b/07-matrix_transpose/cpp/matrix_transpose_test.cpp deleted file mode 100644 index cbbfff1134c95ec1d5a49dc0425f5ff2358930fd..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/cpp/matrix_transpose_test.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include <functional> -#include <vector> -#include <iostream> -#include <iomanip> - -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); - -#include "matrix_tests.h" - -void generic_test(unsigned N, unsigned M, unsigned B, double max_ratio, int debug_level) -{ - TestMatrix m(N, M, B, debug_level); - m.fill_matrix(); - m.reset_stats(); - m.transpose(); - - cout << "\t" << m.stat_cache_misses << " misses in " << m.stat_accesses << " accesses\n"; - if (m.stat_accesses) { - double mpa = (double) m.stat_cache_misses / m.stat_accesses; - double lb = 1. / B; - double ratio = mpa / lb; - cout << "\t" << - std::fixed << std::setprecision(6) << - mpa << " misses/access (lower bound is " << lb << - " => ratio " << ratio << - ", need " << max_ratio << ")\n"; - EXPECT(ratio <= max_ratio, "Algorithm did too many I/O operations."); - } - - m.check_result(); -} - -/*** A list of all tests ***/ - -vector<pair<string, function<void()>>> tests = { -// name N M B max_ratio debug_level - { "small2k", [] { generic_test( 8, 32, 8, 8, 2 ); } }, - { "small", [] { generic_test( 13, 64, 8, 4, 2 ); } }, - { "n100b16", [] { generic_test( 100, 1024, 16, 3, 1 ); } }, - { "n1000b16", [] { generic_test(1000, 1024, 16, 3, 1 ); } }, - { "n1000b64", [] { generic_test(1000, 8192, 64, 3, 1 ); } }, - { "n1000b256", [] { generic_test(1000, 65536, 256, 5, 1 ); } }, - { "n1000b4096", [] { generic_test(1000, 65536, 4096, 50, 1 ); } }, -}; diff --git a/07-matrix_transpose/cpp/test_main.cpp b/07-matrix_transpose/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/07-matrix_transpose/python/matrix_tests.py b/07-matrix_transpose/python/matrix_tests.py deleted file mode 100644 index f002701a4e68f65cbde033b2058a79d71aad61ba..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/python/matrix_tests.py +++ /dev/null @@ -1,165 +0,0 @@ -from matrix_transpose import Matrix -import numpy - -# Description of memory blocks is stored in an array blocks[block_index][B_xxx]. -# If the block is cached, B_CACHED is 1 and B_LRU_NEXT and B_LRU_PREV point to -# neighboring blocks in the cyclic LRU list. Otherwise, B_CACHED is 0 and the block -# is not in the LRU. -B_CACHED = 0 -B_LRU_NEXT = 1 -B_LRU_PREV = 2 - -class CachedMatrix(Matrix): - """A matrix stored in simulated cache""" - - def __init__(self, N, M, B, debug_level=0): - assert N>0, "CachedMatrix must be non-empty." - assert B>0, "Blocks must be non-empty." - assert M%B == 0, "Cache size must be divisible by block size." - assert M >= 2*B, "Cache must have at least 2 blocks." - - Matrix.__init__(self, N) - - NN = N*N - self.items = numpy.zeros(shape=NN, dtype=numpy.int32, order="C") - - self.B = B - self.M = M - self.debug_level = debug_level - self.mem_blocks = (NN+B-1) // B - self.cache_blocks = M//B - self.cache_used = 0 - - # Initialize the LRU list. There is a virtual block right after the last real block, - # which serves as a head of the cyclic LRU list. - self.blocks = numpy.zeros(shape=(self.mem_blocks+1, 3), dtype=numpy.int32, order="C") - self.lru_head = self.mem_blocks - self.blocks[self.lru_head, B_LRU_NEXT] = self.lru_head - self.blocks[self.lru_head, B_LRU_PREV] = self.lru_head - - self.reset_stats() - - if debug_level > 0: - print("\tMemory: {} blocks of {} items, {} cached".format(self.mem_blocks, self.B, self.cache_blocks)) - - def _pos(self, i, j): - """Convert position in matrix to linear address in simulated memory.""" - - return i*self.N + j - - def read(self, i, j): - """Read value at position (i,j), used only in testing code.""" - - assert i >= 0 and i < self.N and j >= 0 and j < self.N, "Read out of range: ({},{})".format(i, j) - addr = self._pos(i, j) - self._access(addr) - return self.items[addr] - - def write(self, i, j, value): - """Write value at position (i,j), used only in testing code.""" - - assert i >= 0 and i < self.N and j >= 0 and j < self.N, "Write out of range: ({},{})".format(i, j) - addr = self._pos(i, j) - self._access(addr) - self.items[addr] = value - - def swap(self, i1, j1, i2, j2): - """Swap items (i1,j1) and (i2,j2).""" - - assert i1 >= 0 and i1 < self.N and j1 >= 0 and j1 < self.N and \ - i2 >= 0 and i2 < self.N and j2 >= 0 and j2 < self.N, \ - "Swap out of range: ({},{}) with ({},{})".format(i1, j1, i2, j2) - if self.debug_level > 1: - print("\tSwap ({},{}) ({},{})".format(i1, j1, i2, j2)) - addr1 = self._pos(i1, j1) - addr2 = self._pos(i2, j2) - self._access(addr1) - self._access(addr2) - items = self.items - items[addr1], items[addr2] = items[addr2], items[addr1] - - def reset_stats(self): - """Reset statistic counters.""" - - self.stat_cache_misses = 0 - self.stat_accesses = 0 - - def _access(self, addr): - """Bring the given address to the cache.""" - - blocks = self.blocks - i = addr // self.B # Which block to bring - if blocks[i, B_CACHED] > 0: - self._lru_remove(i) - else: - if self.cache_used < self.cache_blocks: - # We still have room in the cache. - self.cache_used += 1 - if self.debug_level > 1: - print("\t\tLoading block {}".format(i)) - else: - # We need to evict the least-recently used block to make space. - replace = blocks[self.lru_head, B_LRU_PREV] - self._lru_remove(replace) - assert blocks[replace, B_CACHED] > 0, "Internal error: Buggy LRU list" - blocks[replace, B_CACHED] = 0 - if self.debug_level > 1: - print("\t\tLoading block {}, replacing {}".format(i, replace)) - blocks[i, B_CACHED] = 1 - self.stat_cache_misses += 1 - self._lru_add_after(i, self.lru_head) - self.stat_accesses += 1 - - def _lru_remove(self, i): - """Remove block from the LRU list.""" - - blocks = self.blocks - prev, next = blocks[i, B_LRU_PREV], blocks[i, B_LRU_NEXT] - blocks[prev, B_LRU_NEXT] = next - blocks[next, B_LRU_PREV] = prev - - def _lru_add_after(self, i, after): - """Add block at the given position in the LRU list.""" - - blocks = self.blocks - next = blocks[after, B_LRU_NEXT] - blocks[after, B_LRU_NEXT] = i - blocks[next, B_LRU_PREV] = i - blocks[i, B_LRU_NEXT] = next - blocks[i, B_LRU_PREV] = after - -class TestMatrix(CachedMatrix): - """A cached matrix extended by methods for testing.""" - - # Constructor is inherited - - def fill_matrix(self): - """Fill matrix with a testing pattern.""" - - if self.debug_level > 1: - print("\tInitializing") - N = self.N - for i in range(N): - for j in range(N): - self.write(i, j, i*N + j) - - def check_result(self): - """Check that the pattern corresponds to the properly transposed matrix.""" - - if self.debug_level > 1: - print("\tChecking") - N = self.N - for i in range(N): - for j in range(N): - want = j*N + i - have = self.read(i, j) - have_i = have // N - have_j = have % N - assert have == want, "Mismatch at position ({},{}): expected element from ({},{}), found element from ({},{})".format(i, j, j, i, have_i, have_j) - - def naive_transpose(self): - """Transpose the matrix naively.""" - - for i in range(self.N): - for j in range(i): - self.swap(i, j, j, i) diff --git a/07-matrix_transpose/python/matrix_transpose.py b/07-matrix_transpose/python/matrix_transpose.py deleted file mode 100644 index 77570f47c7ca42fc2c6d7367af5860a27ade5c1e..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/python/matrix_transpose.py +++ /dev/null @@ -1,24 +0,0 @@ -class Matrix: - """Interface of a matrix. - - This class provides only the matrix size N and a method for swapping - two items. The actual storage of the matrix in memory is provided by - subclasses in testing code. - """ - - def __init__(self, N): - self.N = N - - def swap(self, i1, j1, i2, j2): - """Swap elements (i1,j1) and (i2,j2).""" - - # Overridden in subclasses - raise NotImplementedError - - def transpose(self): - """Transpose the matrix.""" - - # TODO: Implement more efficiently - for i in range(self.N): - for j in range(i): - self.swap(i, j, j, i) diff --git a/07-matrix_transpose/python/matrix_transpose_test.py b/07-matrix_transpose/python/matrix_transpose_test.py deleted file mode 100644 index ba5c0eccd00e8ece0e1868a6c758739ea2de0c9c..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/python/matrix_transpose_test.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -import math -import sys - -from matrix_tests import TestMatrix - -def generic_test(N, M, B, max_ratio, debug_level): - m = TestMatrix(N, M, B, debug_level) - m.fill_matrix() - m.reset_stats() - m.transpose() - - print("\t{} misses in {} accesses".format(m.stat_cache_misses, m.stat_accesses)) - if m.stat_accesses: - mpa = m.stat_cache_misses / m.stat_accesses - lb = 1 / B - ratio = mpa / lb - print("\t{:.6f} misses/access (lower bound is {:.6f} => ratio {:.6f}, need {:.6f})".format(mpa, lb, ratio, max_ratio)) - assert ratio <= max_ratio, "Algorithm did too many I/O operations." - - m.check_result() - -# A list of all tests -tests = [ - # name N M B max_ratio debug_level - ("small2k", lambda: generic_test( 8, 32, 8, 8, 2 )), - ("small", lambda: generic_test( 13, 64, 8, 4, 2 )), - ("n100b16", lambda: generic_test( 100, 1024, 16, 3, 1 )), - ("n1000b16", lambda: generic_test(1000, 1024, 16, 3, 1 )), - ("n1000b64", lambda: generic_test(1000, 8192, 64, 3, 1 )), - ("n1000b256", lambda: generic_test(1000, 65536, 256, 5, 1 )), - ("n1000b4096", lambda: generic_test(1000, 65536, 4096, 50, 1 )), -] - -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)) diff --git a/07-matrix_transpose/task.md b/07-matrix_transpose/task.md deleted file mode 100644 index a07084a541a292d60d8a235d19fe20e2b7d0f490..0000000000000000000000000000000000000000 --- a/07-matrix_transpose/task.md +++ /dev/null @@ -1,4 +0,0 @@ -Implement cache-oblivious transposition of square matrices. - -The Python version requires the NumPy module for storing the matrix -in memory efficiently. diff --git a/08-matrix_experiment/cpp/Makefile b/08-matrix_experiment/cpp/Makefile deleted file mode 100644 index ab0539e2c28b67a2ed9735f41a2e986606d2c122..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/cpp/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -.PHONY: test -test: matrix_experiment_sim matrix_experiment_real - @rm -rf out && mkdir out - @for exp in m1024-b16 m8192-b64 m65536-b256 m65536-b4096 ; do \ - for impl in smart naive ; do \ - echo "t-sim-$$exp-$$impl" ; \ - ./matrix_experiment_sim $$exp $$impl >out/t-sim-$$exp-$$impl ; \ - done ; \ - done - @for impl in smart naive ; do \ - echo "t-real-$$impl" ; \ - ./matrix_experiment_real $$impl >out/t-real-$$impl ; \ - done - -CXXFLAGS=-std=c++11 -O3 -Wall -Wextra -g -Wno-sign-compare - -matrix_experiment_sim: matrix_transpose.h matrix_tests.h matrix_experiment_sim.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) matrix_experiment_sim.cpp -o $@ - -matrix_experiment_real: matrix_transpose.h matrix_tests.h matrix_experiment_real.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) matrix_experiment_real.cpp -o $@ - -.PHONY: clean -clean: - rm -f matrix_experiment_sim matrix_experiment_real - rm -rf out diff --git a/08-matrix_experiment/cpp/matrix_experiment_real.cpp b/08-matrix_experiment/cpp/matrix_experiment_real.cpp deleted file mode 100644 index 3d2a28aea1f8bfd6d74c1d2480e993cb5ea94048..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/cpp/matrix_experiment_real.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include <functional> -#include <string> -#include <vector> -#include <cstdio> -#include <cmath> -#include <iostream> - -#include <time.h> // FIXME - -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) { - cerr << "Test error: " << message << endl; - exit(1); -} - -class Matrix { - vector<unsigned> items; - unsigned &item(unsigned i, unsigned j) { return items[i*N + j]; } - public: - unsigned N; - Matrix(unsigned N) { this->N = N; items.resize(N*N, 0); } - - void swap(unsigned i1, unsigned j1, unsigned i2, unsigned j2) - { - // EXPECT(i1 < N && j1 < N && i2 < N && j2 < N, "Swap out of range: " + coord_string(i1, j1) + " with " + coord_string(i2, j2) + "."); - std::swap(item(i1, j1), item(i2, j2)); - } - - void naive_transpose() - { - for (unsigned i=0; i<N; i++) - for (unsigned j=0; j<i; j++) - swap(i, j, j, i); - } - -#include "matrix_transpose.h" -}; - -void real_test(bool naive) -{ - for (int e=40; e<=112; e++) { - unsigned N = (unsigned) pow(2, e/8.); - Matrix m(N); - - clock_t start_time, stop_time; - unsigned tries = 1; - do { - start_time = clock(); - for (unsigned t=0; t < tries; t++) { - if (naive) - m.naive_transpose(); - else - m.transpose(); - } - stop_time = clock(); - tries *= 2; - } while (stop_time - start_time < CLOCKS_PER_SEC/10); - // It is guaranteed that the total number of tries is odd :) - - double ns_per_item = (double)(stop_time - start_time) / CLOCKS_PER_SEC / (N*(N-1)) / tries * 1e9; - printf("%d\t%.6f\n", N, ns_per_item); - } -} - -int main(int argc, char **argv) -{ - if (argc != 2) { - fprintf(stderr, "Usage: %s (smart|naive)\n", argv[0]); - return 1; - } - - std::string mode = argv[1]; - - bool naive; - if (mode == "smart") - naive = false; - else if (mode == "naive") - naive = true; - else { - fprintf(stderr, "The argument must be either 'smart' or 'naive'\n"); - return 1; - } - - real_test(naive); - return 0; -} diff --git a/08-matrix_experiment/cpp/matrix_experiment_sim.cpp b/08-matrix_experiment/cpp/matrix_experiment_sim.cpp deleted file mode 100644 index 5eca7abdb4737e2a8c91419674f05ff2e7013e35..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/cpp/matrix_experiment_sim.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include <functional> -#include <string> -#include <vector> -#include <cstdio> -#include <cmath> -#include <string> -#include <iostream> - -#include <time.h> // FIXME - -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) { - cerr << "Test error: " << message << endl; - exit(1); -} - -#include "matrix_tests.h" - -void simulated_test(unsigned M, unsigned B, bool naive) -{ - for (int e=20; e<=52; e++) { - unsigned N = (unsigned) pow(2, e/4.); - TestMatrix m(N, M, B, 0); - m.fill_matrix(); - m.reset_stats(); - if (naive) - m.naive_transpose(); - else - m.transpose(); - - double misses_per_item = (double) m.stat_cache_misses / (N*(N-1)); - printf("%d\t%.6f\n", N, misses_per_item); - - m.check_result(); - } -} - -vector<pair<string, function<void(bool n)>>> tests = { -// M B - { "m1024-b16", [](bool n) { simulated_test( 1024, 16, n); } }, - { "m8192-b64", [](bool n) { simulated_test( 8192, 64, n); } }, - { "m65536-b256", [](bool n) { simulated_test(65536, 256, n); } }, - { "m65536-b4096", [](bool n) { simulated_test(65536, 4096, n); } }, -}; - -int main(int argc, char **argv) -{ - if (argc != 3) { - fprintf(stderr, "Usage: %s <test> (smart|naive)\n", argv[0]); - return 1; - } - - std::string which_test = argv[1]; - std::string mode = argv[2]; - - bool naive; - if (mode == "smart") - naive = false; - else if (mode == "naive") - naive = true; - else - { - fprintf(stderr, "Last argument must be either 'smart' or 'naive'\n"); - return 1; - } - - for (const auto& test : tests) { - if (test.first == which_test) { - test.second(naive); - return 0; - } - } - - fprintf(stderr, "Unknown test %s\n", which_test.c_str()); - return 1; -} diff --git a/08-matrix_experiment/cpp/matrix_tests.h b/08-matrix_experiment/cpp/matrix_tests.h deleted file mode 100644 index 6110f8efc37782f04f949fb30f9e18392cc325e2..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/cpp/matrix_tests.h +++ /dev/null @@ -1,213 +0,0 @@ -#include <string> -#include <vector> -#include <iostream> -#include <algorithm> - -/* A matrix stored in a simulated cache */ - -class CachedMatrix { - unsigned B; // Block size - unsigned mem_blocks; // Memory size in blocks - unsigned cache_blocks; // Cache size in blocks - unsigned cache_used; // How many blocks of cache we already used - - // We store the matrix as a one-dimensional array - vector<unsigned> items; - unsigned pos(unsigned i, unsigned j) { return i*N + j; } - - /* - * For each memory block, we keep the following structure. - * If the block is currently cached, we set cached == true - * and lru_prev/next point to neighboring blocks in the cyclic LRU list. - * Otherwise, cached == false and the block is not in the LRU. - */ - - class MemoryBlock { - public: - unsigned lru_prev, lru_next; - bool cached; - MemoryBlock() - { - lru_prev = lru_next = 0; - cached = false; - } - }; - - vector<MemoryBlock> blocks; - - // One block at the end of "blocks" serves as a head of the LRU list. - unsigned lru_head; - - public: - // Number of rows and columns of the matrix - unsigned N; - - int debug_level; // Verbosity - - CachedMatrix(unsigned N, unsigned M, unsigned B, int debug_level=0) - { - EXPECT(N > 0, "CachedMatrix must be non-empty."); - EXPECT(B > 0, "Blocks must be non-empty."); - EXPECT(!(M % B), "Cache size must be divisible by block size."); - EXPECT(M >= 2*B, "Cache must have at least 2 blocks."); - - unsigned NN = N*N; - items.resize(NN, 0); - - this->N = N; - this->B = B; - this->debug_level = debug_level; - mem_blocks = (NN+B-1) / B; - cache_blocks = M / B; - cache_used = 0; - - // Initialize the LRU list - blocks.resize(mem_blocks + 1); - lru_head = mem_blocks; - blocks[lru_head].lru_prev = lru_head; - blocks[lru_head].lru_next = lru_head; - - if (debug_level > 0) - cout << "\tMemory: " << mem_blocks << " blocks of " << B << " items, " << cache_blocks << " cached\n"; - } - - // Read value at position (i,j), used only in testing code - unsigned read(unsigned i, unsigned j) - { - EXPECT(i < N && j < N, "Read out of range: " + coord_string(i, j) + "."); - unsigned addr = pos(i, j); - access(addr); - return items[addr]; - } - - // Write value at position (i,j), used only in testing code - void write(unsigned i, unsigned j, unsigned data) - { - EXPECT(i < N && j < N, "Write out of range: " + coord_string(i, j) + "."); - unsigned addr = pos(i, j); - access(addr); - items[addr] = data; - } - - // Swap items (i1,j1) and (i2,j2) - void swap(unsigned i1, unsigned j1, unsigned i2, unsigned j2) - { - EXPECT(i1 < N && j1 < N && i2 < N && j2 < N, "Swap out of range: " + coord_string(i1, j1) + " with " + coord_string(i2, j2) + "."); - if (debug_level > 1) - cout << "\tSwap " << coord_string(i1, j1) << " " << coord_string(i2, j2) << endl; - unsigned addr1 = pos(i1, j1), addr2 = pos(i2, j2); - access(addr1); - access(addr2); - std::swap(items[addr1], items[addr2]); - } - - unsigned stat_cache_misses; - unsigned stat_accesses; - - // Reset statistic counters. - void reset_stats() - { - stat_cache_misses = 0; - stat_accesses = 0; - } - - static string coord_string(unsigned i, unsigned j) - { - return "(" + to_string(i) + "," + to_string(j) + ")"; - } - -#include "matrix_transpose.h" - - private: - // Bring the given address to the cache. - void access(unsigned addr) - { - int i = addr / B; // Which block to bring - if (blocks[i].cached) { - lru_remove(i); - } else { - if (cache_used < cache_blocks) { - // We still have room in the cache. - cache_used++; - if (debug_level > 1) - cout << "\t\tLoading block " << i << endl; - } else { - // We need to evict the least-recently used block to make space. - unsigned replace = blocks[lru_head].lru_prev; - lru_remove(replace); - EXPECT(blocks[replace].cached, "Internal error: Buggy LRU list."); - blocks[replace].cached = false; - if (debug_level > 1) - cout << "\t\tLoading block " << i << ", replacing " << replace << endl; - } - blocks[i].cached = true; - stat_cache_misses++; - } - lru_add_after(i, lru_head); - stat_accesses++; - } - - // Remove block from the LRU list. - void lru_remove(unsigned i) - { - unsigned prev = blocks[i].lru_prev; - unsigned next = blocks[i].lru_next; - blocks[prev].lru_next = next; - blocks[next].lru_prev = prev; - } - - // Add block at the given position in the LRU list. - void lru_add_after(unsigned i, unsigned after) - { - unsigned next = blocks[after].lru_next; - blocks[next].lru_prev = i; - blocks[after].lru_next = i; - blocks[i].lru_next = next; - blocks[i].lru_prev = after; - } -}; - -/* A cached matrix extended by methods for testing */ - -class TestMatrix : public CachedMatrix { - public: - TestMatrix(unsigned N, unsigned M, unsigned B, int debug_level = 0) : CachedMatrix(N, M, B, debug_level) { } - - // Fill matrix with a testing pattern. - void fill_matrix() - { - if (debug_level > 1) - cout << "\tInitializing\n"; - for (unsigned i = 0; i < N; i++) - for (unsigned j = 0; j < N; j++) - write(i, j, i*N + j); - } - - // Check that the pattern corresponds to the properly transposed matrix. - void check_result() - { - if (debug_level > 1) - cout << "\tChecking\n"; - for (unsigned i = 0; i < N; i++) { - for (unsigned j = 0; j < N; j++) { - unsigned want = j*N + i; - unsigned found = read(i, j); - unsigned found_i = found / N; - unsigned found_j = found % N; - EXPECT(found == want, - "Mismatch at position " + coord_string(i, j) + - ": expected element from " + coord_string(j, i) + - ", found element from " + coord_string(found_i, found_j) + - "."); - } - } - } - - // Transpose the matrix naively. - void naive_transpose() - { - for (unsigned i=0; i<N; i++) - for (unsigned j=0; j<i; j++) - swap(i, j, j, i); - } -}; diff --git a/08-matrix_experiment/python/Makefile b/08-matrix_experiment/python/Makefile deleted file mode 100644 index 6ae85a911a8e15919075926571af214c8c2e2657..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/python/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -TESTS=m1024-b16 m8192-b64 m65536-b256 m65536-b4096 -TESTFILES=$(addprefix out/t-sim-,$(TESTS)) - -.PHONY: test -test: $(addsuffix -smart,$(TESTFILES)) $(addsuffix -naive,$(TESTFILES)) - -out/t-sim-%-naive: - @mkdir -p out - ./matrix_experiment_sim.py $* naive >$@ - -out/t-sim-%-smart: - @mkdir -p out - ./matrix_experiment_sim.py $* smart >$@ - -.PHONY: clean -clean: - rm -rf out diff --git a/08-matrix_experiment/python/matrix_experiment_sim.py b/08-matrix_experiment/python/matrix_experiment_sim.py deleted file mode 100755 index 5f326f193d349f11ccb73b77c984ea26a4dba597..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/python/matrix_experiment_sim.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 -import sys - -from matrix_tests import TestMatrix - -def simulated_test(M, B, naive): - for e in range(10, 25): - N = int(2 ** (e/2)) - print(" ", N, M, B, file=sys.stderr) - m = TestMatrix(N, M, B, 0) - m.fill_matrix() - m.reset_stats() - if naive: - m.naive_transpose() - else: - m.transpose() - misses_per_item = m.stat_cache_misses / (N*(N-1)) - print(N, misses_per_item, flush=True) - m.check_result() - -tests = { -# M B - "m1024-b16": lambda n: simulated_test( 1024, 16, n), - "m8192-b64": lambda n: simulated_test( 8192, 64, n), - "m65536-b256": lambda n: simulated_test(65536, 256, n), - "m65536-b4096": lambda n: simulated_test(65536, 4096, n), -} - -if len(sys.argv) == 3: - test = sys.argv[1] - if sys.argv[2] == "smart": - naive = False - elif sys.argv[2] == "naive": - naive = True - else: - raise ValueError("Last argument must be either 'smart' or 'naive'") - if test in tests: - tests[test](naive) - else: - raise ValueError("Unknown test {}".format(test)) -else: - raise ValueError("Usage: {} <test> (smart|naive)".format(sys.argv[0])) diff --git a/08-matrix_experiment/python/matrix_tests.py b/08-matrix_experiment/python/matrix_tests.py deleted file mode 100644 index f002701a4e68f65cbde033b2058a79d71aad61ba..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/python/matrix_tests.py +++ /dev/null @@ -1,165 +0,0 @@ -from matrix_transpose import Matrix -import numpy - -# Description of memory blocks is stored in an array blocks[block_index][B_xxx]. -# If the block is cached, B_CACHED is 1 and B_LRU_NEXT and B_LRU_PREV point to -# neighboring blocks in the cyclic LRU list. Otherwise, B_CACHED is 0 and the block -# is not in the LRU. -B_CACHED = 0 -B_LRU_NEXT = 1 -B_LRU_PREV = 2 - -class CachedMatrix(Matrix): - """A matrix stored in simulated cache""" - - def __init__(self, N, M, B, debug_level=0): - assert N>0, "CachedMatrix must be non-empty." - assert B>0, "Blocks must be non-empty." - assert M%B == 0, "Cache size must be divisible by block size." - assert M >= 2*B, "Cache must have at least 2 blocks." - - Matrix.__init__(self, N) - - NN = N*N - self.items = numpy.zeros(shape=NN, dtype=numpy.int32, order="C") - - self.B = B - self.M = M - self.debug_level = debug_level - self.mem_blocks = (NN+B-1) // B - self.cache_blocks = M//B - self.cache_used = 0 - - # Initialize the LRU list. There is a virtual block right after the last real block, - # which serves as a head of the cyclic LRU list. - self.blocks = numpy.zeros(shape=(self.mem_blocks+1, 3), dtype=numpy.int32, order="C") - self.lru_head = self.mem_blocks - self.blocks[self.lru_head, B_LRU_NEXT] = self.lru_head - self.blocks[self.lru_head, B_LRU_PREV] = self.lru_head - - self.reset_stats() - - if debug_level > 0: - print("\tMemory: {} blocks of {} items, {} cached".format(self.mem_blocks, self.B, self.cache_blocks)) - - def _pos(self, i, j): - """Convert position in matrix to linear address in simulated memory.""" - - return i*self.N + j - - def read(self, i, j): - """Read value at position (i,j), used only in testing code.""" - - assert i >= 0 and i < self.N and j >= 0 and j < self.N, "Read out of range: ({},{})".format(i, j) - addr = self._pos(i, j) - self._access(addr) - return self.items[addr] - - def write(self, i, j, value): - """Write value at position (i,j), used only in testing code.""" - - assert i >= 0 and i < self.N and j >= 0 and j < self.N, "Write out of range: ({},{})".format(i, j) - addr = self._pos(i, j) - self._access(addr) - self.items[addr] = value - - def swap(self, i1, j1, i2, j2): - """Swap items (i1,j1) and (i2,j2).""" - - assert i1 >= 0 and i1 < self.N and j1 >= 0 and j1 < self.N and \ - i2 >= 0 and i2 < self.N and j2 >= 0 and j2 < self.N, \ - "Swap out of range: ({},{}) with ({},{})".format(i1, j1, i2, j2) - if self.debug_level > 1: - print("\tSwap ({},{}) ({},{})".format(i1, j1, i2, j2)) - addr1 = self._pos(i1, j1) - addr2 = self._pos(i2, j2) - self._access(addr1) - self._access(addr2) - items = self.items - items[addr1], items[addr2] = items[addr2], items[addr1] - - def reset_stats(self): - """Reset statistic counters.""" - - self.stat_cache_misses = 0 - self.stat_accesses = 0 - - def _access(self, addr): - """Bring the given address to the cache.""" - - blocks = self.blocks - i = addr // self.B # Which block to bring - if blocks[i, B_CACHED] > 0: - self._lru_remove(i) - else: - if self.cache_used < self.cache_blocks: - # We still have room in the cache. - self.cache_used += 1 - if self.debug_level > 1: - print("\t\tLoading block {}".format(i)) - else: - # We need to evict the least-recently used block to make space. - replace = blocks[self.lru_head, B_LRU_PREV] - self._lru_remove(replace) - assert blocks[replace, B_CACHED] > 0, "Internal error: Buggy LRU list" - blocks[replace, B_CACHED] = 0 - if self.debug_level > 1: - print("\t\tLoading block {}, replacing {}".format(i, replace)) - blocks[i, B_CACHED] = 1 - self.stat_cache_misses += 1 - self._lru_add_after(i, self.lru_head) - self.stat_accesses += 1 - - def _lru_remove(self, i): - """Remove block from the LRU list.""" - - blocks = self.blocks - prev, next = blocks[i, B_LRU_PREV], blocks[i, B_LRU_NEXT] - blocks[prev, B_LRU_NEXT] = next - blocks[next, B_LRU_PREV] = prev - - def _lru_add_after(self, i, after): - """Add block at the given position in the LRU list.""" - - blocks = self.blocks - next = blocks[after, B_LRU_NEXT] - blocks[after, B_LRU_NEXT] = i - blocks[next, B_LRU_PREV] = i - blocks[i, B_LRU_NEXT] = next - blocks[i, B_LRU_PREV] = after - -class TestMatrix(CachedMatrix): - """A cached matrix extended by methods for testing.""" - - # Constructor is inherited - - def fill_matrix(self): - """Fill matrix with a testing pattern.""" - - if self.debug_level > 1: - print("\tInitializing") - N = self.N - for i in range(N): - for j in range(N): - self.write(i, j, i*N + j) - - def check_result(self): - """Check that the pattern corresponds to the properly transposed matrix.""" - - if self.debug_level > 1: - print("\tChecking") - N = self.N - for i in range(N): - for j in range(N): - want = j*N + i - have = self.read(i, j) - have_i = have // N - have_j = have % N - assert have == want, "Mismatch at position ({},{}): expected element from ({},{}), found element from ({},{})".format(i, j, j, i, have_i, have_j) - - def naive_transpose(self): - """Transpose the matrix naively.""" - - for i in range(self.N): - for j in range(i): - self.swap(i, j, j, i) diff --git a/08-matrix_experiment/task.md b/08-matrix_experiment/task.md deleted file mode 100644 index 4e36fb582dc30644691a3a97b9258fd1bb767cc4..0000000000000000000000000000000000000000 --- a/08-matrix_experiment/task.md +++ /dev/null @@ -1,87 +0,0 @@ -## Goal - -The goal of this assignment is to evaluate your implementation of cache-oblivious -matrix transposition experimentally and to compare it with the trivial algorithm -which transposes by definition. - -You are given a test program (`matrix_experiment_sim`) which evaluates your -implementation from the previous assignment on different simulated caches and -matrices of different sizes. For each experiment, the average number of cache -misses per item is reported (the diagonal items which do not move are not -counted). The program also evaluates performance of the trivial transposition algorithm. - -You should run these experiments and write a report, which contains one plot of -the measured data for each cache type, showing dependency of the average number of -misses on the matrix size. There should be two curves in each plot: one for your -algorithm, another for the trivial one. - -The report should discuss the experimental results and try to explain the observed -behavior (including any strange anomalies) using theory from the lectures. -If you want, you can carry out further experiments to gain better understanding -of the algorithm and include these in the report. - -You should submit a PDF file with the report (and no source code). -You will get 1 temporary point upon submission if the file is syntactically correct; -proper points will be assigned later. - -## Test program - -The test program is given two arguments: -- Cache type: - - `m1024b16` – cache of 1024 items organized in 16-item blocks - - `m8192b64` – cache of 8192 items organized in 64-item blocks - - `m65536b256` – cache of 65536 items organized on 256-item blocks - - `m65536b4096` – cache of 65536 items organized in 4096-item blocks -- The implementation to test (`smart` or `naive`). - -The output of the program contains one line per experiment, which consists of -the matrix size and the average number of cache misses. - -*Warning:* The Python tests are slow, even though they use only a subset of the -matrix sizes. They can take about one hour to complete. -If your machine has multiple processors or cores, you can try `make -j` -to run the tests in parallel. - -## Optional: Tests on real hardware (for 5 extra points) - -You can also test your transposition algorithm on real hardware -using the `matrix_experiment_real` program. The matrix is stored in row-major -order, each item takes 4 bytes. - -The program takes one parameter, the implementation to test: `smart` or `naive`. -Its output contains one line per experiment, which consists of the matrix size -and the average time per item in nanoseconds. - -However, the program is available only for C++, because general slowness of -Python completely hides all cache effects. - -Again, your report should show a plot of the measured data and discuss the observed -effects. You should also include the configuration of caches in your machine. -(If you are using Linux, you can try the `machinfo` script from -[this repository](https://gitlab.kam.mff.cuni.cz/mj/aim.git).) - -## Hints - -The following tools can be useful for producing nice plots: -- [pandas](https://pandas.pydata.org/) -- [matplotlib](https://matplotlib.org/) -- [gnuplot](http://www.gnuplot.info/) - -A quick checklist for plots: -- Is there a caption explaining what is plotted? -- Are the axes clearly labelled? Do they have value ranges and units? -- Have you mentioned that this axis has logarithmic scale? (Logarithmic graphs - are more fitting in some cases, but you should tell.) -- Is it clear which curve means what? -- Is it clear what are the measured points and what is an interpolated - curve between them? -- Are there any overlaps? (E.g., the most interesting part of the curve - hidden underneath a label?) -- **Is the graph distorted by compression artifacts?** (No, you shouldn't use JPEG for plots!) - -In your discussion, please distinguish the following kinds of claims. -It should be always clear which is which: -- Experimental results (i.e., the raw data you obtained from the experiments) -- Theoretical facts (i.e., claims we have proved mathematically) -- Your hypotheses (e.g., when you claim that the graph looks like something is true, - but you are not able to prove rigorously that it always holds) diff --git a/09-cuckoo_hash/cpp/Makefile b/09-cuckoo_hash/cpp/Makefile deleted file mode 100644 index f32e87ad710a520cec3a5f5a211462fcb0b97fa1..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/cpp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -test: cuckoo_hash_test - ./$< - -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -I$(INCLUDE) - -cuckoo_hash_test: cuckoo_hash_test.cpp cuckoo_hash.h test_main.cpp $(INCLUDE)/random.h - $(CXX) $(CXXFLAGS) $^ -o $@ - -clean: - rm -f cuckoo_hash_test - -.PHONY: clean test diff --git a/09-cuckoo_hash/cpp/cuckoo_hash.h b/09-cuckoo_hash/cpp/cuckoo_hash.h deleted file mode 100644 index 6323dffaf013116c739a1519b0564f408f2ff739..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/cpp/cuckoo_hash.h +++ /dev/null @@ -1,102 +0,0 @@ -#include <string> -#include <vector> -#include <cstdint> -#include <iostream> - -#include "random.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); - -class TabulationHash { - /* - * Hash function for hashing by tabulation. - * - * The 32-bit key is split to four 8-bit parts. Each part indexes - * a separate table of 256 randomly generated values. Obtained values - * are XORed together. - */ - - unsigned num_buckets; - uint32_t tables[4][256]; - -public: - TabulationHash(unsigned num_buckets, RandomGen *random_gen) - { - this->num_buckets = num_buckets; - for (int i=0; i<4; i++) - for (int j=0; j<256; j++) - tables[i][j] = random_gen->next_u32(); - } - - uint32_t hash(uint32_t key) - { - unsigned h0 = key & 0xff; - unsigned h1 = (key >> 8) & 0xff; - unsigned h2 = (key >> 16) & 0xff; - unsigned h3 = (key >> 24) & 0xff; - return (tables[0][h0] ^ tables[1][h1] ^ tables[2][h2] ^ tables[3][h3]) % num_buckets; - } -}; - -class CuckooTable { - /* - * Hash table with Cuckoo hashing. - * - * We have two hash functions, which map 32-bit keys to buckets of a common - * hash table. Unused buckets contain 0xffffffff. - */ - - const uint32_t UNUSED = 0xffffffff; - - // The array of buckets - vector<uint32_t> table; - unsigned num_buckets; - - // Hash functions and the random generator used to create them - TabulationHash *hashes[2]; - RandomGen *random_gen; - -public: - - CuckooTable(unsigned num_buckets) - { - // Initialize the table with the given number of buckets. - - this->num_buckets = num_buckets; - table.resize(num_buckets, UNUSED); - - // Obtain two fresh hash functions. - random_gen = new RandomGen(42); - for (int i=0; i<2; i++) - hashes[i] = new TabulationHash(num_buckets, random_gen); - } - - ~CuckooTable() - { - for (int i=0; i<2; i++) - delete hashes[i]; - delete random_gen; - } - - bool lookup(uint32_t key) - { - // Check if the table contains the given key. Returns True or False. - unsigned h0 = hashes[0]->hash(key); - unsigned h1 = hashes[1]->hash(key); - return (table[h0] == key || table[h1] == key); - } - - void insert(uint32_t key) - { - // Insert a new key to the table. Assumes that the key is not present yet. - EXPECT(key != UNUSED, "Keys must differ from UNUSED."); - - // TODO: Implement - } - -}; diff --git a/09-cuckoo_hash/cpp/cuckoo_hash_test.cpp b/09-cuckoo_hash/cpp/cuckoo_hash_test.cpp deleted file mode 100644 index 84ececb2de628938f855590f0f0b06fa60f73957..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/cpp/cuckoo_hash_test.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <functional> -#include <cstdlib> -#include <vector> - -#include "cuckoo_hash.h" - -void simple_test(unsigned n, unsigned table_size_percentage) -{ - CuckooTable table(n * table_size_percentage / 100); - - for (unsigned i=0; i < n; i++) - table.insert(37*i); - - for (unsigned i=0; i < n; i++) { - EXPECT(table.lookup(37*i), "Item not present in table, but it should be."); - EXPECT(!table.lookup(37*i+1), "Item present in table, even though it should not be."); - } -} - -void multiple_test(unsigned min_n, unsigned max_n, unsigned step_n, unsigned table_size_percentage) -{ - for (unsigned n=min_n; n < max_n; n += step_n) { - printf("\tn=%u\n", n); - simple_test(n, table_size_percentage); - } -} - -/*** A list of all tests ***/ - -vector<pair<string, function<void()>>> tests = { - { "small", [] { simple_test(100, 400); } }, - { "middle", [] { simple_test(31415, 300); } }, - { "big", [] { simple_test(1000000, 300); } }, - { "tight", [] { multiple_test(20000, 40000, 500, 205); } }, -}; diff --git a/09-cuckoo_hash/cpp/random.h b/09-cuckoo_hash/cpp/random.h deleted file mode 100644 index 7d18ab60dfd6302a9261fc034f28e91d37eca78b..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/cpp/random.h +++ /dev/null @@ -1,59 +0,0 @@ -#define DS1_RANDOM_H - -#include <cstdint> - -/* - * This is the xoroshiro128+ random generator, designed in 2016 by David Blackman - * and Sebastiano Vigna, distributed under the CC-0 license. For more details, - * see http://vigna.di.unimi.it/xorshift/. - * - * Rewritten to C++ by Martin Mares, also placed under CC-0. - */ - -class RandomGen { - uint64_t state[2]; - - uint64_t rotl(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - public: - // Initialize the generator, set its seed and warm it up. - RandomGen(unsigned int seed) - { - state[0] = seed * 0xdeadbeef; - state[1] = seed ^ 0xc0de1234; - for (int i=0; i<100; i++) - next_u64(); - } - - // Generate a random 64-bit number. - uint64_t next_u64(void) - { - uint64_t s0 = state[0], s1 = state[1]; - uint64_t result = s0 + s1; - s1 ^= s0; - state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); - state[1] = rotl(s1, 36); - return result; - } - - // Generate a random 32-bit number. - uint32_t next_u32(void) - { - return next_u64() >> 11; - } - - // Generate a number between 0 and range-1. - unsigned int next_range(unsigned int range) - { - /* - * This is not perfectly uniform, unless the range is a power of two. - * However, for 64-bit random values and 32-bit ranges, the bias is - * insignificant. - */ - return next_u64() % range; - } -}; - diff --git a/09-cuckoo_hash/cpp/test_main.cpp b/09-cuckoo_hash/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/09-cuckoo_hash/python/cuckoo_hash.py b/09-cuckoo_hash/python/cuckoo_hash.py deleted file mode 100644 index a224d648b40958ea8e79747bad2cc196230c22f0..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/python/cuckoo_hash.py +++ /dev/null @@ -1,55 +0,0 @@ -import random -import math - -class TabulationHash: - """Hash function for hashing by tabulation. - - The 32-bit key is split to four 8-bit parts. Each part indexes - a separate table of 256 randomly generated values. Obtained values - are XORed together. - """ - - def __init__(self, num_buckets): - self.tables = [None] * 4 - for i in range(4): - self.tables[i] = [random.randint(0, 0xffffffff) for _ in range(256)] - self.num_buckets = num_buckets - - def hash(self, key): - h0 = key & 0xff; - h1 = (key >> 8) & 0xff; - h2 = (key >> 16) & 0xff; - h3 = (key >> 24) & 0xff; - t = self.tables - return (t[0][h0] ^ t[1][h1] ^ t[2][h2] ^ t[3][h3]) % self.num_buckets - -class CuckooTable: - """Hash table with Cuckoo hashing. - - We have two hash functions, which map 32-bit keys to buckets of a common - hash table. Unused buckets contain None. - """ - - def __init__(self, num_buckets): - """Initialize the table with the given number of buckets.""" - - # The array of buckets - self.num_buckets = num_buckets - self.table = [None] * num_buckets - - # Create two fresh hash functions - self.hashes = [TabulationHash(num_buckets), TabulationHash(num_buckets)] - - def lookup(self, key): - """Check if the table contains the given key. Returns True or False.""" - - b0 = self.hashes[0].hash(key) - b1 = self.hashes[1].hash(key) - # print("## Lookup key={} b0={} b1={}".format(key, b0, b1)) - return self.table[b0] == key or self.table[b1] == key - - def insert(self, key): - """Insert a new key to the table. Assumes that the key is not present yet.""" - - # TODO: Implement - raise NotImplementedError diff --git a/09-cuckoo_hash/python/cuckoo_hash_test.py b/09-cuckoo_hash/python/cuckoo_hash_test.py deleted file mode 100755 index f9137c45d9a5cfd8e3f0f526626961dbffe78c30..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/python/cuckoo_hash_test.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -import sys -import random - -from cuckoo_hash import CuckooTable - -def simple_test(n, table_size_percentage): - random.seed(42) - table = CuckooTable(n*table_size_percentage//100) - - # Insert an arithmetic progression - for i in range(n): - table.insert(37*i) - - # Verify contents of the table - for i in range(n): - assert table.lookup(37*i), "Item not present in table, but it should be." - assert not table.lookup(37*i+1), "Item present in table, even though it should not be." - -def multiple_test(min_n, max_n, step_n, table_size_percentage): - for n in range(min_n, max_n, step_n): - print("\tn={}".format(n)) - simple_test(n, table_size_percentage) - -# A list of all tests -tests = [ - ("small", lambda: simple_test(100, 400)), - ("middle", lambda: simple_test(31415, 300)), - ("big", lambda: simple_test(1000000, 300)), - ("tight", lambda: multiple_test(20000, 40000, 500, 205)), -] - -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)) diff --git a/09-cuckoo_hash/task.md b/09-cuckoo_hash/task.md deleted file mode 100644 index e568f723037a3b052e8f08b5ce21172bd6d93bc4..0000000000000000000000000000000000000000 --- a/09-cuckoo_hash/task.md +++ /dev/null @@ -1,9 +0,0 @@ -Implement Cuckoo hash table with simple tabulation hashing. - -You are given a skeleton code which defines the table, implements -`lookup()`, and provides hash functions. You have to add an `insert()` -method. - -If too many elements are moved during a single insert, the table must -be rehashed with new hash functions. See lecture notes for the particular -bounds. diff --git a/10-hash_experiment/cpp/Makefile b/10-hash_experiment/cpp/Makefile deleted file mode 100644 index d844438cdebcecece806d4a705a505dcaff2370b..0000000000000000000000000000000000000000 --- a/10-hash_experiment/cpp/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -I$(INCLUDE) - -hash_experiment: hash_experiment.cpp $(INCLUDE)/random.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) hash_experiment.cpp -o $@ - -.PHONY: clean -clean: - rm -f hash_experiment diff --git a/10-hash_experiment/cpp/hash_experiment.cpp b/10-hash_experiment/cpp/hash_experiment.cpp deleted file mode 100644 index 7eac12f5a6eae0dbb753ebfa6ee71dd2804feb54..0000000000000000000000000000000000000000 --- a/10-hash_experiment/cpp/hash_experiment.cpp +++ /dev/null @@ -1,314 +0,0 @@ -#include <vector> -#include <functional> -#include <algorithm> -#include <utility> -#include <stdexcept> -#include <stdio.h> -#include <stdint.h> -#include <math.h> -#include "random.h" - -using namespace std; - -RandomGen rng(42); - -typedef uint32_t uint; - -typedef function<uint(uint)> HashFunction; -typedef function<HashFunction(unsigned num_buckets)> HashFunctionFactory; - -/* - * Hash function for hashing by tabulation. - * - * The 32-bit key is split to four 8-bit parts. Each part indexes - * a separate table of 256 randomly generated values. Obtained values - * are XORed together. - */ -class TabulationHash { - unsigned num_buckets; - vector<uint> tables; - - TabulationHash(unsigned num_buckets) : num_buckets(num_buckets), tables(4 * 256) { - for (uint& x : tables) x = rng.next_u32(); - } - - public: - static HashFunction factory(unsigned num_buckets) { - return HashFunction(TabulationHash(num_buckets)); - } - - uint operator()(uint key) { - return ( - tables[key & 0xff] ^ - tables[((key >> 8) & 0xff) | 0x100] ^ - tables[((key >> 16) & 0xff) | 0x200] ^ - tables[((key >> 24) & 0xff) | 0x300] - ) % num_buckets; - } -}; - -// Hash function using polynomial modulo a prime. -template < int degree, uint prime = 2147483647 > -class PolynomialHash { - unsigned num_buckets; - vector<uint> coefs; - - PolynomialHash(unsigned num_buckets) : num_buckets(num_buckets), coefs(degree + 1) { - for (uint& x : coefs) x = rng.next_u32(); - } - - public: - static HashFunction factory(unsigned num_buckets) { - return HashFunction(PolynomialHash(num_buckets)); - } - - uint operator()(uint key) { - uint64_t acc = 0; - for (uint c : coefs) acc = (acc * key + c) % prime; - return (uint)(acc % num_buckets); - } -}; - -typedef PolynomialHash<1> LinearHash; -typedef PolynomialHash<2> QuadraticHash; - -// Multiply-shift hash function taking top bits of 32-bit word -class MultiplyShiftLowHash { - uint mult; - uint mask; - int shift = 0; - - MultiplyShiftLowHash(unsigned num_buckets) { - mult = rng.next_u32() | 0x1; - mask = num_buckets - 1; - - if (mask & num_buckets) - throw runtime_error("MultiplyShiftLowHash: num_buckets must be power of 2"); - - unsigned tmp = num_buckets - 1; - while ((0x80000000U & tmp) == 0) { - tmp <<= 1; - shift++; - } - } - - public: - static HashFunction factory(unsigned num_buckets) { - return HashFunction(MultiplyShiftLowHash(num_buckets)); - } - - uint operator()(uint key) { - return ((key * mult) >> shift) & mask; - } -}; - -// Multiply-shift hash function taking low bits of upper half of 64-bit word -class MultiplyShiftHighHash { - uint mask; - uint64_t mult; - - MultiplyShiftHighHash(unsigned num_buckets) { - mult = rng.next_u64() | 0x1; - mask = num_buckets - 1; - - if (mask & num_buckets) - throw runtime_error("MultiplyShiftHighHash: num_buckets must be power of 2"); - } - - public: - static HashFunction factory(unsigned num_buckets) { - return HashFunction(MultiplyShiftHighHash(num_buckets)); - } - - uint operator()(uint key) { - return ((key * mult) >> 32) & mask; - } -}; - - -// Hash table with linear probing -class HashTable { - HashFunction hash; - vector<uint> table; - unsigned size = 0; - - unsigned ops; - unsigned max_; - uint64_t steps; - - public: - // We reserve one integer to mark unused buckets. This integer - // cannot be stored in the table. - static constexpr uint UNUSED = ~((uint)0); - - HashTable(const HashFunctionFactory& factory, unsigned num_buckets) : - hash(factory(num_buckets)), table(num_buckets, +UNUSED) { - reset_counter(); - } - - // Check whether key is present in the table. - bool lookup(uint key) { - if (key == UNUSED) throw runtime_error("Cannot lookup UNUSED"); - - bool ret = false; - unsigned steps = 1; - - uint b = hash(key); - while (table[b] != UNUSED) { - if (table[b] == key) { - ret = true; - break; - } - steps++; - b = next_bucket(b); - } - - update_counter(steps); - return ret; - } - - // Add the key in the table. - void insert(uint key) { - if (key == UNUSED) throw runtime_error("Cannot insert UNUSED"); - if (size >= table.size()) throw runtime_error("Insert: Table is full"); - - unsigned steps = 1; - uint b = hash(key); - - while (table[b] != UNUSED) { - if (table[b] == key) goto key_found; - steps++; - b = next_bucket(b); - } - - table[b] = key; - size++; - - key_found: - update_counter(steps); - } - - void reset_counter() { ops = steps = max_ = 0; } - double report_avg() { return ((double)steps) / max(1U, ops); } - double report_max() { return max_; } - - private: - void update_counter(unsigned steps) { - ops++; - this->steps += steps; - max_ = max(steps, max_); - } - - unsigned next_bucket(unsigned b) { return (b + 1) % table.size(); } -}; - -void usage_test(HashFunctionFactory factory, int max_usage = 90, int retry = 40) { - vector<double> avg(max_usage, 0.0); - vector<double> avg2(max_usage, 0.0); - - unsigned N = 1 << 20; - unsigned step_size = N / 100; - - vector<uint> elements(N); - for (unsigned i = 0; i < N; i++) elements[i] = i; - - for (int t = 0; t < retry; t++) { - HashTable H(factory, N); - for (unsigned i = 0; i < N-1; i++) - swap(elements[i], elements[i + (rng.next_u32() % (N-i))]); - - for (int s = 0; s < max_usage; s++) { - H.reset_counter(); - for (unsigned i = 0; i < step_size; i++) - H.insert(elements[s*step_size + i]); - - avg[s] += H.report_avg(); - avg2[s] += H.report_avg() * H.report_avg(); - } - } - - for (int i = 0; i < max_usage; i++) { - avg[i] /= retry; - avg2[i] /= retry; - double std_dev = sqrt(avg2[i] - avg[i]*avg[i]); - - printf("%i %.03lf %.03lf\n", i+1, avg[i], std_dev); - } -} - - -void grow_test(HashFunctionFactory factory, int usage = 60, int retry = 40, - int begin = 7, int end = 22) { - - for (int n = begin; n < end; n++) { - double avg = 0; - double avg2 = 0; - unsigned N = 1 << n; - - vector<uint> elements(N); - for (unsigned i = 0; i < N; i++) elements[i] = i; - - for (int t = 0; t < retry; t++) { - HashTable H(factory, N); - for (unsigned i = 0; i < N-1; i++) - swap(elements[i], elements[i + (rng.next_u32() % (N-i))]); - - for (unsigned i = 0; i < ((uint64_t)N) * usage / 100; i++) - H.insert(elements[i]); - - for (unsigned i = 0; i < N; i++) - H.lookup(i); - - avg += H.report_avg(); - avg2 += H.report_avg() * H.report_avg(); - } - - avg /= retry; - avg2 /= retry; - double std_dev = sqrt(avg2 - avg*avg); - - printf("%i %.03lf %.03lf\n", N, avg, std_dev); - } -} - -int main(int argc, char** argv) { - vector<pair<string, HashFunctionFactory>> grow_tests = { - {"grow-ms-low", MultiplyShiftLowHash::factory}, - {"grow-ms-high", MultiplyShiftHighHash::factory}, - {"grow-poly-1", LinearHash::factory}, - {"grow-poly-2", QuadraticHash::factory}, - {"grow-tab", TabulationHash::factory} - }; - vector<pair<string, HashFunctionFactory>> usage_tests = { - {"usage-ms-low", MultiplyShiftLowHash::factory}, - {"usage-ms-high", MultiplyShiftHighHash::factory}, - {"usage-poly-1", LinearHash::factory}, - {"usage-poly-2", QuadraticHash::factory}, - {"usage-tab", TabulationHash::factory} - }; - - if (argc != 3) goto fail; - - rng = RandomGen(atoi(argv[2])); - - for (auto t : grow_tests) { - if (t.first == argv[1]) { - grow_test(t.second); - return 0; - } - } - - for (auto t : usage_tests) { - if (t.first == argv[1]) { - usage_test(t.second); - return 0; - } - } - - fail: - printf("Usage: %s <test> <seed>\nAvailable tests are:", argv[0]); - for (auto t : grow_tests) printf(" %s", t.first.c_str()); - for (auto t : usage_tests) printf(" %s", t.first.c_str()); - return 1; -} - diff --git a/10-hash_experiment/cpp/random.h b/10-hash_experiment/cpp/random.h deleted file mode 100644 index 5ef10aeb1fe7e58a48277fb3565169ec267d43d9..0000000000000000000000000000000000000000 --- a/10-hash_experiment/cpp/random.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef DS1_RANDOM_H -#define DS1_RANDOM_H - -#include <cstdint> - -/* - * This is the xoroshiro128+ random generator, designed in 2016 by David Blackman - * and Sebastiano Vigna, distributed under the CC-0 license. For more details, - * see http://vigna.di.unimi.it/xorshift/. - * - * Rewritten to C++ by Martin Mares, also placed under CC-0. - */ - -class RandomGen { - uint64_t state[2]; - - uint64_t rotl(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - public: - // Initialize the generator, set its seed and warm it up. - RandomGen(unsigned int seed) - { - state[0] = seed * 0xdeadbeef; - state[1] = seed ^ 0xc0de1234; - for (int i=0; i<100; i++) - next_u64(); - } - - // Generate a random 64-bit number. - uint64_t next_u64(void) - { - uint64_t s0 = state[0], s1 = state[1]; - uint64_t result = s0 + s1; - s1 ^= s0; - state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); - state[1] = rotl(s1, 36); - return result; - } - - // Generate a random 32-bit number. - uint32_t next_u32(void) - { - return next_u64() >> 11; - } - - // Generate a number between 0 and range-1. - unsigned int next_range(unsigned int range) - { - /* - * This is not perfectly uniform, unless the range is a power of two. - * However, for 64-bit random values and 32-bit ranges, the bias is - * insignificant. - */ - return next_u64() % range; - } -}; - -#endif diff --git a/10-hash_experiment/python/hash_experiment.py b/10-hash_experiment/python/hash_experiment.py deleted file mode 100644 index 9de266ede9de09b87d209f29ff7bfca9e3ed6ec1..0000000000000000000000000000000000000000 --- a/10-hash_experiment/python/hash_experiment.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python3 - -import random, sys -from math import sqrt - -# Our wrapper of random so we can substitute it with another random generator -rng_init = lambda x: random.seed(x) -rng_next_u32 = lambda: random.randint(0, 2**32 - 1) - -class TabulationHash: - """Hash function for hashing by tabulation. - - The 32-bit key is split to four 8-bit parts. Each part indexes - a separate table of 256 randomly generated values. Obtained values - are XORed together. - """ - - def __init__(self, num_buckets): - self.num_buckets = num_buckets - self.tables = [None] * 4 - for i in range(4): - self.tables[i] = [ rng_next_u32() for _ in range(256) ] - - def __call__(self, key): - h0 = key & 0xff; - h1 = (key >> 8) & 0xff; - h2 = (key >> 16) & 0xff; - h3 = (key >> 24) & 0xff; - t = self.tables - return (t[0][h0] ^ t[1][h1] ^ t[2][h2] ^ t[3][h3]) % self.num_buckets - -class PolynomialHash: - """Hash function using polynomial modulo a prime.""" - - def __init__(self, num_buckets, degree, prime = 2147483647): - self.num_buckets = num_buckets - self.prime = prime - self.coefs = [ rng_next_u32() for _ in range(degree + 1) ] - - def __call__(self, key): - acc = 0 - for c in self.coefs: - acc = (acc * key + c) % self.prime - return acc % self.num_buckets - -LinearHash = lambda num_buckets: PolynomialHash(num_buckets, 1) -QuadraticHash = lambda num_buckets: PolynomialHash(num_buckets, 2) - -class MultiplyShiftLowHash: - """Multiply-shift hash function taking top bits of 32-bit word""" - - def __init__(self, num_buckets): - self.mask = num_buckets - 1 - assert (num_buckets & self.mask == 0), \ - "MultiplyShiftLowHash: num_buckets must be power of 2" - - self.mult = rng_next_u32() | 0x1 - self.shift = 0; - tmp = num_buckets - 1 - while 0x80000000 & tmp == 0: - tmp <<= 1 - self.shift += 1 - - def __call__(self, key): - return ((key * self.mult) >> self.shift) & self.mask - -class MultiplyShiftHighHash: - """Multiply-shift hash function taking low bits of upper half of 64-bit word""" - - def __init__(self, num_buckets): - self.mask = num_buckets - 1 - assert (num_buckets & self.mask == 0), \ - "MultiplyShiftLowHash: num_buckets must be power of 2" - self.mult = (rng_next_u32() << 32) | rng_next_u32() | 0x1 - - def __call__(self, key): - return ((key * self.mult) >> 32) & self.mask - -class HashTable: - """Hash table with linear probing""" - - def __init__(self, hash_fun_factory, num_buckets): - self._hash = hash_fun_factory(num_buckets) - self._num_buckets = num_buckets - self._table = [None] * num_buckets - self._size = 0 - self.reset_counter() - - def _next_bucket(self, b): - return (b + 1) % self._num_buckets - - def lookup(self, key): - """Check whether key is present in the table.""" - ret = False - steps = 1 - - b = self._hash(key) - while self._table[b] is not None: - if self._table[b] == key: - ret = True - break - steps += 1 - b = self._next_bucket(b) - - self._update_counter(steps) - return ret - - def insert(self, key): - """Add the key in the table.""" - assert self._size < self._num_buckets, "Cannot insert into a full table." - steps = 1 - - b = self._hash(key) - while self._table[b] is not None: - if self._table[b] == key: break - steps += 1 - b = self._next_bucket(b) - else: - self._table[b] = key - - self._update_counter(steps) - - def _update_counter(self, steps): - self._ops += 1 - self._steps += steps - self._max = max(self._max, steps) - - def reset_counter(self): - self._steps = 0 - self._ops = 0 - self._max = 0 - - def report_avg(self): return self._steps / max(1, self._ops) - def report_max(self): return self._max - -def permute_list(l): - N = len(l) - for i in range(N - 1): - dst = i + (rng_next_u32() % (N-i)) - l[i], l[dst] = l[dst], l[i] - -def usage_test(hash_fun_factory, max_usage = 90, retry = 40): - avg = [0.0] * max_usage - avg2 = [0.0] * max_usage - - N = 2**19 - step_size = N // 100 - elements = list(range(N)) - - for _ in range(retry): - H = HashTable(hash_fun_factory, N) - permute_list(elements) - - for s in range(max_usage): - H.reset_counter() - for i in range(step_size): - H.insert(s*step_size + i) - avg[s] += H.report_avg() - avg2[s] += H.report_avg() ** 2 - - for i in range(max_usage): - avg[i] /= retry; - avg2[i] /= retry; - std_dev = sqrt(avg2[i] - avg[i]**2) - - print("%i %.03f %.03f" % ((i + 1), avg[i], std_dev)) - -def grow_test(hash_fun_factory, usage = 60, retry = 40, begin = 7, end = 21): - for n in range(begin, end): - avg = 0.0 - avg2 = 0.0 - N = 2 ** n - elements = list(range(N)) - - for _ in range(retry): - H = HashTable(hash_fun_factory, N) - permute_list(elements) - - for x in elements[:N * usage // 100]: - H.insert(x) - - for i in range(N): - H.lookup(i) - - avg += H.report_avg() - avg2 += H.report_avg() ** 2 - - avg /= retry - avg2 /= retry - std_dev = sqrt(avg2 - avg**2) - - print("%i %.03f %.03f" % (N, avg, std_dev)) - -tests = { - "usage-ms-low": lambda: usage_test(MultiplyShiftLowHash), - "usage-ms-high": lambda: usage_test(MultiplyShiftHighHash), - "usage-poly-1": lambda: usage_test(LinearHash), - "usage-poly-2": lambda: usage_test(QuadraticHash), - "usage-tab": lambda: usage_test(TabulationHash), - - "grow-ms-low": lambda: grow_test(MultiplyShiftLowHash), - "grow-ms-high": lambda: grow_test(MultiplyShiftHighHash), - "grow-poly-1": lambda: grow_test(LinearHash), - "grow-poly-2": lambda: grow_test(QuadraticHash), - "grow-tab": lambda: grow_test(TabulationHash), -} - -if len(sys.argv) == 3: - test, student_id = sys.argv[1], sys.argv[2] - rng_init(int(student_id)) - if test in tests: - tests[test]() - else: - raise ValueError("Unknown test {}".format(test)) -else: - raise ValueError("Usage: {} <test> <student-id>".format(sys.argv[0])) - diff --git a/10-hash_experiment/task.md b/10-hash_experiment/task.md deleted file mode 100644 index cfc27239b0d79cc118e3fdd04dfbe9448a278ea6..0000000000000000000000000000000000000000 --- a/10-hash_experiment/task.md +++ /dev/null @@ -1,74 +0,0 @@ -## Goal - -The goal of this assignment is to experimentally evaluate Linear probing -hash table with different systems of hash functions. - -You are given a test program (`hash_experiment`) which implements everything -needed to perform the following experiments: - -- _Grow test:_ This test tries different sizes $N$ of the hash table and for each size - it inserts small keys in random order until 60% of the table is used - and then it performs lookup operation for keys $0,\ldots,N-1$. -- _Usage test:_ This test uses hash table of size $2^{20}$. It performs insertions - to increase usage of the table by 1%, reports efficiency of the insert operation, - and repeats until usage of the table reaches 90%. - -Both test measure number of probed buckets per operation, are repeated 40 times -and report average and standard deviation. Note that even with 40 repetitions -the reported numbers still depend quite a lot on the random seed used. - -You should perform these experiments for 5 different classes of hash functions – -tabulation, multiply-shift which uses top bits of 32-bit word (`ms-low`), -multiply-shift which uses low bits of upper half of 64-bit word (`ms-high`), -and polynomial hash function of degree 1 and 2 – and write a report, which contains two -plots of the measured data for each test. The first plot should contain average -complexity of operations and the second one the standard deviation. - -Each plot should show the dependence of the average number of probed buckets -either on size of the hash table (the grow test) or the usage of the hash table -(the usage test). - -The report should discuss the experimental results and try to explain the observed -behavior using theory from the lectures. (If you want, you can carry out further -experiments to gain better understanding of the data structure and include these -in the report. This is strictly optional.) - -You should submit a PDF file with the report (and no source code). -You will get 1 temporary point upon submission if the file is syntactically correct; -proper points will be assigned later. - -## Test program - -The test program is given two arguments: -- The name of the test (`{grow,usage}-{ms-low,ms-high,poly-1,poly-2,tab}`). -- The random seed: you should use the last 2 digits of your student ID (you can find - it in the Study Information System – just click on the Personal data icon). Please - include the random seed in your report. - -The output of the program contains one line per experiment, which consists of -the set size and the average number of structural changes. - -## Hints - -The following tools can be useful for producing nice plots: -- [pandas](https://pandas.pydata.org/) -- [matplotlib](https://matplotlib.org/) -- [gnuplot](http://www.gnuplot.info/) - -A quick checklist for plots: -- Is there a caption explaining what is plotted? -- Are the axes clearly labelled? Do they have value ranges and units? -- Have you mentioned that this axis has logarithmic scale? (Logarithmic graphs - are more fitting in some cases, but you should tell.) -- Is it clear which curve means what? -- Is it clear what are the measured points and what is an interpolated - curve between them? -- Are there any overlaps? (E.g., the most interesting part of the curve - hidden underneath a label?) - -In your discussion, please distinguish the following kinds of claims. -It should be always clear which is which: -- Experimental results (i.e., the raw data you obtained from the experiments) -- Theoretical facts (i.e., claims we have proved mathematically) -- Your hypotheses (e.g., when you claim that the graph looks like something is true, - but you are not able to prove rigorously that it always holds) diff --git a/11-find_duplicates/cpp/Makefile b/11-find_duplicates/cpp/Makefile deleted file mode 100644 index 015ffbfb10452ac353db0f8099dd82fab4696ec0..0000000000000000000000000000000000000000 --- a/11-find_duplicates/cpp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -test: find_duplicates_test - ./$< - -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -I$(INCLUDE) - -find_duplicates_test: find_duplicates_test.cpp find_duplicates.h test_main.cpp - $(CXX) $(CXXFLAGS) $(filter %.cpp,$^) -o $@ - -clean: - rm -f find_duplicates_test - -.PHONY: clean test diff --git a/11-find_duplicates/cpp/find_duplicates.h b/11-find_duplicates/cpp/find_duplicates.h deleted file mode 100644 index 5c94a6cfc3d176ec9d029097f26256bd53b1c0b5..0000000000000000000000000000000000000000 --- a/11-find_duplicates/cpp/find_duplicates.h +++ /dev/null @@ -1,20 +0,0 @@ -#include <unordered_map> - -vector<string> find_duplicates(DataGenerator& generator) { - /* - * Find duplicates in the given data. - * - * The `generator` provides a forward iterator over strings - * for traversing the data, so it can be iterated for example - * using a `for` cycle: - * - * for (const string& item : generator) {...} - * - * The `generator` can be traversed multiple times. - * - * The goal is to return a vector of duplicated entries, - * reporting each duplicated entry only once, in the order - * of their first occurrence in the data. - */ - return vector<string>(); -} diff --git a/11-find_duplicates/cpp/find_duplicates_test.cpp b/11-find_duplicates/cpp/find_duplicates_test.cpp deleted file mode 100644 index 8af7b582d087353f58fb2eadd72db3160ff903bb..0000000000000000000000000000000000000000 --- a/11-find_duplicates/cpp/find_duplicates_test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include <cmath> -#include <functional> -#include <iterator> -#include <iostream> -#include <string> -#include <vector> - -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); - -class DataGenerator { - public: - class Iterator : iterator<input_iterator_tag, string> { - public: - Iterator(int counter, DataGenerator* generator) { this->counter = counter; this->generator = generator; } - Iterator(const Iterator& other) { this->counter = other.counter; this->generator = other.generator; } - Iterator& operator++() { if (counter < generator->total_size) counter++; return *this; } - Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; } - bool operator==(const Iterator& other) const { return counter == other.counter; } - bool operator!=(const Iterator& other) const { return counter != other.counter; } - const string& operator*() { - data.clear(); - - int segment = counter / generator->segment_size, index = counter % generator->segment_size; - if (index + 1 == generator->segment_size) { - data.push_back('0' + generator->base - 1 - segment); - data.append(generator->suffix); - } else { - data.push_back('0' + segment); - for (int length = generator->length - 1; length; length--, index /= generator->base) - data.push_back('0' + (index % generator->base)); - } - return data; - } - - private: - DataGenerator* generator; - string data; - int counter; - }; - - inline Iterator begin() { return Iterator(0, this); } - inline Iterator end() { return Iterator(total_size, this); } - - DataGenerator(int base, int length, string suffix) { - this->base = base; - this->length = length; - this->suffix = suffix; - segment_size = powl(base, length - 1) + 1; - total_size = base * segment_size; - }; - - private: - int base, length; - int segment_size, total_size; - string suffix; -}; - -#include "find_duplicates.h" - -#ifdef __linux__ -#include <sys/time.h> -#include <sys/resource.h> -#endif - -void test_duplicates(int base, int length, string suffix) { -#ifdef __linux__ - rlimit data_limit; - data_limit.rlim_cur = data_limit.rlim_max = 64 << 20; - setrlimit(RLIMIT_DATA, &data_limit); -#endif - - DataGenerator generator(base, length, suffix); - auto results = find_duplicates(generator); - - vector<string> correct; - for (int i = 0; i < base / 2; i++) { - correct.push_back(string(1, '0' + i) + suffix); - correct.push_back(string(1, '0' + base - 1 - i) + suffix); - } - - EXPECT(results.size() == correct.size(), - "Wrong number of generated duplicates, got " + to_string(results.size()) + - " and expected " + to_string(correct.size())); - for (int i = 0; i < int(results.size()); i++) - EXPECT(results[i] == correct[i], - "Wrong generated duplicate, got " + results[i] + " and expected " + correct[i]); -} - -vector<pair<string, function<void()>>> tests = { - {"10k", [] { test_duplicates(10, 4, "101"); }}, - {"100k", [] { test_duplicates(10, 5, "1984"); }}, - {"1M", [] { test_duplicates(10, 6, "22222"); }}, - {"10M", [] { test_duplicates(10, 7, "314159"); }}, - {"16M", [] { test_duplicates(8, 8, "7654321"); }}, -}; diff --git a/11-find_duplicates/cpp/test_main.cpp b/11-find_duplicates/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/11-find_duplicates/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/11-find_duplicates/python/find_duplicates.py b/11-find_duplicates/python/find_duplicates.py deleted file mode 100644 index e176dbe38c61921a1c89bcf344ec647967647790..0000000000000000000000000000000000000000 --- a/11-find_duplicates/python/find_duplicates.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -import sys - -def find_duplicates(data_generator): - """Find duplicates in the given data. - - The `data_generator` is an iterable over strings, so it can be - iterated for example using a `for` cycle: - - for item in data_generator: ... - - It can be iterated multiple times. - - The goal is to return a list of duplicated entries, reporting each duplicated - entry only once, in the order of their first occurrence in the data. - """ - - raise NotImplementedError() diff --git a/11-find_duplicates/python/find_duplicates_test.py b/11-find_duplicates/python/find_duplicates_test.py deleted file mode 100644 index 0b08916cee4370fba447c48372fc549c5629c693..0000000000000000000000000000000000000000 --- a/11-find_duplicates/python/find_duplicates_test.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -import gc -import itertools -import sys - -from find_duplicates import find_duplicates - -class DataGenerator(): - def __init__(self, base, length, suffix): - self.digits = "0123456789"[:base] - self.length = length - self.suffix = suffix - - def generator(self): - for first in range(len(self.digits)): - for sequence in itertools.product(self.digits[first], *[self.digits] * (self.length - 1)): - yield "".join(sequence) - yield self.digits[-1 - first] + self.suffix - - def __iter__(self): - return self.generator() - -def test_duplicates(base, length, suffix): - generator = DataGenerator(base, length, suffix) - results = find_duplicates(generator) - gc.collect() - - prefixes = [generator.digits[i] for o in range(0, base // 2) for i in [o, -1 - o]] - correct = [prefix + suffix for prefix in prefixes] - assert results == correct, "The generates list of duplicates is not correct, got {} and expected {}".format(results, correct) - -tests = [ - ("10k", lambda: test_duplicates(10, 4, "101")), - ("100k", lambda: test_duplicates(10, 5, "1984")), - ("1M", lambda: test_duplicates(10, 6, "22222")), - ("10M", lambda: test_duplicates(10, 7, "314159")), - ("16M", lambda: test_duplicates(8, 8, "7654321")), -] - -if __name__ == "__main__": - try: - import resource - resource.setrlimit(resource.RLIMIT_DATA, (64<<20, 64<<20)) - except: - pass - - 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)) diff --git a/11-find_duplicates/task.md b/11-find_duplicates/task.md deleted file mode 100644 index ad076c4cfd2ed97b396ce6f48814050fee8bf1ad..0000000000000000000000000000000000000000 --- a/11-find_duplicates/task.md +++ /dev/null @@ -1,18 +0,0 @@ -In this assignment, you are given a large file on input. Your goal is to find -duplicated lines and return every duplicated line once, in the order of their -first occurrences in the file. - -The challenging part of this assignment is the fact, that your program has to -run in a limited memory, using at most `64MB`, and the input file can be -considerably larger than this memory limit. However, you can rely on the fact -that the number of duplicated lines is considerably smaller (so that all -duplicated lines fit in the memory at the same time). - -Instead of handling a real file, you are given a data generator (an `iterator` -in C++ and a `generator` in Python). Note that limiting memory during the -tests works only on Linux (and not on Windows), and of course also in ReCodEx. - -You can use full standard library of Python and C++ in this assignment, -including data structure implementations (also, `bytearray` might come handy). -Note that the largest test in Python can run for several minutes. -As usual, you should submit only the `find_duplicates.{h,py}` file. diff --git a/12-range_tree/cpp/Makefile b/12-range_tree/cpp/Makefile deleted file mode 100644 index 38c4dcd29a404a7b537edd674123d25ad692b75b..0000000000000000000000000000000000000000 --- a/12-range_tree/cpp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -test: range_tree_test - ./$< - -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -I$(INCLUDE) - -range_tree_test: range_tree_test.cpp range_tree.h test_main.cpp - $(CXX) $(CXXFLAGS) $(filter %.cpp,$^) -o $@ - -clean: - rm -f range_tree_test - -.PHONY: clean test diff --git a/12-range_tree/cpp/range_tree.h b/12-range_tree/cpp/range_tree.h deleted file mode 100644 index a5ca59475a82ca15e714bb4bbfd91c5aa20280e0..0000000000000000000000000000000000000000 --- a/12-range_tree/cpp/range_tree.h +++ /dev/null @@ -1,177 +0,0 @@ -#include <cstdint> -#include <limits> - -// A node of the tree -class Node { - public: - int64_t key; - int64_t value; - Node* left; - Node* right; - Node* parent; - - // Constructor - Node(int64_t key, int64_t value, Node* parent=nullptr, Node* left=nullptr, Node* right=nullptr) { - this->key = key; - this->value = value; - this->parent = parent; - this->left = left; - this->right = right; - if (left) left->parent = this; - if (right) right->parent = this; - } - -}; - -// Splay tree -class Tree { - public: - // Pointer to root of the tree; nullptr if the tree is empty. - Node* root; - - Tree(Node* root=nullptr) { - this->root = root; - } - - // Rotate the given `node` up. Perform a single rotation of the edge - // between the node and its parent, choosing left or right rotation - // appropriately. - virtual void rotate(Node* node) { - if (node->parent) { - if (node->parent->left == node) { - if (node->right) node->right->parent = node->parent; - node->parent->left = node->right; - node->right = node->parent; - } else { - if (node->left) node->left->parent = node->parent; - node->parent->right = node->left; - node->left = node->parent; - } - if (node->parent->parent) { - if (node->parent->parent->left == node->parent) - node->parent->parent->left = node; - else - node->parent->parent->right = node; - } else { - root = node; - } - - Node* original_parent = node->parent; - node->parent = node->parent->parent; - original_parent->parent = node; - } - } - - // Splay the given node. - virtual void splay(Node* node) { - while (node->parent && node->parent->parent) { - if ((node->parent->right == node && node->parent->parent->right == node->parent) || - (node->parent->left == node && node->parent->parent->left == node->parent)) { - rotate(node->parent); - rotate(node); - } else { - rotate(node); - rotate(node); - } - } - if (node->parent) - rotate(node); - } - - // Look up the given key in the tree, returning the - // the node with the requested key or nullptr. - Node* lookup(int64_t key) { - Node* node = root; - Node* node_last = nullptr; - while (node) { - node_last = node; - if (node->key == key) - break; - if (key < node->key) - node = node->left; - else - node = node->right; - } - if (node_last) - splay(node_last); - return node; - } - - // Insert a (key, value) into the tree. - // If the key is already present, nothing happens. - void insert(int64_t key, int64_t value) { - if (!root) { - root = new Node(key, value); - return; - } - - Node* node = root; - while (node->key != key) { - if (key < node->key) { - if (!node->left) - node->left = new Node(key, value, node); - node = node->left; - } else { - if (!node->right) - node->right = new Node(key, value, node); - node = node->right; - } - } - splay(node); - } - - // Delete given key from the tree. - // It the key is not present, do nothing. - // - // The implementation first splays the element to be removed to - // the root, and if it has both children, splays the largest element - // in the left subtree and links it to the original right subtree. - void remove(int64_t key) { - if (lookup(key)) { - Node* right = root->right; - root = root->left; - if (!root) { - root = right; - right = nullptr; - } - if (root) - root->parent = nullptr; - - if (right) { - Node* node = root; - while (node->right) - node = node->right; - splay(node); - root->right = right; - right->parent = root; - } - } - } - - // Return the sum of elements with keys in range [left, right]. - // - // Given a closed range [left, right], return the sum of values of elements - // in the range, i.e., sum(value | (key, value) in tree, left <= key <= right). - int64_t range_sum(int64_t left, int64_t right) { - // TODO: Implement - } - - // Destructor to free all allocated memory. - ~Tree() { - Node* node = root; - while (node) { - Node* next; - if (node->left) { - next = node->left; - node->left = nullptr; - } else if (node->right) { - next = node->right; - node->right = nullptr; - } else { - next = node->parent; - delete node; - } - node = next; - } - } -}; diff --git a/12-range_tree/cpp/range_tree_test.cpp b/12-range_tree/cpp/range_tree_test.cpp deleted file mode 100644 index 95d016921de8e492acf0bdcf7dcd2cd9c5d70549..0000000000000000000000000000000000000000 --- a/12-range_tree/cpp/range_tree_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include <algorithm> -#include <functional> -#include <string> -#include <utility> -#include <vector> - -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); - -#include "range_tree.h" - -void create_test_tree(int64_t size, bool ascending, Tree& tree) { - vector<int64_t> sequence = {7}; - for (int64_t i = 2; i < size; i++) - sequence.push_back((sequence.back() * sequence.front()) % size); - if (ascending) - sort(sequence.begin(), sequence.end()); - - for (int64_t element : sequence) - tree.insert(element, element); -} - -void test_missing(int64_t size, bool ascending) { - Tree tree; - create_test_tree(size, ascending, tree); - - int64_t values = 0; - for (int64_t i = 0; i < size; i++) - values += tree.range_sum(-size, 0) + tree.range_sum(size, 2 * size); - EXPECT(values == 0, "Expected no values in an empty range"); -} - -void test_suffixes(int64_t size, bool ascending) { - Tree tree; - create_test_tree(size, ascending, tree); - - for (int64_t left = 1; left < size; left++) { - int64_t values = tree.range_sum(left, size - 1); - int64_t expected = size * (size - 1) / 2 - left * (left - 1) / 2; - EXPECT(values == expected, - "Expected " + to_string(expected) + " for range [" + to_string(left) + - ", " + to_string(size - 1) + "], got " + to_string(values)); - } -} - -void test_updates(int64_t size, bool ascending) { - Tree tree; - create_test_tree(size, ascending, tree); - - for (int64_t left = 1; left < size; left++) { - tree.remove(left); - tree.insert(left + size - 1, left + size - 1); - int64_t values = tree.range_sum(left + 1, size + left); - int64_t expected = (size + left) * (size + left - 1) / 2 - (left + 1) * left / 2; - EXPECT(values == expected, - "Expected " + to_string(expected) + " for range [" + to_string(left + 1) + - ", " + to_string(size + left) + "], got " + to_string(values)); - } -} - - -vector<pair<string, function<void()>>> tests = { - {"random_missing", [] { test_missing(13, false); }}, - {"random_suffixes", [] { test_suffixes(13, false); }}, - {"random_updates", [] { test_updates(13, false); }}, - - {"path_missing", [] { test_missing(13, true); }}, - {"path_suffixes", [] { test_suffixes(13, true); }}, - {"path_updates", [] { test_updates(13, true); }}, - - {"random_missing_big", [] { test_missing(199999, false); }}, - {"random_suffixes_big", [] { test_suffixes(199999, false); }}, - {"random_updates_big", [] { test_updates(199999, false); }}, - - {"path_missing_big", [] { test_missing(199999, true); }}, - {"path_suffixes_big", [] { test_suffixes(199999, true); }}, - {"path_updates_big", [] { test_updates(199999, true); }}, -}; diff --git a/12-range_tree/cpp/test_main.cpp b/12-range_tree/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/12-range_tree/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/12-range_tree/python/range_tree.py b/12-range_tree/python/range_tree.py deleted file mode 100644 index 3288ca977f3849f96dc28e6c8e17f6fa9bad69b2..0000000000000000000000000000000000000000 --- a/12-range_tree/python/range_tree.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 -import math - -class Node: - """Node in a binary tree `Tree`""" - - def __init__(self, key, value, left=None, right=None, parent=None): - self.key = key - self.value = value - self.parent = parent - self.left = left - self.right = right - if left is not None: left.parent = self - if right is not None: right.parent = self - -class Tree: - """A splay tree implementation""" - - def __init__(self, root=None): - self.root = root - - def rotate(self, node): - """ Rotate the given `node` up. - - Performs a single rotation of the edge between the given node - and its parent, choosing left or right rotation appropriately. - """ - if node.parent is not None: - if node.parent.left == node: - if node.right is not None: node.right.parent = node.parent - node.parent.left = node.right - node.right = node.parent - else: - if node.left is not None: node.left.parent = node.parent - node.parent.right = node.left - node.left = node.parent - if node.parent.parent is not None: - if node.parent.parent.left == node.parent: - node.parent.parent.left = node - else: - node.parent.parent.right = node - else: - self.root = node - node.parent.parent, node.parent = node, node.parent.parent - - def splay(self, node): - """Splay the given node""" - while node.parent is not None and node.parent.parent is not None: - if (node.parent.right == node and node.parent.parent.right == node.parent) or \ - (node.parent.left == node and node.parent.parent.left == node.parent): - self.rotate(node.parent) - self.rotate(node) - else: - self.rotate(node) - self.rotate(node) - if node.parent is not None: - self.rotate(node) - - def lookup(self, key): - """Look up the given key in the tree. - - Returns the node with the requested key or `None`. - """ - node, node_last = self.root, None - while node is not None: - node_last = node - if node.key == key: - break - if key < node.key: - node = node.left - else: - node = node.right - if node_last is not None: - self.splay(node_last) - return node - - def insert(self, key, value): - """Insert (key, value) into the tree. - - If the key is already present, do nothing. - """ - if self.root is None: - self.root = Node(key, value) - return - - node = self.root - while node.key != key: - if key < node.key: - if node.left is None: - node.left = Node(key, value, parent=node) - node = node.left - else: - if node.right is None: - node.right = Node(key, value, parent=node) - node = node.right - self.splay(node) - - def remove(self, key): - """Remove given key from the tree. - - It the key is not present, do nothing. - - The implementation first splays the element to be removed to - the root, and if it has both children, splays the largest element - in the left subtree and links it to the original right subtree. - """ - if self.lookup(key) is not None: - right = self.root.right - self.root = self.root.left - if self.root is None: - self.root, right = right, None - if self.root is not None: - self.root.parent = None - - if right is not None: - node = self.root - while node.right is not None: - node = node.right - self.splay(node) - self.root.right = right - right.parent = self.root - - def range_sum(self, left, right): - """Return the sum of elements with keys in range [left, right] - - Given a closed range [left, right], return the sum of values of elements - in the range, i.e., sum(value | (key, value) in tree, left <= key <= right). - """ - raise NotImplementedError() diff --git a/12-range_tree/python/range_tree_test.py b/12-range_tree/python/range_tree_test.py deleted file mode 100644 index bc866e16df3de9d97c9c12ce9a0d4a0291611d6f..0000000000000000000000000000000000000000 --- a/12-range_tree/python/range_tree_test.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -import sys - -from range_tree import Tree - -def test_tree(size, ascending): - sequence = [pow(7, i, size) for i in range(1, size)] - if ascending: sequence = sorted(sequence) - - tree = Tree() - for element in sequence: - tree.insert(element, element) - return tree - -def test_missing(size, ascending): - tree = test_tree(size, ascending) - - values = 0 - for _ in range(size): - values += tree.range_sum(-size, 0) - values += tree.range_sum(size, 2 * size) - assert values == 0, "Expected no values in an empty range" - -def test_suffixes(size, ascending): - tree = test_tree(size, ascending) - - for left in range(1, size): - values = tree.range_sum(left, size - 1) - expected = size * (size - 1) // 2 - left * (left - 1) // 2 - assert values == expected, "Expected {} for range [{}, {}], got {}".format(expected, left, size - 1, values) - -def test_updates(size, ascending): - tree = test_tree(size, ascending) - - for left in range(1, size): - tree.remove(left) - tree.insert(left + size - 1, left + size - 1) - values = tree.range_sum(left + 1, size + left) - expected = (size + left) * (size + left - 1) // 2 - (left + 1) * left // 2 - assert values == expected, "Expected {} for range [{}, {}], got {}".format(expected, left + 1, size + left, values) - -tests = [ - ("random_missing", lambda: test_missing(13, False)), - ("random_suffixes", lambda: test_suffixes(13, False)), - ("random_updates", lambda: test_updates(13, False)), - - ("path_missing", lambda: test_missing(13, True)), - ("path_suffixes", lambda: test_suffixes(13, True)), - ("path_updates", lambda: test_updates(13, True)), - - ("random_missing_big", lambda: test_missing(19997, False)), - ("random_suffixes_big", lambda: test_suffixes(19997, False)), - ("random_updates_big", lambda: test_updates(19997, False)), - - ("path_missing_big", lambda: test_missing(19997, True)), - ("path_suffixes_big", lambda: test_suffixes(19997, True)), - ("path_updates_big", lambda: test_updates(19997, True)), -] - -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)) diff --git a/12-range_tree/task.md b/12-range_tree/task.md deleted file mode 100644 index 24206ed0ff39398611439e0a1aadef869f5555fc..0000000000000000000000000000000000000000 --- a/12-range_tree/task.md +++ /dev/null @@ -1,22 +0,0 @@ -You are given an implementation of a Splay tree which associates every numeric -key with a numeric value. The Splay tree provides `lookup`, `insert`, and `remove` -operations. - -Your goal is to modify the Splay tree to support range queries in amortized -logarithmic time. The operation you need to implement takes a range of the -keys and it should return the sum of values of the elements in the given range. - -As usual, you should submit only the `range_tree.{h,py}` file. - -## Optional: Range updates (for extra 5 points) - -If you also implement an operation -``` -range_update(left, right, delta) -``` -which adds `delta` to the value of all elements with key in `[left, right]` range -and runs in amortized logarithmic time, you will get 5 points. - -Currently there are no automated tests for this method; therefore, if you -implement it, submit the solution to ReCodEx and write an email to your -teaching assistant. diff --git a/13-kgrams/cpp/Makefile b/13-kgrams/cpp/Makefile deleted file mode 100644 index 7046027a2bf4c6dcd8902493bb9e24568db1efd0..0000000000000000000000000000000000000000 --- a/13-kgrams/cpp/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -test: kgrams_test - ./$< - -INCLUDE ?= . -CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare -I$(INCLUDE) - -kgrams_test: kgrams_test.cpp kgrams.h test_main.cpp - $(CXX) $(CXXFLAGS) $(filter %.cpp,$^) -o $@ - -clean: - rm -f kgrams_test - -.PHONY: clean test diff --git a/13-kgrams/cpp/kgrams.h b/13-kgrams/cpp/kgrams.h deleted file mode 100644 index bdcd4e8e7f744663a1210ea2a0be4c9a5f4d3894..0000000000000000000000000000000000000000 --- a/13-kgrams/cpp/kgrams.h +++ /dev/null @@ -1,59 +0,0 @@ -#include <vector> -#include <iostream> -#include <algorithm> -#include <string> -#include <functional> - -using namespace std; - -class SuffixArray { - public: - string text; - int n; // Length of text - vector<int> S; // Permutation which sorts suffixes - vector<int> R; // Ranking array (an inverse of S) - - // Construct suffix array and ranking array for the given string - // using the doubling algorithm. - SuffixArray(const string &orig_text) - { - text = orig_text; - n = text.size(); - S.resize(n+1); - R.resize(n+1); - - sort_and_rank([this](int a, int b) -> bool { return text[a] < text[b]; }); - - for (int k=1; k<n; k*=2) { - sort_and_rank([this,k](int a, int b) -> bool { - pair<int,int> pa(R[a], (a+k < n) ? R[a+k] : -1); - pair<int,int> pb(R[b], (b+k < n) ? R[b+k] : -1); - return (pa < pb); - }); - } - } - - // An auxiliary function used in the doubling algorithm. - void sort_and_rank(function<bool(int a, int b)> comp) - { - for (size_t i=0; i<S.size(); i++) - S[i] = i; - - sort(S.begin(), S.end(), comp); - - vector<int> R2(S.size()); - for (size_t i=0; i<S.size(); i++) { - if (!i || comp(S[i-1], S[i]) || comp(S[i], S[i-1])) - R2[S[i]] = i; - else - R2[S[i]] = R2[S[i-1]]; - } - R.swap(R2); - } - - // Return the number of distinct k-grams in the string. - int num_kgrams(int k) - { - // TODO: Implement - } -}; diff --git a/13-kgrams/cpp/kgrams_test.cpp b/13-kgrams/cpp/kgrams_test.cpp deleted file mode 100644 index bb90a324bbbb399eecec76b9a385e4809efdacff..0000000000000000000000000000000000000000 --- a/13-kgrams/cpp/kgrams_test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include <algorithm> -#include <functional> -#include <string> -#include <vector> -#include <cstdint> - -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); - -#include "kgrams.h" - -void test_generic(const string& text, int k, int expected_kg) -{ - SuffixArray sa(text); - int num_kg = sa.num_kgrams(k); - EXPECT(num_kg == expected_kg, "Expected " + to_string(expected_kg) + " " + to_string(k) + "-grams, found " + to_string(num_kg) + "."); - -} - -// Test on a fixed string. -void test_explicit(int k, int expected_kg) -{ - test_generic("annbansbananas", k, expected_kg); -} - -// Test on a very non-uniform random string. -void test_random(int n, int k, int expected_kg) -{ - string s(n, ' '); - uint32_t state = n; - - for (int i=0; i<n; i++) { - state = state*2654289733 + 7; - unsigned x = (state >> 28) % 16; - char next = "aaaaaaaaaaaabbbc"[x]; - s[i] = next; - } - - test_generic(s, k, expected_kg); -} - -// Test on an almost-constant string. -void test_trivial(int n, int k, int expected_kg) -{ - string s(n, ' '); - - for (int i=0; i<n; i++) { - if (i == n/2) - s[i] = 'b'; - else - s[i] = 'a'; - } - - test_generic(s, k, expected_kg); -} - -vector<pair<string, function<void()>>> tests = { - {"basic-1", [] { test_explicit(1, 4); }}, - {"basic-2", [] { test_explicit(2, 8); }}, - {"basic-3", [] { test_explicit(3, 10); }}, - {"basic-4", [] { test_explicit(4, 11); }}, - {"basic-14", [] { test_explicit(14, 1); }}, - - {"short-5", [] { test_random(1000, 5, 107); }}, - {"short-33", [] { test_random(1000, 33, 968); }}, - {"short-500", [] { test_random(1000, 500, 501); }}, - - {"long-5", [] { test_random(100000, 5, 230); }}, - {"long-33", [] { test_random(100000, 33, 99767); }}, - {"long-5000", [] { test_random(100000, 5000, 95001); }}, - - {"triv-1", [] { test_trivial(1000000, 1, 2); }}, - {"triv-5", [] { test_trivial(1000000, 5, 6); }}, - {"triv-3333", [] { test_trivial(1000000, 3333, 3334); }}, - {"triv-500000", [] { test_trivial(1000000, 500000, 500001); }}, -}; diff --git a/13-kgrams/cpp/test_main.cpp b/13-kgrams/cpp/test_main.cpp deleted file mode 100644 index 3f4aff0785f636b7fd0ea1a15aa69dafe06f290f..0000000000000000000000000000000000000000 --- a/13-kgrams/cpp/test_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include <cstdlib> -#include <functional> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -using namespace std; - -extern vector<pair<string, function<void()>>> tests; - -void expect_failed(const string& message) { - cerr << "Test error: " << message << endl; - exit(1); -} - -int main(int argc, char* argv[]) { - vector<string> required_tests; - - if (argc > 1) { - required_tests.assign(argv + 1, argv + argc); - } else { - for (const auto& test : tests) - required_tests.push_back(test.first); - } - - for (const auto& required_test : required_tests) { - bool found = false; - for (const auto& test : tests) - if (required_test == test.first) { - cerr << "Running test " << required_test << endl; - test.second(); - found = true; - break; - } - if (!found) { - cerr << "Unknown test " << required_test << endl; - return 1; - } - } - - return 0; -} diff --git a/13-kgrams/python/kgrams.py b/13-kgrams/python/kgrams.py deleted file mode 100644 index 0eefda33b44dbd2690c511c7a8e8c3d59bea8489..0000000000000000000000000000000000000000 --- a/13-kgrams/python/kgrams.py +++ /dev/null @@ -1,43 +0,0 @@ -class SuffixArray: - def __init__(self, text): - self.text = text - # S is the suffix array (a permutation which sorts suffixes) - # R is the ranking array (the inverse of S) - self.R, self.S = self._build_suffix_array(text) - - def _build_suffix_array(self, text): - """ - Construct the suffix array and ranking array for the given string - using the doubling algorithm. - """ - - n = len(text) - R = [None] * (n+1) - S = [None] * (n+1) - - R = self._sort_and_rank(S, lambda a: ord(text[a]) if a < len(text) else -1) - - k = 1 - while k < n: - R = self._sort_and_rank(S, lambda a: (R[a], (R[a+k] if a+k < n else -1))) - k *= 2 - - return (tuple(R), tuple(S)) - - # An auxiliary function used in the doubling algorithm. - def _sort_and_rank(self, S, key): - for i in range(len(S)): S[i] = i - S.sort(key = key) - - R = [None] * len(S) - for i, s in enumerate(S): - prev_s = S[i-1] - if i == 0 or key(prev_s) != key(s): R[s] = i - else: R[s] = R[prev_s] - return R - - def num_kgrams(self, k): - """Return the number of distinct k-grams in the string.""" - - raise NotImplementedError - diff --git a/13-kgrams/python/kgrams_test.py b/13-kgrams/python/kgrams_test.py deleted file mode 100644 index 18646f44c845e9c2f07f1d5b81dda99bc6eacec9..0000000000000000000000000000000000000000 --- a/13-kgrams/python/kgrams_test.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python - -import sys -from kgrams import SuffixArray - -def test_generic(text, k, expected_count): - sa = SuffixArray(text) - count = sa.num_kgrams(k) - assert count == expected_count, \ - "Expected %i %i-grams, found %i." % (expected_count, k, count) - -def test_explicit(k, expected_count): - test_generic("annbansbananas", k, expected_count) - -def test_random(n, k, expected_count): - b = bytearray(n) - state = n - for i in range(n): - state = (state*2654289733 + 7) % (1 << 32) - x = (state >> 28) % 16 - next = "aaaaaaaaaaaabbbc"[x] - b[i] = ord(next) - - test_generic(b.decode(), k, expected_count) - -def test_trivial(n, k, expected_count): - s = "".join( "b" if i == n // 2 else "a" for i in range (n) ) - test_generic(s, k, expected_count) - -tests = [ - ("basic-1", lambda: test_explicit(1, 4)), - ("basic-2", lambda: test_explicit(2, 8)), - ("basic-3", lambda: test_explicit(3, 10)), - ("basic-4", lambda: test_explicit(4, 11)), - ("basic-14", lambda: test_explicit(14, 1)), - - ("short-5", lambda: test_random(1000, 5, 107)), - ("short-33", lambda: test_random(1000, 33, 968)), - ("short-500", lambda: test_random(1000, 500, 501)), - - ("long-5", lambda: test_random(100000, 5, 230)), - ("long-33", lambda: test_random(100000, 33, 99767)), - ("long-5000", lambda: test_random(100000, 5000, 95001)), - - ("triv-1", lambda: test_trivial(1000000, 1, 2)), - ("triv-5", lambda: test_trivial(1000000, 5, 6)), - ("triv-3333", lambda: test_trivial(1000000, 3333, 3334)), - ("triv-500000", lambda: test_trivial(1000000, 500000, 500001)), -] - -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)) - diff --git a/13-kgrams/task.md b/13-kgrams/task.md deleted file mode 100644 index 7788c4386d2c6a55db47fa7836cf7a4577ec16d8..0000000000000000000000000000000000000000 --- a/13-kgrams/task.md +++ /dev/null @@ -1,8 +0,0 @@ -Your task is to write a function which takes a string and an integer K -and it reports how many different K-grams (K-character substrings) the -string has. - -You are given an algorithm for construction of the suffix array. For -simplicity, this algorithm has time complexity $O(n \log^2 n)$. Except -for constructing the suffix array, your algorithm should run in linear -time.