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.