diff --git a/11-range_tree/cpp/Makefile b/11-range_tree/cpp/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..38c4dcd29a404a7b537edd674123d25ad692b75b
--- /dev/null
+++ b/11-range_tree/cpp/Makefile
@@ -0,0 +1,13 @@
+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/11-range_tree/cpp/range_tree.h b/11-range_tree/cpp/range_tree.h
new file mode 100644
index 0000000000000000000000000000000000000000..a5ca59475a82ca15e714bb4bbfd91c5aa20280e0
--- /dev/null
+++ b/11-range_tree/cpp/range_tree.h
@@ -0,0 +1,177 @@
+#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/11-range_tree/cpp/range_tree_test.cpp b/11-range_tree/cpp/range_tree_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..95d016921de8e492acf0bdcf7dcd2cd9c5d70549
--- /dev/null
+++ b/11-range_tree/cpp/range_tree_test.cpp
@@ -0,0 +1,81 @@
+#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/11-range_tree/cpp/test_main.cpp b/11-range_tree/cpp/test_main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3f4aff0785f636b7fd0ea1a15aa69dafe06f290f
--- /dev/null
+++ b/11-range_tree/cpp/test_main.cpp
@@ -0,0 +1,43 @@
+#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-range_tree/python/range_tree.py b/11-range_tree/python/range_tree.py
new file mode 100644
index 0000000000000000000000000000000000000000..3288ca977f3849f96dc28e6c8e17f6fa9bad69b2
--- /dev/null
+++ b/11-range_tree/python/range_tree.py
@@ -0,0 +1,129 @@
+#!/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/11-range_tree/python/range_tree_test.py b/11-range_tree/python/range_tree_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc866e16df3de9d97c9c12ce9a0d4a0291611d6f
--- /dev/null
+++ b/11-range_tree/python/range_tree_test.py
@@ -0,0 +1,68 @@
+#!/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/11-range_tree/task.md b/11-range_tree/task.md
new file mode 100644
index 0000000000000000000000000000000000000000..6584a8c1bbb03b71cec05a44ce2630f3806c2413
--- /dev/null
+++ b/11-range_tree/task.md
@@ -0,0 +1,24 @@
+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.
+
+Source code templates can be found in [git](https://gitlab.kam.mff.cuni.cz/datovky/assignments/-/tree/master).