diff --git a/.gitignore b/.gitignore
index bd3609c..d4ead18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,8 @@
-.idea
+__pycache__
.coursecreator
+*.pyc
+*.DS_Store
+*_windows
+venv/
+.idea/
+*-remote-info.yaml
diff --git a/L01/Welcome/main.py b/L01/Welcome/main.py
new file mode 100644
index 0000000..0a9300d
--- /dev/null
+++ b/L01/Welcome/main.py
@@ -0,0 +1,3 @@
+if __name__ == "__main__":
+ # Write your solution here
+ pass
diff --git a/L01/Welcome/task-info.yaml b/L01/Welcome/task-info.yaml
new file mode 100644
index 0000000..2d9fd35
--- /dev/null
+++ b/L01/Welcome/task-info.yaml
@@ -0,0 +1,4 @@
+type: theory
+files:
+ - name: main.py
+ visible: true
diff --git a/L01/Welcome/task.md b/L01/Welcome/task.md
new file mode 100644
index 0000000..0c7d4ba
--- /dev/null
+++ b/L01/Welcome/task.md
@@ -0,0 +1,41 @@
+This course contains programming assignments for our Algorithmic Toolbox course on Coursera,
+which is part of the Data Structures and Algorithms specialization.
+Here you will find the tasks used to study core algorithmic ideas and develop practical problem-solving skills in Python.
+We encourage you to explore this material and learn it step by step, just as thousands of talented learners around the world have done.
+
+A particular goal of this framework is to help you to learn how to
+write efficient, reliable, and compact Python code. The PyCharm IDE
+will make the learning process smooth and enjoyable: it will help
+you with testing and debugging as well as structuring and formatting your code.
+
+The assumed pipeline is the following.
+For each programming challenge in this course:
+
+
Design an algorithm, prove that it is correct and has the expected asymptotic behavior.
+
Implement it in Python here, in PyCharm.
+
Implement unit tests and stress tests for your solution. If a bug is found, fix it and test again.
+
By pressing the play button on the right pane, check your solution against a few test cases.
+
Finally, make sure that your implementation works correctly and that all tests pass.
+ When you are ready, move on to the next programming challenge on Coursera.
+
+
+Enjoy!
+
+### Getting Started
+
+1. We assume that you are already familiar
+with basic Python programming. To refresh
+your Python skills, you may want to take an
+Introductory Python course here
+(File -> Learn and Teach -> Browse Courses).
+
+2. To get familiar with PyCharm,
+take a look at the
+Start Guide.
+
+3. The companion MOOCBook contains
+detailed statements of all 30 programming challenges
+from this course as well as good programming practices
+and solutions for selected problems.
+
+Now that you know how this course is organized and which tools you will use, let’s move on to the first programming challenge.
\ No newline at end of file
diff --git a/L01/lesson-info.yaml b/L01/lesson-info.yaml
new file mode 100644
index 0000000..6b45e1c
--- /dev/null
+++ b/L01/lesson-info.yaml
@@ -0,0 +1,3 @@
+custom_name: Introduction
+content:
+ - Welcome
diff --git a/L02/T01/images/check.gif b/L02/T01/images/check.gif
new file mode 100644
index 0000000..d46fe51
Binary files /dev/null and b/L02/T01/images/check.gif differ
diff --git a/L02/T01/images/check_dark.gif b/L02/T01/images/check_dark.gif
new file mode 100644
index 0000000..9987c9c
Binary files /dev/null and b/L02/T01/images/check_dark.gif differ
diff --git a/L02/T01/logo.png b/L02/T01/logo.png
new file mode 100644
index 0000000..47451a0
Binary files /dev/null and b/L02/T01/logo.png differ
diff --git a/L02/T01/sum_of_two_digits.py b/L02/T01/sum_of_two_digits.py
new file mode 100644
index 0000000..fff09c3
--- /dev/null
+++ b/L02/T01/sum_of_two_digits.py
@@ -0,0 +1,8 @@
+def sum_of_two_digits(first_digit, second_digit):
+ assert 0 <= first_digit <= 9 and 0 <= second_digit <= 9
+ return first_digit + second_digit
+
+
+if __name__ == '__main__':
+ a, b = map(int, input().split())
+ print(sum_of_two_digits(a, b))
diff --git a/L02/T01/task-info.yaml b/L02/T01/task-info.yaml
new file mode 100644
index 0000000..8471e34
--- /dev/null
+++ b/L02/T01/task-info.yaml
@@ -0,0 +1,23 @@
+type: edu
+custom_name: Sum of Two Digits
+files:
+ - name: sum_of_two_digits.py
+ visible: true
+ placeholders:
+ - offset: 121
+ length: 26
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/sum_of_two_digits_unit_tests.py
+ visible: true
+ - name: tests/tests.py
+ visible: false
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: images/check.gif
+ visible: false
+ is_binary: true
+ - name: images/check_dark.gif
+ visible: false
+ is_binary: true
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/sLjtY/programming-assignment-1-sum-of-two-digits/discussions
diff --git a/L02/T01/task.md b/L02/T01/task.md
new file mode 100644
index 0000000..8e936d3
--- /dev/null
+++ b/L02/T01/task.md
@@ -0,0 +1,38 @@
+# Programming Challenge: Sum of Two Digits
+
+
+
+## Implementing a Solution
+Implement the `compute_sum` function that takes two digits (that is,
+integers in the range from 0 to 9) and returns the sum of these two digits.
+
+We start from this ridiculously simple problem to show you the
+pipeline of designing an algorithm,
+implementing it, testing and debugging your program.
+
+For this trivial problem, we will skip “Designing an algorithm” and "Implementing"
+steps and will move to testing.
+
+## Testing
+Switch to the file `sum_of_two_digits_unit_tests.py`.
+It contains unit tests for your implementation. If
+you haven't heard about unit testing before, we
+encourage you to read about it in
+this official
+doc article
+or to take a short course here, in PyCharm
+(through File -> Learn and Teach -> Browse Courses).
+
+For this starting programming challenge, we've already
+implemented everything for you. Press the blue `Check`
+button at the bottom of the theory section, to run the
+tests and make sure that all tests pass successfully.
+
+
+
+In this challenge, the space of all possible inputs
+is so small that it is possible to check just all possible
+test cases: the `test_all_inputs` method goes through all
+pairs $0 \le a, b \le 9$.
+
+
Just type "first_digit + second_digit"
diff --git a/L02/T01/tests/sum_of_two_digits_unit_tests.py b/L02/T01/tests/sum_of_two_digits_unit_tests.py
new file mode 100644
index 0000000..9209153
--- /dev/null
+++ b/L02/T01/tests/sum_of_two_digits_unit_tests.py
@@ -0,0 +1,13 @@
+import unittest
+from itertools import product
+from L02.T01.sum_of_two_digits import sum_of_two_digits
+
+
+class TestSumOfTwoDigits(unittest.TestCase):
+ def test_all_inputs(self):
+ for first_digit, second_digit in product(range(10), repeat=2):
+ self.assertEqual(sum_of_two_digits(first_digit, second_digit), first_digit + second_digit)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T01/tests/tests.py b/L02/T01/tests/tests.py
new file mode 100644
index 0000000..9a0741b
--- /dev/null
+++ b/L02/T01/tests/tests.py
@@ -0,0 +1,14 @@
+import unittest
+from itertools import product
+from L02.T01.sum_of_two_digits import sum_of_two_digits
+
+
+class TestCase(unittest.TestCase):
+
+ def test_all_single_digit_combinations(self):
+ for a, b in product(range(10), repeat=2):
+ self.assertEqual(a + b, sum_of_two_digits(a, b), msg=f"Wrong answer for a={a}, b={b}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T02/images/run.gif b/L02/T02/images/run.gif
new file mode 100644
index 0000000..09c99fb
Binary files /dev/null and b/L02/T02/images/run.gif differ
diff --git a/L02/T02/images/run.svg b/L02/T02/images/run.svg
new file mode 100644
index 0000000..dd11f50
--- /dev/null
+++ b/L02/T02/images/run.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/L02/T02/images/run_dark.gif b/L02/T02/images/run_dark.gif
new file mode 100644
index 0000000..1eb5ba6
Binary files /dev/null and b/L02/T02/images/run_dark.gif differ
diff --git a/L02/T02/images/run_dark.svg b/L02/T02/images/run_dark.svg
new file mode 100644
index 0000000..0c199c7
--- /dev/null
+++ b/L02/T02/images/run_dark.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/L02/T02/logo.png b/L02/T02/logo.png
new file mode 100644
index 0000000..073d0d6
Binary files /dev/null and b/L02/T02/logo.png differ
diff --git a/L02/T02/maximum_pairwise_product.py b/L02/T02/maximum_pairwise_product.py
new file mode 100644
index 0000000..952c2a2
--- /dev/null
+++ b/L02/T02/maximum_pairwise_product.py
@@ -0,0 +1,25 @@
+def max_pairwise_product_naive(numbers):
+ assert len(numbers) >= 2
+ assert all(0 <= x <= 2 * 10 ** 5 for x in numbers)
+
+ product = 0
+
+ for i in range(len(numbers)):
+ for j in range(i + 1, len(numbers)):
+ product = max(product, numbers[i] * numbers[j])
+
+ return product
+
+
+def max_pairwise_product(numbers):
+ assert len(numbers) >= 2
+ assert all(0 <= x <= 2 * 10 ** 5 for x in numbers)
+ numbers = sorted(numbers)
+ return numbers[-1] * numbers[-2]
+
+
+if __name__ == '__main__':
+ n = int(input())
+ input_numbers = list(map(int, input().split()))
+ assert len(input_numbers) == n
+ print(max_pairwise_product(input_numbers))
diff --git a/L02/T02/task-info.yaml b/L02/T02/task-info.yaml
new file mode 100644
index 0000000..5c36b55
--- /dev/null
+++ b/L02/T02/task-info.yaml
@@ -0,0 +1,31 @@
+type: edu
+custom_name: Maximum Pairwise Product
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: maximum_pairwise_product.py
+ visible: true
+ placeholders:
+ - offset: 427
+ length: 62
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/maximum_pairwise_product_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 409
+ length: 49
+ placeholder_text: "# TODO: write your test here"
+ - name: tests/tests.py
+ visible: false
+ - name: images/run_dark.gif
+ visible: false
+ is_binary: true
+ - name: images/run.svg
+ visible: false
+ - name: images/run_dark.svg
+ visible: false
+ - name: images/run.gif
+ visible: false
+ is_binary: true
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/Xscmz/programming-assignment-1-maximum-pairwise-product/discussions
diff --git a/L02/T02/task.md b/L02/T02/task.md
new file mode 100644
index 0000000..ad4698d
--- /dev/null
+++ b/L02/T02/task.md
@@ -0,0 +1,74 @@
+
+
+Given a list $A$ of non-negative integers, find
+the maximum product of two distinct elements (that is,
+the maximum value of $A[i] \cdot A[j]$ where $i \neq j$;
+note that it may be the case that $A[i]=A[j]$).
+The length of $A$ is at least 2 and at most $2 \cdot 10^5$,
+all elements are non-negative and do not exceed
+$2\cdot 10^5$.
+
+# Naive Algorithm
+
+The source file `maximum_pairwise_product.py`
+contains an implementation of a naive algorithm
+that just goes through all possible pairs. While it is
+clearly correct, it is too slow. To enusre this,
+make the following experiment. Add the following
+three lines to the end of the file:
+```
+n = 10
+A = [0] * n
+print(max_pairwise_product_naive(A))
+```
+This code creates a list $A$ of size
+$10$ filled with zeros. It then passes it to the
+function `max_pairwise_product_naive` and prints
+the result. To see the result, press the
+`Run`  button. It will print the result (0) in the
+Run area on blink of an eye.
+
+
+
+Now, change the value
+of $n$ from $10$ to $10^5$ by replacing `10` with
+`10**5` and run the resulting program again.
+You'll see that it hangs.
+
+# Fast Algorithm
+
+See the materials of the first week at Coursera
+to implement a faster algorithm as `max_pairwise_product`
+function.
+
+# Testing
+
+After implementing the function `max_pairwise_product`,
+start testing your solution. For this, switch to the file
+`maximum_pairwise_product_unit_tests.py`. It contains
+several unit tests that ensure the correctness of
+your program. The function `test_small` checks your
+function against a few manually created tests.
+The function `test_stress` generates a few short
+lists and checks whether your function returns the same
+as the naive one. Finally, the function `test_large` checks
+your function against massive datasets: the first one
+is a list of size $2 \cdot 10^5$ filled in with 4's, the
+second one is a list $[0, 1, \ldots, 10^5-1]$.
+
+Add one more unit test and run it. Ensure that all tests pass.
+
+
+# Checking Your Solution
+
+When all unit tests pass, check your solution by
+pressing the "Check" button.
+
+
+
Use some small list for which you are able to compute the result by hand
+
+Fibonacci numbers are defined recursively: $F_0=0$, $F_1=1$,
+and $F_n=F_{n-1}+F_{n-2}$ for $n \ge 1$.
+This definition results in the recursive function `compute_fibonacci_number_naive`
+that you see on the left in the file `fibonacci_number.py`.
+
+# Running the Naive Solution
+Try to compute $F_{10}$ using this function.
+For this, comment out everything except for the
+implementation of the `fibonacci_number_naive` function
+and add the line
+```
+print(fibonacci_number_naive(10))
+```
+to the end of the file and press the `Run`  button next to `main` on the left. You'll see that it will print the result (55) in the
+Run pane at the bottom in the blink of an eye. Now, change 10 to 40 and press the `Run`  button again. You'll see
+that the solution hangs and does not print anything. Stop it by pressing the red Stop button in the Run pane.
+
+Let's try to understand why our current solution hangs.
+To this end, let's add *debug printing* to the `fibonacci_number_naive` function:
+add the line
+```
+print("Compute F sub", n)
+```
+to the beginning of the function and run it again.
+You'll see a seemingly endless series of recursive calls in the Run pane:
+```
+Compute F sub 1
+Compute F sub 0
+Compute F sub 7
+Compute F sub 6
+Compute F sub 5
+Compute F sub 4
+Compute F sub 3
+Compute F sub 2
+Compute F sub 1
+Compute F sub 0
+Compute F sub 1
+Compute F sub 2
+Compute F sub 1
+Compute F sub 0
+Compute F sub 3
+```
+This output, in particular, reveals the reason why our
+current solution is so slow:
+*it computes the same thing again many times*.
+
+
+# Task
+
+## Implement an Efficient Solution
+Implement the `fibonacci_number` function.
+Make sure to avoid recomputing the same thing again.
+
+
+## Test Your Solution
+Now, switch to the file `fibonacci_number_unit_tests.py`.
+It consists of several unit tests. The function `test_small`
+checks that your implementation computes the same as the naive one
+for all $0 \le n < 8$ (this is affordable,
+since for $n < 8$ the
+`fibonacci_number_naive` is fast enough).
+The function `test_large`
+checks that your implementation computes $F_{30}$, $F_{35}$, and
+$F_{40}$ correctly.
+
+Enter the value of $F_{35}$ to the placeholder and
+run the unit tests by pressing the `Run`  button.
+
+Make sure that all tests pass. Then press the "Check" button in the
+Task Description pane.
+
+
Compute the value of the 35-th Fibonacci number and add it here. This way, the function test_large will also check that your implementation computes $F_{35}$ correctly.
+
+
diff --git a/L03/T01/tests/fibonacci_number_unit_tests.py b/L03/T01/tests/fibonacci_number_unit_tests.py
new file mode 100644
index 0000000..fd50627
--- /dev/null
+++ b/L03/T01/tests/fibonacci_number_unit_tests.py
@@ -0,0 +1,17 @@
+import unittest
+from L03.T01.fibonacci_number import fibonacci_number, fibonacci_number_naive
+
+
+class TestFibonacciNumber(unittest.TestCase):
+ def test_small(self):
+ for n in range(8):
+ self.assertEqual(fibonacci_number(n), fibonacci_number_naive(n))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (n, Fn) in [(30, 832040), (35, 9227465), (40, 102334155)]:
+ self.assertEqual(fibonacci_number(n), Fn)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T01/tests/tests.py b/L03/T01/tests/tests.py
new file mode 100644
index 0000000..2c8363c
--- /dev/null
+++ b/L03/T01/tests/tests.py
@@ -0,0 +1,25 @@
+import unittest
+from L03.T01.fibonacci_number import fibonacci_number
+
+
+def fibonacci_number_reference(n):
+ assert 0 <= n <= 40
+ if n <= 1:
+ return n
+
+ previous, current = 0, 1
+ for _ in range(n - 1):
+ previous, current = current, previous + current
+
+ return current
+
+
+class TestCase(unittest.TestCase):
+
+ def test_fibonacci_numbers(self):
+ for x in range(41):
+ self.assertEqual(fibonacci_number_reference(x), fibonacci_number(x), msg=f"Wrong answer for n={x}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T02/last_digit_of_fibonacci_number.py b/L03/T02/last_digit_of_fibonacci_number.py
new file mode 100644
index 0000000..02fe4ce
--- /dev/null
+++ b/L03/T02/last_digit_of_fibonacci_number.py
@@ -0,0 +1,27 @@
+def last_digit_of_fibonacci_number_naive(n):
+ assert 0 <= n <= 10 ** 7
+
+ if n <= 1:
+ return n
+
+ return (last_digit_of_fibonacci_number_naive(n - 1) + last_digit_of_fibonacci_number_naive(n - 2)) % 10
+
+
+def last_digit_of_fibonacci_number(n):
+ assert 0 <= n <= 10 ** 7
+
+ if n <= 1:
+ return n
+
+ fibonacci_numbers = [0] * (n + 1)
+ fibonacci_numbers[0] = 0
+ fibonacci_numbers[1] = 1
+ for i in range(2, n + 1):
+ fibonacci_numbers[i] = (fibonacci_numbers[i - 2] + fibonacci_numbers[i - 1]) % 10
+
+ return fibonacci_numbers[n]
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ print(last_digit_of_fibonacci_number(input_n))
diff --git a/L03/T02/logo.png b/L03/T02/logo.png
new file mode 100644
index 0000000..9b9e43e
Binary files /dev/null and b/L03/T02/logo.png differ
diff --git a/L03/T02/task-info.yaml b/L03/T02/task-info.yaml
new file mode 100644
index 0000000..1432277
--- /dev/null
+++ b/L03/T02/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Last Digit of Fibonacci Number
+files:
+ - name: last_digit_of_fibonacci_number.py
+ visible: true
+ placeholders:
+ - offset: 291
+ length: 277
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/last_digit_of_fibonacci_number_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 522
+ length: 8
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T02/task.md b/L03/T02/task.md
new file mode 100644
index 0000000..661860f
--- /dev/null
+++ b/L03/T02/task.md
@@ -0,0 +1,22 @@
+# Last Digit of Fibonacci Number
+
+
+
+Implement `last_digit_of_fibonacci_number` function
+that takes an integer $0 \le n \le 10^7$ and returns
+the last digit of $F_n$.
+
+As usual, after implementing a solution, do the
+following:
+* Switch to the unit tests file, add a few new
+tests to the already implemented ones, and run the
+tests.
+* If a bug is found, fix it and run the tests again.
+* Then, check your solution in PyCharm.
+
+Please follow the same steps for all the
+forthcoming programming challenges. For them, we will
+provide the challenge statement only.
+
+
Recall that you need to compute just the last digit of a Fibonacci number. In order to speed up the computation, take every intermediate result modulo 10.
+
diff --git a/L03/T02/tests/last_digit_of_fibonacci_number_unit_tests.py b/L03/T02/tests/last_digit_of_fibonacci_number_unit_tests.py
new file mode 100644
index 0000000..1c8baf3
--- /dev/null
+++ b/L03/T02/tests/last_digit_of_fibonacci_number_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L03.T02.last_digit_of_fibonacci_number import last_digit_of_fibonacci_number, last_digit_of_fibonacci_number_naive
+
+
+class TestLastDigitOfFibonacciNumber(unittest.TestCase):
+ def test_small(self):
+ for n in range(20):
+ self.assertEqual(last_digit_of_fibonacci_number_naive(n),
+ last_digit_of_fibonacci_number(n))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (n, last_digit) in [(100, 5), (139, 1), (91239, 6), (100, 5)]:
+ self.assertEqual(last_digit_of_fibonacci_number(n), last_digit)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T02/tests/tests.py b/L03/T02/tests/tests.py
new file mode 100644
index 0000000..6650f1a
--- /dev/null
+++ b/L03/T02/tests/tests.py
@@ -0,0 +1,42 @@
+import unittest
+from L03.T02.last_digit_of_fibonacci_number import last_digit_of_fibonacci_number
+
+
+def fibonacci_number_last_digit_reference(n):
+ assert 0 <= n <= 10 ** 6
+ if n <= 1:
+ return n
+
+ previous, current = 0, 1
+ for _ in range(n - 1):
+ previous, current = current, (previous + current) % 10
+
+ return current
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_2(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(2), last_digit_of_fibonacci_number(2), msg="Wrong answer for n=2")
+
+ def test_n_3(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(3), last_digit_of_fibonacci_number(3), msg="Wrong answer for n=3")
+
+ def test_n_239(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(239), last_digit_of_fibonacci_number(239), msg="Wrong answer for n=239")
+
+ def test_n_240(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(240), last_digit_of_fibonacci_number(240), msg="Wrong answer for n=240")
+
+ def test_n_1000(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(1000), last_digit_of_fibonacci_number(1000), msg="Wrong answer for n=1000")
+
+ def test_n_9999(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(9999), last_digit_of_fibonacci_number(9999), msg="Wrong answer for n=9999")
+
+ def test_n_10_6(self):
+ self.assertEqual(fibonacci_number_last_digit_reference(10**6), last_digit_of_fibonacci_number(10**6), msg="Wrong answer for n=1000000")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T03/gcd.py b/L03/T03/gcd.py
new file mode 100644
index 0000000..7539c24
--- /dev/null
+++ b/L03/T03/gcd.py
@@ -0,0 +1,26 @@
+def gcd_naive(a, b):
+ assert 1 <= a <= 2 * 10 ** 9 and 1 <= b <= 2 * 10 ** 9
+
+ for divisor in range(min(a, b), 0, -1):
+ if a % divisor == 0 and b % divisor == 0:
+ return divisor
+
+ assert False
+
+
+def gcd(a, b):
+ assert 0 <= a <= 2 * 10 ** 9 and 0 <= b <= 2 * 10 ** 9
+
+ if a == 0:
+ return b
+ if b == 0:
+ return a
+ if a > b:
+ return gcd(a % b, b)
+ else:
+ return gcd(a, b % a)
+
+
+if __name__ == '__main__':
+ input_a, input_b = map(int, input().split())
+ print(gcd(input_a, input_b))
diff --git a/L03/T03/logo.png b/L03/T03/logo.png
new file mode 100644
index 0000000..5339eb4
Binary files /dev/null and b/L03/T03/logo.png differ
diff --git a/L03/T03/task-info.yaml b/L03/T03/task-info.yaml
new file mode 100644
index 0000000..362fcd1
--- /dev/null
+++ b/L03/T03/task-info.yaml
@@ -0,0 +1,24 @@
+type: edu
+custom_name: Greatest Common Divisor
+files:
+ - name: gcd.py
+ visible: true
+ placeholders:
+ - offset: 301
+ length: 141
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/gcd_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 204
+ length: 6
+ placeholder_text: None
+ - offset: 399
+ length: 9
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T03/task.md b/L03/T03/task.md
new file mode 100644
index 0000000..a008f81
--- /dev/null
+++ b/L03/T03/task.md
@@ -0,0 +1,27 @@
+# Greatest Common Divisor
+
+
+
+The greatest common divisor
+$\operatorname{GCD}(a,b)$ of two positive
+integers $a$ and $b$ is the largest integer $d$
+that divides both $a$ and $b$. The solution
+of the Greatest Common Divisor Problem was
+first described (but not discovered!) by
+the Greek mathematician Euclid twenty
+three centuries ago. But the name of
+a mathematician who discovered this algorithm,
+a century before Euclid described it, remains
+unknown. Centuries later, Euclid's algorithm
+was re-discovered by Indian and Chinese astronomers.
+Now, efficient algorithm for computing the greatest
+common divisor is an important ingredient of modern
+cryptographic algorithms.
+
+Your goal is to implement Euclid's algorithm for computing $\operatorname{GCD}$.
+
+Implement a function that computes the greatest
+common divisor of two integers
+$1 \le a, b \le 2 \cdot 10^9$.
+
+
diff --git a/L03/T03/tests/gcd_unit_tests.py b/L03/T03/tests/gcd_unit_tests.py
new file mode 100644
index 0000000..181ac0d
--- /dev/null
+++ b/L03/T03/tests/gcd_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L03.T03.gcd import gcd, gcd_naive
+
+
+class TestGCD(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for (a, b) in [(1, 1), (2, 6), (2, 6)]:
+ self.assertEqual(gcd(a, b), gcd_naive(a, b))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (a, b, d) in [(28851538, 1183019, 17657), (1, 1, 1)]:
+ self.assertEqual(gcd(a, b), d)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T03/tests/tests.py b/L03/T03/tests/tests.py
new file mode 100644
index 0000000..7255222
--- /dev/null
+++ b/L03/T03/tests/tests.py
@@ -0,0 +1,19 @@
+import math
+import unittest
+from L03.T03.gcd import gcd
+
+
+class TestCase(unittest.TestCase):
+
+ def test_coprime_numbers(self):
+ self.assertEqual(math.gcd(2, 3), gcd(2, 3), msg="Wrong answer for a=2, b=3")
+
+ def test_large_numbers_1(self):
+ self.assertEqual(math.gcd(10**8, 10**5 - 1), gcd(10**8, 10**5 - 1), msg="Wrong answer for a=100000000, b=99999")
+
+ def test_large_numbers_2(self):
+ self.assertEqual(math.gcd(10**8, 10**9), gcd(10**8, 10**9), msg="Wrong answer for a=100000000, b=1000000000")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T04/lcm.py b/L03/T04/lcm.py
new file mode 100644
index 0000000..1984af7
--- /dev/null
+++ b/L03/T04/lcm.py
@@ -0,0 +1,25 @@
+def lcm_naive(a, b):
+ assert 1 <= a <= 2 * 10 ** 9 and 1 <= b <= 2 * 10 ** 9
+
+ multiple = max(a, b)
+ while multiple % a != 0 or multiple % b != 0:
+ multiple += 1
+
+ return multiple
+
+
+def lcm(a, b):
+ assert 1 <= a <= 2 * 10 ** 9 and 1 <= b <= 2 * 10 ** 9
+
+ def gcd(smaller, larger):
+ if smaller == 0:
+ return larger
+ else:
+ return gcd(larger % smaller, smaller)
+
+ return a * b // gcd(min(a, b), max(a, b))
+
+
+if __name__ == '__main__':
+ input_a, input_b = map(int, input().split())
+ print(lcm(input_a, input_b))
diff --git a/L03/T04/logo.png b/L03/T04/logo.png
new file mode 100644
index 0000000..c9fad2c
Binary files /dev/null and b/L03/T04/logo.png differ
diff --git a/L03/T04/task-info.yaml b/L03/T04/task-info.yaml
new file mode 100644
index 0000000..a953bf2
--- /dev/null
+++ b/L03/T04/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Least Common Multiple
+files:
+ - name: lcm.py
+ visible: true
+ placeholders:
+ - offset: 280
+ length: 187
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/lcm_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 397
+ length: 31
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T04/task.md b/L03/T04/task.md
new file mode 100644
index 0000000..3ab2da4
--- /dev/null
+++ b/L03/T04/task.md
@@ -0,0 +1,15 @@
+# Least Common Multiple
+
+
+
+The least common multiple
+$\operatorname{LCM}(a,b)$ of two positive
+integers $a$ and $b$ is the smallest
+integer $m$ that is divisible by both $a$ and $b$.
+
+How $\operatorname{LCM}(a,b)$ is related to
+$\operatorname{GCD}(a,b)$?
+
+Compute the least common multiple
+of two integers
+$1 \le a, b \le 2 \cdot 10^9$.
diff --git a/L03/T04/tests/lcm_unit_tests.py b/L03/T04/tests/lcm_unit_tests.py
new file mode 100644
index 0000000..590ce82
--- /dev/null
+++ b/L03/T04/tests/lcm_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from itertools import product
+from L03.T04.lcm import lcm, lcm_naive
+
+
+class TestLCM(unittest.TestCase):
+ def test_small(self):
+ for (a, b) in product(range(1, 15), repeat=2):
+ self.assertEqual(lcm(a, b), lcm_naive(a, b))
+
+ def test_large(self):
+ # TODO: replace None with the correct value
+ for (a, b, m) in [(28851538, 1183019, 1933053046), (28851538, 1183019, 1933053046)]:
+ self.assertEqual(lcm(a, b), m)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T04/tests/tests.py b/L03/T04/tests/tests.py
new file mode 100644
index 0000000..8dcdcd1
--- /dev/null
+++ b/L03/T04/tests/tests.py
@@ -0,0 +1,22 @@
+import math
+import unittest
+from random import randint
+from L03.T04.lcm import lcm
+
+
+class TestCase(unittest.TestCase):
+
+ def test_random_pairs(self):
+ for _ in range(10):
+ a, b = randint(1, 2 * 10 ** 9), randint(1, 2 * 10 ** 9)
+ self.assertEqual(a * b // math.gcd(a, b), lcm(a, b), msg=f"Wrong answer for a={a}, b={b}")
+
+ def test_random_pairs_with_common_factor(self):
+ for _ in range(10):
+ c = randint(1, 10 ** 4)
+ a, b = randint(1, 10 ** 5) * c, randint(1, 10 ** 5) * c
+ self.assertEqual(a * b // math.gcd(a, b), lcm(a, b), msg=f"Wrong answer for a={a}, b={b}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T05/fibonacci_number_again.py b/L03/T05/fibonacci_number_again.py
new file mode 100644
index 0000000..821d42c
--- /dev/null
+++ b/L03/T05/fibonacci_number_again.py
@@ -0,0 +1,36 @@
+def fibonacci_number_again_naive(n, m):
+ assert 0 <= n <= 10 ** 18 and 2 <= m <= 10 ** 3
+
+ if n <= 1:
+ return n
+
+ previous, current = 0, 1
+ for _ in range(n - 1):
+ previous, current = current, (previous + current) % m
+
+ return current
+
+
+def fibonacci_number_again(n, m):
+ assert 0 <= n <= 10 ** 18 and 2 <= m <= 10 ** 3
+
+ if n <= 1:
+ return n
+
+ prev, cur = 0, 1
+ remainders = {(0, 1)}
+
+ while True:
+ prev, cur = cur, (prev + cur) % m
+ if (prev, cur) in remainders:
+ break
+ remainders.add((prev, cur))
+
+ period = len(remainders)
+
+ return fibonacci_number_again_naive(n % period, m)
+
+
+if __name__ == '__main__':
+ input_n, input_m = map(int, input().split())
+ print(fibonacci_number_again(input_n, input_m))
diff --git a/L03/T05/logo.png b/L03/T05/logo.png
new file mode 100644
index 0000000..6e48801
Binary files /dev/null and b/L03/T05/logo.png differ
diff --git a/L03/T05/table1.png b/L03/T05/table1.png
new file mode 100644
index 0000000..f9026ec
Binary files /dev/null and b/L03/T05/table1.png differ
diff --git a/L03/T05/table2.png b/L03/T05/table2.png
new file mode 100644
index 0000000..014a2f3
Binary files /dev/null and b/L03/T05/table2.png differ
diff --git a/L03/T05/task-info.yaml b/L03/T05/task-info.yaml
new file mode 100644
index 0000000..1358099
--- /dev/null
+++ b/L03/T05/task-info.yaml
@@ -0,0 +1,27 @@
+type: edu
+custom_name: Fibonacci Number Again
+files:
+ - name: fibonacci_number_again.py
+ visible: true
+ placeholders:
+ - offset: 357
+ length: 312
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/fibonacci_number_again_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 512
+ length: 16
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: table1.png
+ visible: false
+ is_binary: true
+ - name: table2.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T05/task.md b/L03/T05/task.md
new file mode 100644
index 0000000..7f85c96
--- /dev/null
+++ b/L03/T05/task.md
@@ -0,0 +1,28 @@
+# Fibonacci Number Again
+
+
+
+Given two integers $0 \le n \le 10^{18}$ and
+$2 \le m \le 10^3$,
+compute the $n$-th Fibonacci number modulo $m$.
+
+In this problem, $n$ may be so huge that an algorithm looping for $n$ iterations will be too slow. Therefore we need to avoid such a loop.
+To get an idea how to solve this problem without going through all Fibonacci numbers
+$F_i$ for $i$ from $0$ to $n$,
+take a look at the table below:
+
+
+
+Do you see any interesting properties of the last two rows in the table above?
+
+Both these sequences are periodic! For $m=2$, the period is $0 1 1$ and has length $3$, while for $m=3$ the period is $0 1 1 2 0 2 2 1$ and has length $8$.
+
+
+
+Therefore, to compute, say, $F_{2015} \bmod{3}$ we just need to find the remainder of $2015$ when divided by $8$. Since $2015=251 \cdot 8 + 7$, we conclude that $F_{2015} \bmod{3} = F_{7} \bmod{3}=1$.
+
+It turns out that for any integer $m \ge 2$,
+the sequence $F_n \bmod{m}$ is periodic.
+The period always starts with $0 1$ and is
+known as *Pisano period*
+(Pisano is another name of Fibonacci).
diff --git a/L03/T05/tests/fibonacci_number_again_unit_tests.py b/L03/T05/tests/fibonacci_number_again_unit_tests.py
new file mode 100644
index 0000000..bb2222c
--- /dev/null
+++ b/L03/T05/tests/fibonacci_number_again_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from itertools import product
+from L03.T05.fibonacci_number_again import fibonacci_number_again, fibonacci_number_again_naive
+
+
+class TestFibonacciNumberAgain(unittest.TestCase):
+ def test_small(self):
+ for n, m in product(range(2, 15), repeat=2):
+ self.assertEqual(fibonacci_number_again(n, m), fibonacci_number_again_naive(n, m))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (n, m, r) in [(115, 1000, 885), (2816213588, 239, 151), (115, 1000, 885)]:
+ self.assertEqual(fibonacci_number_again(n, m), r)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T05/tests/tests.py b/L03/T05/tests/tests.py
new file mode 100644
index 0000000..a1e9c2b
--- /dev/null
+++ b/L03/T05/tests/tests.py
@@ -0,0 +1,40 @@
+import unittest
+from L03.T05.fibonacci_number_again import fibonacci_number_again
+
+
+def pisano_period(m):
+ current, next = 0, 1
+ period = 0
+
+ while True:
+ current, next = next, (current + next) % m
+ period += 1
+ if current == 0 and next == 1:
+ return period
+
+
+def fib_mod(n, m):
+ current, next = 0, 1
+ for _ in range(n):
+ current, next = next, (current + next) % m
+
+ return current
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_7_m_239(self):
+ n, m = 7, 239
+ self.assertEqual(fib_mod(n % pisano_period(m), m), fibonacci_number_again(n, m), msg=f"Wrong answer for n={n}, m={m}")
+
+ def test_n_239_m_7(self):
+ n, m = 239, 7
+ self.assertEqual(fib_mod(n % pisano_period(m), m), fibonacci_number_again(n, m), msg=f"Wrong answer for n={n}, m={m}")
+
+ def test_n_10_18_m_239(self):
+ n, m = 10 ** 18, 239
+ self.assertEqual(fib_mod(n % pisano_period(m), m), fibonacci_number_again(n, m), msg=f"Wrong answer for n={n}, m={m}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T06/last_digit_of_the_sum_of_fibonacci_numbers.py b/L03/T06/last_digit_of_the_sum_of_fibonacci_numbers.py
new file mode 100644
index 0000000..47ee5c4
--- /dev/null
+++ b/L03/T06/last_digit_of_the_sum_of_fibonacci_numbers.py
@@ -0,0 +1,30 @@
+def last_digit_of_the_sum_of_fibonacci_numbers_naive(n):
+ assert 0 <= n <= 10 ** 18
+
+ if n <= 1:
+ return n
+
+ fibonacci_numbers = [0] * (n + 1)
+ fibonacci_numbers[0] = 0
+ fibonacci_numbers[1] = 1
+ for i in range(2, n + 1):
+ fibonacci_numbers[i] = fibonacci_numbers[i - 2] + fibonacci_numbers[i - 1]
+
+ return sum(fibonacci_numbers) % 10
+
+
+def last_digit_of_the_sum_of_fibonacci_numbers(n):
+ assert 0 <= n <= 10 ** 18
+
+ n = (n + 2) % 60
+
+ prev, cur = 0, 1
+ for _ in range(n):
+ prev, cur = cur, (prev + cur) % 10
+
+ return (prev + 9) % 10
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ print(last_digit_of_the_sum_of_fibonacci_numbers(input_n))
diff --git a/L03/T06/logo.png b/L03/T06/logo.png
new file mode 100644
index 0000000..3f1b6dd
Binary files /dev/null and b/L03/T06/logo.png differ
diff --git a/L03/T06/task-info.yaml b/L03/T06/task-info.yaml
new file mode 100644
index 0000000..c7054c5
--- /dev/null
+++ b/L03/T06/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Last Digit of the Sum of Fibonacci Numbers
+files:
+ - name: last_digit_of_the_sum_of_fibonacci_numbers.py
+ visible: true
+ placeholders:
+ - offset: 458
+ length: 132
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/last_digit_of_the_sum_of_fibonacci_numbers_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 569
+ length: 8
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T06/task.md b/L03/T06/task.md
new file mode 100644
index 0000000..8416cfa
--- /dev/null
+++ b/L03/T06/task.md
@@ -0,0 +1,17 @@
+# Last Digit of the Sum of Fibonacci Numbers
+
+
+
+Given $0 \le n \le 10^{18}$,
+compute the last digit of $$F_0+F_1+\dotsb+F_n.$$
+
+Since the brute force approach for this problem is too slow, try to come up with a formula for $F_0+F_1+F_2+\dotsb+F_n$. Play with small values of $n$ to get an insight and use a solution for the previous problem afterwards.
+
+
+A detailed solution for this programming challenge is covered in the companion MOOCBook. But we strongly encourage you to do your best to solve the challenge yourself before looking into the book! There are at least three good reasons for this.
+
+
By solving this challenge, you practice solving algorithmic problems similar to those given at technical interviews.
+
The satisfaction and self confidence that you get when passing the grader is priceless =)
+
Even if you fail to pass the grader yourself, the time will not be lost as you will better understand the solution from the book and better appreciate the beauty of the underlying ideas.
+
+
diff --git a/L03/T06/tests/last_digit_of_the_sum_of_fibonacci_numbers_unit_tests.py b/L03/T06/tests/last_digit_of_the_sum_of_fibonacci_numbers_unit_tests.py
new file mode 100644
index 0000000..1744a11
--- /dev/null
+++ b/L03/T06/tests/last_digit_of_the_sum_of_fibonacci_numbers_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L03.T06.last_digit_of_the_sum_of_fibonacci_numbers import last_digit_of_the_sum_of_fibonacci_numbers, last_digit_of_the_sum_of_fibonacci_numbers_naive
+
+
+class TestLastDigitOfTheSumOfFibonacciNumbers(unittest.TestCase):
+ def test_small(self):
+ for n in range(20):
+ self.assertEqual(last_digit_of_the_sum_of_fibonacci_numbers(n),
+ last_digit_of_the_sum_of_fibonacci_numbers_naive(n))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (n, last_digit) in [(100, 5), (100, 5)]:
+ self.assertEqual(last_digit_of_the_sum_of_fibonacci_numbers(n), last_digit)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T06/tests/tests.py b/L03/T06/tests/tests.py
new file mode 100644
index 0000000..dcddb8c
--- /dev/null
+++ b/L03/T06/tests/tests.py
@@ -0,0 +1,40 @@
+import unittest
+from L03.T06.last_digit_of_the_sum_of_fibonacci_numbers import last_digit_of_the_sum_of_fibonacci_numbers
+
+
+def fibonacci_sum_last_digit(n):
+ n = (n + 2) % 60
+
+ prev, cur = 0, 1
+ for _ in range(n):
+ prev, cur = cur, (prev + cur) % 10
+
+ return (prev + 9) % 10
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_2(self):
+ self.assertEqual(fibonacci_sum_last_digit(2), last_digit_of_the_sum_of_fibonacci_numbers(2), msg="Wrong answer for n=2")
+
+ def test_n_3(self):
+ self.assertEqual(fibonacci_sum_last_digit(3), last_digit_of_the_sum_of_fibonacci_numbers(3), msg="Wrong answer for n=3")
+
+ def test_n_239(self):
+ self.assertEqual(fibonacci_sum_last_digit(239), last_digit_of_the_sum_of_fibonacci_numbers(239), msg="Wrong answer for n=239")
+
+ def test_n_240(self):
+ self.assertEqual(fibonacci_sum_last_digit(240), last_digit_of_the_sum_of_fibonacci_numbers(240), msg="Wrong answer for n=240")
+
+ def test_n_1000(self):
+ self.assertEqual(fibonacci_sum_last_digit(1000), last_digit_of_the_sum_of_fibonacci_numbers(1000), msg="Wrong answer for n=1000")
+
+ def test_n_9999(self):
+ self.assertEqual(fibonacci_sum_last_digit(9999), last_digit_of_the_sum_of_fibonacci_numbers(9999), msg="Wrong answer for n=9999")
+
+ def test_n_10_17(self):
+ self.assertEqual(fibonacci_sum_last_digit(10 ** 17), last_digit_of_the_sum_of_fibonacci_numbers(10 ** 17), msg="Wrong answer for n=100000000000000000")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T07/last_digit_of_the_sum_of_fibonacci_numbers_again.py b/L03/T07/last_digit_of_the_sum_of_fibonacci_numbers_again.py
new file mode 100644
index 0000000..cef08db
--- /dev/null
+++ b/L03/T07/last_digit_of_the_sum_of_fibonacci_numbers_again.py
@@ -0,0 +1,33 @@
+def last_digit_of_the_sum_of_fibonacci_numbers_again_naive(from_index, to_index):
+ assert 0 <= from_index <= to_index <= 10 ** 18
+
+ if to_index == 0:
+ return 0
+
+ fibonacci_numbers = [0] * (to_index + 1)
+ fibonacci_numbers[0] = 0
+ fibonacci_numbers[1] = 1
+ for i in range(2, to_index + 1):
+ fibonacci_numbers[i] = fibonacci_numbers[i - 2] + fibonacci_numbers[i - 1]
+
+ return sum(fibonacci_numbers[from_index:to_index + 1]) % 10
+
+
+def last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index):
+ assert 0 <= from_index <= to_index <= 10 ** 18
+
+ def fib_sum_last_digit(n):
+ n = n % 60
+
+ prev, cur = 0, 1
+ for _ in range(n):
+ prev, cur = cur, (prev + cur) % 10
+
+ return (prev + 9) % 10
+
+ return (20 + fib_sum_last_digit(to_index + 2) - fib_sum_last_digit(from_index + 1)) % 10
+
+
+if __name__ == '__main__':
+ input_from, input_to = map(int, input().split())
+ print(last_digit_of_the_sum_of_fibonacci_numbers_again(input_from, input_to))
diff --git a/L03/T07/logo.png b/L03/T07/logo.png
new file mode 100644
index 0000000..da1b8f4
Binary files /dev/null and b/L03/T07/logo.png differ
diff --git a/L03/T07/task-info.yaml b/L03/T07/task-info.yaml
new file mode 100644
index 0000000..03b4c7e
--- /dev/null
+++ b/L03/T07/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Last Digit of the Sum of Fibonacci Numbers Again
+files:
+ - name: last_digit_of_the_sum_of_fibonacci_numbers_again.py
+ visible: true
+ placeholders:
+ - offset: 596
+ length: 271
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/last_digit_of_the_sum_of_fibonacci_numbers_again_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 960
+ length: 9
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T07/task.md b/L03/T07/task.md
new file mode 100644
index 0000000..9799bd6
--- /dev/null
+++ b/L03/T07/task.md
@@ -0,0 +1,6 @@
+# Last Digit of the Sum of Fibonacci Numbers Again
+
+
+
+Given two integers $0 \le m \le n \le 10^{18}$,
+compute the last digit of $$F_m+F_{m+1}+\dotsb+F_n.$$
diff --git a/L03/T07/tests/last_digit_of_the_sum_of_fibonacci_numbers_again_unit_tests.py b/L03/T07/tests/last_digit_of_the_sum_of_fibonacci_numbers_again_unit_tests.py
new file mode 100644
index 0000000..6e26071
--- /dev/null
+++ b/L03/T07/tests/last_digit_of_the_sum_of_fibonacci_numbers_again_unit_tests.py
@@ -0,0 +1,23 @@
+import unittest
+from itertools import combinations
+from L03.T07.last_digit_of_the_sum_of_fibonacci_numbers_again import last_digit_of_the_sum_of_fibonacci_numbers_again, \
+ last_digit_of_the_sum_of_fibonacci_numbers_again_naive
+
+
+class TestLastDigitOfTheSumOfFibonacciNumbersAgain(unittest.TestCase):
+ def test_small(self):
+ for from_index, to_index in combinations(range(2, 15), 2):
+ self.assertEqual(last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index),
+ last_digit_of_the_sum_of_fibonacci_numbers_again_naive(from_index, to_index))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (from_index, to_index, last_digit) in [(3, 7, 1), (10, 10, 5), (100, 200, 0),
+ (17, 1700, 7),
+ (19, 10000000000, 1),
+ (3, 7, 1)]:
+ self.assertEqual(last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index), last_digit)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T07/tests/tests.py b/L03/T07/tests/tests.py
new file mode 100644
index 0000000..a692e52
--- /dev/null
+++ b/L03/T07/tests/tests.py
@@ -0,0 +1,39 @@
+import unittest
+from L03.T07.last_digit_of_the_sum_of_fibonacci_numbers_again import last_digit_of_the_sum_of_fibonacci_numbers_again
+
+
+def fibonacci_sum_last_digit(n):
+ n = n % 60
+
+ prev, cur = 0, 1
+ for _ in range(n):
+ prev, cur = cur, (prev + cur) % 10
+
+ return (prev + 9) % 10
+
+
+def reference(from_index, to_index):
+ return (20 + fibonacci_sum_last_digit(to_index + 2) - fibonacci_sum_last_digit(from_index + 1)) % 10
+
+
+class TestCase(unittest.TestCase):
+
+ def test_from_1_to_2(self):
+ from_index, to_index = 1, 2
+ self.assertEqual(reference(from_index, to_index), last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index), msg=f"Wrong answer for m={from_index}, n={to_index}")
+
+ def test_from_2_to_239(self):
+ from_index, to_index = 2, 239
+ self.assertEqual(reference(from_index, to_index), last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index), msg=f"Wrong answer for m={from_index}, n={to_index}")
+
+ def test_from_1_to_10_10(self):
+ from_index, to_index = 1, 10 ** 10
+ self.assertEqual(reference(from_index, to_index), last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index), msg=f"Wrong answer for m={from_index}, n={to_index}")
+
+ def test_from_10_10_to_10_13(self):
+ from_index, to_index = 10 ** 10, 10 ** 13
+ self.assertEqual(reference(from_index, to_index), last_digit_of_the_sum_of_fibonacci_numbers_again(from_index, to_index), msg=f"Wrong answer for m={from_index}, n={to_index}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T08/last_digit_of_the_sum_of_squares_of_fibonacci_numbers.py b/L03/T08/last_digit_of_the_sum_of_squares_of_fibonacci_numbers.py
new file mode 100644
index 0000000..eea435a
--- /dev/null
+++ b/L03/T08/last_digit_of_the_sum_of_squares_of_fibonacci_numbers.py
@@ -0,0 +1,30 @@
+def last_digit_of_the_sum_of_squares_of_fibonacci_numbers_naive(n):
+ assert 0 <= n <= 10 ** 18
+
+ if n <= 1:
+ return n
+
+ fibonacci_numbers = [0] * (n + 1)
+ fibonacci_numbers[0] = 0
+ fibonacci_numbers[1] = 1
+ for i in range(2, n + 1):
+ fibonacci_numbers[i] = fibonacci_numbers[i - 2] + fibonacci_numbers[i - 1]
+
+ return sum([f ** 2 for f in fibonacci_numbers]) % 10
+
+
+def last_digit_of_the_sum_of_squares_of_fibonacci_numbers(n):
+ assert 0 <= n <= 10 ** 18
+
+ n = n % 60
+
+ prev, cur = 0, 1
+ for _ in range(n):
+ prev, cur = cur, (prev + cur) % 10
+
+ return (prev * cur) % 10
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ print(last_digit_of_the_sum_of_squares_of_fibonacci_numbers(input_n))
diff --git a/L03/T08/logo.png b/L03/T08/logo.png
new file mode 100644
index 0000000..125444f
Binary files /dev/null and b/L03/T08/logo.png differ
diff --git a/L03/T08/task-info.yaml b/L03/T08/task-info.yaml
new file mode 100644
index 0000000..be50026
--- /dev/null
+++ b/L03/T08/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Last Digit of the Sum of Squares of Fibonacci Numbers
+files:
+ - name: last_digit_of_the_sum_of_squares_of_fibonacci_numbers.py
+ visible: true
+ placeholders:
+ - offset: 498
+ length: 128
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/last_digit_of_the_sum_of_squares_of_fibonacci_numbers_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 649
+ length: 7
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/b66y2/programming-assignment-2-algorithmic-warm-up/discussions
diff --git a/L03/T08/task.md b/L03/T08/task.md
new file mode 100644
index 0000000..a43bc44
--- /dev/null
+++ b/L03/T08/task.md
@@ -0,0 +1,8 @@
+# Last Digit of the Sum of Squares of Fibonacci Numbers
+
+
+
+Given $0 \le n \le 10^{18}$,
+compute the last digit of $$F_0^2+F_1^2+\dotsb+F_n^2.$$
+
+Since the brute force search algorithm for this problem is too slow ($n$ may be as large as $10^{18}$), we need to come up with a simple formula for $F_0^2+F_1^2+\dotsb+F_n^2$. The figure above represents the sum $F_1^2+F_2^2+F_3^2+F_4^2+F_5^2$ as the area of a rectangle with vertical side $F_5=5$ and horizontal side $F_5+F_4=3+5=F_6$.
diff --git a/L03/T08/tests/last_digit_of_the_sum_of_squares_of_fibonacci_numbers_unit_tests.py b/L03/T08/tests/last_digit_of_the_sum_of_squares_of_fibonacci_numbers_unit_tests.py
new file mode 100644
index 0000000..3c081b0
--- /dev/null
+++ b/L03/T08/tests/last_digit_of_the_sum_of_squares_of_fibonacci_numbers_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L03.T08.last_digit_of_the_sum_of_squares_of_fibonacci_numbers import last_digit_of_the_sum_of_squares_of_fibonacci_numbers, last_digit_of_the_sum_of_squares_of_fibonacci_numbers_naive
+
+
+class TestLastDigitOfTheSumOfSquaresOfFibonacciNumbers(unittest.TestCase):
+ def test_small(self):
+ for n in range(20):
+ self.assertEqual(last_digit_of_the_sum_of_squares_of_fibonacci_numbers(n),
+ last_digit_of_the_sum_of_squares_of_fibonacci_numbers_naive(n))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (n, last_digit) in [(73, 1), (1234567890, 0), (73, 1)]:
+ self.assertEqual(last_digit_of_the_sum_of_squares_of_fibonacci_numbers(n), last_digit)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/T08/tests/tests.py b/L03/T08/tests/tests.py
new file mode 100644
index 0000000..3d3264b
--- /dev/null
+++ b/L03/T08/tests/tests.py
@@ -0,0 +1,12 @@
+import unittest
+from L03.T08.last_digit_of_the_sum_of_squares_of_fibonacci_numbers import last_digit_of_the_sum_of_squares_of_fibonacci_numbers
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_73(self):
+ self.assertEqual(1, last_digit_of_the_sum_of_squares_of_fibonacci_numbers(73), msg="Wrong answer for n=73")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L03/lesson-info.yaml b/L03/lesson-info.yaml
new file mode 100644
index 0000000..2132e9b
--- /dev/null
+++ b/L03/lesson-info.yaml
@@ -0,0 +1,10 @@
+custom_name: Algorithmic Warm Up
+content:
+ - T01
+ - T02
+ - T03
+ - T04
+ - T05
+ - T06
+ - T07
+ - T08
diff --git a/L04/T01/logo.png b/L04/T01/logo.png
new file mode 100644
index 0000000..6f29f83
Binary files /dev/null and b/L04/T01/logo.png differ
diff --git a/L04/T01/money_change.py b/L04/T01/money_change.py
new file mode 100644
index 0000000..d042bb1
--- /dev/null
+++ b/L04/T01/money_change.py
@@ -0,0 +1,8 @@
+def money_change(money):
+ assert 0 <= money <= 10 ** 3
+ return (money // 10) + ((money % 10) // 5) + (money % 5)
+
+
+if __name__ == '__main__':
+ input_money = int(input())
+ print(money_change(input_money))
diff --git a/L04/T01/task-info.yaml b/L04/T01/task-info.yaml
new file mode 100644
index 0000000..d050ee0
--- /dev/null
+++ b/L04/T01/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Money Change
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: money_change.py
+ visible: true
+ placeholders:
+ - offset: 62
+ length: 56
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/money_change_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 243
+ length: 6
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-3-greedy-algorithms/discussions
diff --git a/L04/T01/task.md b/L04/T01/task.md
new file mode 100644
index 0000000..d2fa400
--- /dev/null
+++ b/L04/T01/task.md
@@ -0,0 +1,9 @@
+# Money Change
+
+
+
+Given an integer $1 \le money \le 10^3$, find
+the minimum number of coins with
+denominations 1, 5, and 10 that changes $money$.
+
+In this problem, you will implement a simple greedy algorithm used by cashiers all over the world. We assume that a cashier has unlimited number of coins of each denomination.
diff --git a/L04/T01/tests/money_change_unit_tests.py b/L04/T01/tests/money_change_unit_tests.py
new file mode 100644
index 0000000..974d22d
--- /dev/null
+++ b/L04/T01/tests/money_change_unit_tests.py
@@ -0,0 +1,13 @@
+import unittest
+from L04.T01.money_change import money_change
+
+
+class TestSumOfTwoDigits(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for (money, number_of_coins) in [(1, 1), (2, 2), (28, 6), (1, 1)]:
+ self.assertEqual(money_change(money), number_of_coins)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T01/tests/tests.py b/L04/T01/tests/tests.py
new file mode 100644
index 0000000..9439d8b
--- /dev/null
+++ b/L04/T01/tests/tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L04.T01.money_change import money_change
+
+
+def reference(money):
+ assert 0 <= money <= 10 ** 3
+ return (money // 10) + ((money % 10) // 5) + (money % 5)
+
+
+class TestCase(unittest.TestCase):
+
+ def test_all_amounts(self):
+ for m in range(10 ** 3):
+ self.assertEqual(reference(m), money_change(m), msg=f"Wrong answer for money={m}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T02/car_fueling.py b/L04/T02/car_fueling.py
new file mode 100644
index 0000000..11b9075
--- /dev/null
+++ b/L04/T02/car_fueling.py
@@ -0,0 +1,29 @@
+def compute_min_number_of_refills(d, m, stops):
+ assert 1 <= d <= 10 ** 5
+ assert 1 <= m <= 400
+ assert 1 <= len(stops) <= 300
+ assert 0 < stops[0] and all(stops[i] < stops[i + 1] for i in range(len(stops) - 1)) and stops[-1] < d
+
+ n = len(stops)
+ stops = [0] + stops + [d]
+ num_refills, cur_refill = 0, 0
+ while cur_refill <= n:
+ last_refill = cur_refill
+ while cur_refill <= n and stops[cur_refill + 1] - stops[last_refill] <= m:
+ cur_refill += 1
+ if cur_refill == last_refill:
+ return -1
+ if cur_refill <= n:
+ num_refills += 1
+
+ return num_refills
+
+
+if __name__ == '__main__':
+ input_d = int(input())
+ input_m = int(input())
+ input_n = int(input())
+ input_stops = list(map(int, input().split()))
+ assert len(input_stops) == input_n
+
+ print(compute_min_number_of_refills(input_d, input_m, input_stops))
diff --git a/L04/T02/logo.png b/L04/T02/logo.png
new file mode 100644
index 0000000..9a28756
Binary files /dev/null and b/L04/T02/logo.png differ
diff --git a/L04/T02/task-info.yaml b/L04/T02/task-info.yaml
new file mode 100644
index 0000000..e863ad5
--- /dev/null
+++ b/L04/T02/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Car Fueling
+files:
+ - name: car_fueling.py
+ visible: true
+ placeholders:
+ - offset: 247
+ length: 391
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/car_fueling_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 363
+ length: 25
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-3-greedy-algorithms/discussions
diff --git a/L04/T02/task.md b/L04/T02/task.md
new file mode 100644
index 0000000..ba5330a
--- /dev/null
+++ b/L04/T02/task.md
@@ -0,0 +1,15 @@
+# Car Fueling
+
+
+
+Compute the minimum number of tank refills to get from one city to another.
+
+Assuming that the distance between the cities is $1 \le d \le 10^5$ miles,
+a car can travel at most $1 \le m \le 400$ miles on a full tank,
+and there are $1 \le n \le 300$ gas stations at distances
+$stop_1,stop_2,\dotsc,stop_n$ along the way,
+output the minimum number of refills needed.
+Assume that the car starts with a full tank.
+If it is not possible to reach the destination, output −1.
+The distances to gas stations satisfy the inequalities
+$$0 < stop_1 < stop_2 < \dotsb < stop_n < d .$$
diff --git a/L04/T02/tests/car_fueling_unit_tests.py b/L04/T02/tests/car_fueling_unit_tests.py
new file mode 100644
index 0000000..aedfa03
--- /dev/null
+++ b/L04/T02/tests/car_fueling_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L04.T02.car_fueling import compute_min_number_of_refills
+
+
+class CarFueling(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for (d, m, stops, answer) in [
+ (950, 400, [200, 375, 550, 750], 2),
+ (10, 3, [1, 2, 5, 9], -1),
+ (200, 250, [100, 150], 0),
+ (200, 250, [100, 150], 0)
+ ]:
+ self.assertEqual(compute_min_number_of_refills(d, m, stops), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T02/tests/tests.py b/L04/T02/tests/tests.py
new file mode 100644
index 0000000..1bf899c
--- /dev/null
+++ b/L04/T02/tests/tests.py
@@ -0,0 +1,28 @@
+import unittest
+from L04.T02.car_fueling import compute_min_number_of_refills
+
+
+def reference(distance, tank, stops):
+ stops = [0] + stops + [distance]
+ num_refills, cur_refill = 0, 0
+ while cur_refill <= len(stops) - 2:
+ last_refill = cur_refill
+ while cur_refill <= len(stops) - 2 and stops[cur_refill + 1] - stops[last_refill] <= tank:
+ cur_refill += 1
+ if cur_refill == last_refill:
+ return -1
+ if cur_refill <= len(stops) - 2:
+ num_refills += 1
+
+ return num_refills
+
+
+class TestCase(unittest.TestCase):
+
+ def test_example_case(self):
+ d, m, stops = 500, 200, [100, 200, 300, 400]
+ self.assertEqual(reference(d, m, stops), compute_min_number_of_refills(d, m, stops), msg=f"Wrong answer for d={d}, m={m}, stops={stops}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T03/logo.png b/L04/T03/logo.png
new file mode 100644
index 0000000..def1338
Binary files /dev/null and b/L04/T03/logo.png differ
diff --git a/L04/T03/maximum_loot.py b/L04/T03/maximum_loot.py
new file mode 100644
index 0000000..396cbae
--- /dev/null
+++ b/L04/T03/maximum_loot.py
@@ -0,0 +1,34 @@
+from sys import stdin
+
+
+def maximum_loot_value(capacity, weights, prices):
+ assert 0 <= capacity <= 2 * 10 ** 6
+ assert len(weights) == len(prices)
+ assert 1 <= len(weights) <= 10 ** 3
+ assert all(0 < w <= 2 * 10 ** 6 for w in weights)
+ assert all(0 <= p <= 2 * 10 ** 6 for p in prices)
+
+ value = 0.
+ while capacity > 0:
+ ind = -1
+ for i in range(len(weights)):
+ if weights[i] > 0:
+ if ind == -1 or prices[i] / weights[i] > prices[ind] / weights[ind]:
+ ind = i
+ if ind == -1:
+ break
+ a = min(weights[ind], capacity)
+ value += prices[ind] / weights[ind] * a
+ capacity -= a
+ weights[ind] -= a
+
+ return value
+
+
+if __name__ == "__main__":
+ data = list(map(int, stdin.read().split()))
+ n, input_capacity = data[0:2]
+ input_prices = data[2:(2 * n + 2):2]
+ input_weights = data[3:(2 * n + 2):2]
+ opt_value = maximum_loot_value(input_capacity, input_weights, input_prices)
+ print("{:.10f}".format(opt_value))
diff --git a/L04/T03/task-info.yaml b/L04/T03/task-info.yaml
new file mode 100644
index 0000000..d679a35
--- /dev/null
+++ b/L04/T03/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Maximum Value of the Loot
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: maximum_loot.py
+ visible: true
+ placeholders:
+ - offset: 307
+ length: 427
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/maximum_loot_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 338
+ length: 41
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-3-greedy-algorithms/discussions
diff --git a/L04/T03/task.md b/L04/T03/task.md
new file mode 100644
index 0000000..19d76aa
--- /dev/null
+++ b/L04/T03/task.md
@@ -0,0 +1,36 @@
+
+
+Given the capacity of a backpack
+as well as the weights and prices
+of $n$ different compounds, compute the
+maximum total price of items that
+fit into the backpack of the given capacity.
+
+A thief breaks into a spice shop and finds
+four pounds of saffron, three pounds of
+vanilla, and five pounds of cinnamon. His backpack
+fits at most nine pounds, therefore he cannot
+take everything. Assuming that the prices of
+saffron, vanilla, and cinnamon are
+5000, 200, and 10 per pound respectively,
+what is the most valuable loot in this case?
+If the thief takes $u_1$ pounds of saffron,
+$u_2$ pounds of vanilla, and $u_3$ pounds of cinnamon,
+the total price of the loot is
+$5\,000 \cdot u_1 + 200 \cdot u_2 + 10\cdot u_3$.
+The thief would like to maximize the value of this expression
+subject to the following constraints:
+$u_1 \le 4$, $u_2 \le 3$, $u_3 \le 5$, $u_1+u_2+u_3 \le 9$.
+
+# Running locally
+
+If you run this task locally and enter the input manually, note that it uses `stdin.read()`. This means the program reads input until EOF.
+
+After entering the input, send EOF:
+- for **Windows** `Ctrl + Z`, then `Enter`
+- for **macOS**
+ - in PyCharm `Cmd + D`
+ - in terminal `Ctrl + D`
+- for **Linux** `Ctrl + D`
+
+You may see `stdin.read()` in some future tasks as well, so the same approach will apply there.
\ No newline at end of file
diff --git a/L04/T03/tests/maximum_loot_unit_tests.py b/L04/T03/tests/maximum_loot_unit_tests.py
new file mode 100644
index 0000000..103d420
--- /dev/null
+++ b/L04/T03/tests/maximum_loot_unit_tests.py
@@ -0,0 +1,21 @@
+import unittest
+from L04.T03.maximum_loot import maximum_loot_value
+
+
+class TestMaximumLoot(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for (capacity, weights, prices, answer) in [
+ (50, [20, 50, 30], [60, 100, 120], 180.0),
+ (10, [30], [500], 500/3),
+ (50, [20, 50, 30], [60, 100, 120], 180.0)
+ ]:
+ self.assertAlmostEqual(
+ maximum_loot_value(capacity, weights, prices),
+ answer,
+ delta=1e-03
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T03/tests/tests.py b/L04/T03/tests/tests.py
new file mode 100644
index 0000000..ee46fd8
--- /dev/null
+++ b/L04/T03/tests/tests.py
@@ -0,0 +1,18 @@
+import math
+import unittest
+from L04.T03.maximum_loot import maximum_loot_value
+
+
+class TestCase(unittest.TestCase):
+
+ def test_large_example(self):
+ capacity = 12619
+ prices = [1347, 3494, 4257, 2741, 5711, 1562, 4116, 7471, 5833, 5989, 416, 5515, 2016, 2442, 3905, 3404, 9100, 6207, 1644, 2240, 5231, 1335, 673, 393, 6964, 5535, 6252, 89, 8073, 7775, 2053, 2226, 3563, 693, 9315, 5007, 1829, 9173, 8908, 2625, 5008, 7867, 7532, 7824, 435, 4403, 6028, 5586, 5289, 5541, 2234, 4306, 2299, 8374, 4343, 7359, 8939, 6261, 7100, 8205, 982, 6358, 3990, 570, 2200, 4519, 5251, 5209, 1239, 6045, 8871, 3897, 3756, 143, 7592, 7106, 4762, 1218, 2520, 5230, 1403, 8540, 3841, 6265, 908, 5881, 3717, 8574, 8037, 3239, 4442, 6518, 6564, 4138, 4041, 7052, 2738, 1924, 3326, 6057]
+ weights = [8254, 9484, 2934, 7521, 5054, 4629, 3648, 2124, 3039, 8661, 9916, 3740, 8876, 3138, 7136, 9804, 2398, 7744, 9785, 5399, 5351, 7907, 8511, 5524, 9136, 5894, 118, 5494, 87, 6297, 4437, 7777, 3549, 2956, 5477, 5542, 7290, 3291, 3019, 7229, 7430, 4829, 7996, 5708, 6681, 7195, 4428, 4293, 7030, 857, 5725, 6936, 7497, 3161, 7268, 9172, 2888, 6389, 7087, 4136, 147, 5791, 1100, 9317, 3256, 3074, 9226, 1652, 4033, 1004, 9477, 2245, 1859, 8303, 3872, 9526, 1678, 5881, 5272, 7923, 6299, 9238, 2039, 7903, 4283, 7011, 6773, 5543, 8704, 1786, 5498, 9117, 7320, 2403, 4692, 4411, 8, 272, 5588, 8540]
+ expected = 66152.572
+ result = maximum_loot_value(capacity, weights, prices)
+ self.assertTrue(math.fabs(result - expected) < 1e-03, msg=f"Wrong answer for capacity={capacity}, weights={weights}, prices={prices}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T04/logo.png b/L04/T04/logo.png
new file mode 100644
index 0000000..ad28d23
Binary files /dev/null and b/L04/T04/logo.png differ
diff --git a/L04/T04/maximum_ad_revenue.py b/L04/T04/maximum_ad_revenue.py
new file mode 100644
index 0000000..b6992dd
--- /dev/null
+++ b/L04/T04/maximum_ad_revenue.py
@@ -0,0 +1,34 @@
+from itertools import permutations
+
+
+def max_dot_product_naive(first_sequence, second_sequence):
+ assert len(first_sequence) == len(second_sequence)
+ assert len(first_sequence) <= 10 ** 3
+ assert all(0 <= f <= 10 ** 5 for f in first_sequence)
+ assert all(0 <= s <= 10 ** 5 for s in second_sequence)
+
+ max_product = 0
+ for permutation in permutations(second_sequence):
+ dot_product = sum(first_sequence[i] * permutation[i] for i in range(len(first_sequence)))
+ max_product = max(max_product, dot_product)
+
+ return max_product
+
+
+def max_dot_product(first_sequence, second_sequence):
+ assert len(first_sequence) == len(second_sequence)
+ assert len(first_sequence) <= 10 ** 3
+ assert all(0 <= f <= 10 ** 5 for f in first_sequence)
+ assert all(0 <= s <= 10 ** 5 for s in second_sequence)
+
+ a = sorted(first_sequence)
+ b = sorted(second_sequence)
+ return sum(a[i] * b[i] for i in range(len(a)))
+
+
+if __name__ == '__main__':
+ n = int(input())
+ prices = list(map(int, input().split()))
+ clicks = list(map(int, input().split()))
+ assert len(prices) == len(clicks) == n
+ print(max_dot_product(prices, clicks))
diff --git a/L04/T04/task-info.yaml b/L04/T04/task-info.yaml
new file mode 100644
index 0000000..2bd6e67
--- /dev/null
+++ b/L04/T04/task-info.yaml
@@ -0,0 +1,24 @@
+type: edu
+custom_name: Maximum Advertisement Revenue
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: maximum_ad_revenue.py
+ visible: true
+ placeholders:
+ - offset: 835
+ length: 109
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/maximum_ad_revenue_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 499
+ length: 17
+ placeholder_text: None
+ - offset: 950
+ length: 54
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-3-greedy-algorithms/discussions
diff --git a/L04/T04/task.md b/L04/T04/task.md
new file mode 100644
index 0000000..f7241d8
--- /dev/null
+++ b/L04/T04/task.md
@@ -0,0 +1,23 @@
+# Maximum Advertisement Revenue
+
+
+
+You have $n=3$ advertisement slots on your popular Internet page and
+you want to sell them to advertisers. They expect, respectively,
+${clicks}_1=10$,
+${clicks}_2=20$, and ${clicks}_3=30$ clicks per day. You found three advertisers
+willing to pay ${price}_1=2$, ${price}_2=3$, and ${price}_3=5$ per click.
+How would you pair the slots and advertisers? For example, the blue pairing
+gives a revenue of $10 \cdot 5 + 20 \cdot 2 + 30 \cdot 3 = 180$ dollars, while
+the black one results in revenue of $10\cdot 3 + 20 \cdot 5 + 30 \cdot 2=190$ dollars.
+
+
+Find the maximum dot product of two
+sequences of numbers.
+* Input: Two sequences of $n$ positive
+integers: $price_1,\dots,price_n$
+and $clicks_1,\dots,clicks_n$.
+* Output: The maximum value of
+$price_1 \cdot c_1 + \dots+price_n \cdot c_n$,
+where $c_1,\dots,c_n$ is a
+permutation of $clicks_1,\dots,clicks_n$.
diff --git a/L04/T04/tests/maximum_ad_revenue_unit_tests.py b/L04/T04/tests/maximum_ad_revenue_unit_tests.py
new file mode 100644
index 0000000..f798111
--- /dev/null
+++ b/L04/T04/tests/maximum_ad_revenue_unit_tests.py
@@ -0,0 +1,32 @@
+import unittest
+from L04.T04.maximum_ad_revenue import max_dot_product, max_dot_product_naive
+
+
+class TestMaxDotProduct(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for (first_sequence, second_sequence) in [
+ ([1], [2]),
+ ([2], [1]),
+ ([1], [1]),
+ ([1, 2], [5, 10]),
+ ([2, 1], [5, 10]),
+ ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]),
+ ([17, 12, 20], [19, 2, 3]),
+ ([1, 2], [5, 10])
+ ]:
+ self.assertEqual(
+ max_dot_product(list(first_sequence), list(second_sequence)),
+ max_dot_product_naive(first_sequence, second_sequence)
+ )
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ n = 10 ** 3
+ self.assertEqual(max_dot_product([0] * n, [0] * n), 0)
+ self.assertEqual(max_dot_product([1] * n, [1] * n), n)
+ self.assertEqual(max_dot_product([0] * n, [0] * n), 0)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T04/tests/tests.py b/L04/T04/tests/tests.py
new file mode 100644
index 0000000..d0c4e50
--- /dev/null
+++ b/L04/T04/tests/tests.py
@@ -0,0 +1,34 @@
+import unittest
+from random import randint
+from L04.T04.maximum_ad_revenue import max_dot_product
+
+
+def reference(a, b):
+ a = sorted(a)
+ b = sorted(b)
+ return sum(a[i] * b[i] for i in range(len(a)))
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_10(self):
+ n = 10
+ a = [randint(0, 10 ** 5) for _ in range(n)]
+ b = [randint(0, 10 ** 5) for _ in range(n)]
+ self.assertEqual(reference(a, b), max_dot_product(a, b), msg=f"Wrong answer for a={a}, b={b}")
+
+ def test_n_20(self):
+ n = 20
+ a = [randint(0, 10 ** 5) for _ in range(n)]
+ b = [randint(0, 10 ** 5) for _ in range(n)]
+ self.assertEqual(reference(a, b), max_dot_product(a, b), msg=f"Wrong answer for a={a}, b={b}")
+
+ def test_n_30(self):
+ n = 30
+ a = [randint(0, 10 ** 5) for _ in range(n)]
+ b = [randint(0, 10 ** 5) for _ in range(n)]
+ self.assertEqual(reference(a, b), max_dot_product(a, b), msg=f"Wrong answer for a={a}, b={b}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T05/collecting_signatures.py b/L04/T05/collecting_signatures.py
new file mode 100644
index 0000000..3a79cc6
--- /dev/null
+++ b/L04/T05/collecting_signatures.py
@@ -0,0 +1,24 @@
+from collections import namedtuple
+from sys import stdin
+
+Segment = namedtuple('Segment', 'start end')
+
+
+def compute_optimal_points(segments):
+ points = []
+ segments = sorted(segments, key=lambda x: x.end)
+ limit = -1
+ for segment in segments:
+ if limit < segment.start:
+ limit = segment.end
+ points.append(segment.end)
+ return points
+
+
+if __name__ == '__main__':
+ n, *data = map(int, stdin.read().split())
+ input_segments = list(map(lambda x: Segment(x[0], x[1]), zip(data[::2], data[1::2])))
+ assert n == len(input_segments)
+ output_points = compute_optimal_points(input_segments)
+ print(len(output_points))
+ print(*output_points)
diff --git a/L04/T05/logo.png b/L04/T05/logo.png
new file mode 100644
index 0000000..b6f974a
Binary files /dev/null and b/L04/T05/logo.png differ
diff --git a/L04/T05/task-info.yaml b/L04/T05/task-info.yaml
new file mode 100644
index 0000000..622129d
--- /dev/null
+++ b/L04/T05/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Collecting Signatures
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+ - name: collecting_signatures.py
+ visible: true
+ placeholders:
+ - offset: 147
+ length: 231
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/collecting_signatures_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 398
+ length: 50
+ placeholder_text: None
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-3-greedy-algorithms/discussions
diff --git a/L04/T05/task.md b/L04/T05/task.md
new file mode 100644
index 0000000..4b3b1f0
--- /dev/null
+++ b/L04/T05/task.md
@@ -0,0 +1,27 @@
+# Collecting Signatures
+
+
+
+You are responsible for collecting signatures from all tenants in a building.
+For each tenant, you know a period of time when he or she is at home.
+You would like to collect all signatures by visiting the building as
+few times as possible. For simplicity, we assume that when you enter
+the building, you instantly collect the signatures of all tenants that
+are in the building at that time.
+
+**Input**: A sequence of $n \le 10^3$ segments
+$[l_1,r_1],...,[l_n,r_n]$ on a line.
+
+**Output**: A set of points of minimum size
+such that each segment $[l_i,r_i]$ contains a point,
+i.e., there exists a point $x$ such that $l_i \le x \le r_i$.
+
+
+
+A detailed solution for this programming challenge is covered in the companion MOOCBook. But we strongly encourage you to do your best to solve the challenge yourself before looking into the book! There are at least three good reasons for this.
+
+
By solving this challenge, you practice solving algorithmic problems similar to those given at technical interviews.
+
The satisfaction and self confidence that you get when passing the grader is priceless =)
+
Even if you fail to pass the grader yourself, the time will not be lost as you will better understand the solution from the book and better appreciate the beauty of the underlying ideas.
+
+You are organizing a competition for
+children and have $n$ candies to give as prizes.
+You would like to use these candies for top $k$ places
+in a competition with a restriction that a higher place gets
+a larger number of candies. To make as many children happy
+as possible, you need to find the largest value of $k$ for
+which it is possible.
+
+**Input**: An integer $1 \le n \le 10^9$.
+
+**Output**: The maximum
+number $k$ such that $n$ can be represented as the sum of $k$ pairwise
+distinct positive integers and these integers (if there are many such
+representations, output any of them).
diff --git a/L04/T06/tests/maximum_number_of_prizes_unit_tests.py b/L04/T06/tests/maximum_number_of_prizes_unit_tests.py
new file mode 100644
index 0000000..f007eed
--- /dev/null
+++ b/L04/T06/tests/maximum_number_of_prizes_unit_tests.py
@@ -0,0 +1,17 @@
+import unittest
+from L04.T06.maximum_number_of_prizes import compute_optimal_summands
+
+
+class MaximumNumberOfPrizes(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for (n, answer) in [(1, 1), (6, 3), (100, 13), (200, 19)]:
+ summands = compute_optimal_summands(n)
+ self.assertEqual(len(summands), answer)
+ self.assertEqual(sum(summands), n)
+ summands = sorted(summands)
+ self.assertTrue(all(summands[i] < summands[i + 1] for i in range(len(summands) - 1)))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T06/tests/tests.py b/L04/T06/tests/tests.py
new file mode 100644
index 0000000..d57a15c
--- /dev/null
+++ b/L04/T06/tests/tests.py
@@ -0,0 +1,37 @@
+import unittest
+from L04.T06.maximum_number_of_prizes import compute_optimal_summands
+
+
+def reference(n):
+ summands = []
+
+ k = 1
+ while n >= k + k + 1:
+ summands.append(k)
+ n -= k
+ k += 1
+ summands.append(n)
+
+ return summands
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_2(self):
+ self.assertEqual(reference(2), compute_optimal_summands(2), msg="Wrong answer for n=2")
+
+ def test_n_7(self):
+ self.assertEqual(reference(7), compute_optimal_summands(7), msg="Wrong answer for n=7")
+
+ def test_n_20(self):
+ self.assertEqual(reference(20), compute_optimal_summands(20), msg="Wrong answer for n=20")
+
+ def test_n_239(self):
+ self.assertEqual(reference(239), compute_optimal_summands(239), msg="Wrong answer for n=239")
+
+ def test_n_317(self):
+ self.assertEqual(reference(317), compute_optimal_summands(317), msg="Wrong answer for n=317")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T07/logo.png b/L04/T07/logo.png
new file mode 100644
index 0000000..15edd4b
Binary files /dev/null and b/L04/T07/logo.png differ
diff --git a/L04/T07/maximum_salary.py b/L04/T07/maximum_salary.py
new file mode 100644
index 0000000..6eaf262
--- /dev/null
+++ b/L04/T07/maximum_salary.py
@@ -0,0 +1,32 @@
+from itertools import permutations
+
+
+def largest_number_naive(numbers):
+ numbers = list(map(str, numbers))
+
+ largest = 0
+
+ for permutation in permutations(numbers):
+ largest = max(largest, int("".join(permutation)))
+
+ return largest
+
+
+def largest_number(numbers):
+ numbers = list(map(str, numbers))
+
+ for _ in numbers:
+ for i in range(len(numbers) - 1):
+ if numbers[i] + numbers[i + 1] < numbers[i + 1] + numbers[i]:
+ t = numbers[i]
+ numbers[i] = numbers[i + 1]
+ numbers[i + 1] = t
+
+ return int("".join(numbers))
+
+
+if __name__ == '__main__':
+ n = int(input())
+ input_numbers = input().split()
+ assert len(input_numbers) == n
+ print(largest_number(input_numbers))
diff --git a/L04/T07/task-info.yaml b/L04/T07/task-info.yaml
new file mode 100644
index 0000000..efc87d9
--- /dev/null
+++ b/L04/T07/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Maximum Salary
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: maximum_salary.py
+ visible: true
+ placeholders:
+ - offset: 287
+ length: 316
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/maximum_salary_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 309
+ length: 7
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/kAiGl/programming-assignment-3-greedy-algorithms/discussions
diff --git a/L04/T07/task.md b/L04/T07/task.md
new file mode 100644
index 0000000..7f3961b
--- /dev/null
+++ b/L04/T07/task.md
@@ -0,0 +1,21 @@
+# Maximum Salary
+
+
+
+This is probably the most important problem
+in this course :).
+As the last question of an interview,
+your future boss gives you a few pieces of
+paper with a single number written on
+each of them and asks you to compose
+a largest number from these numbers.
+The resulting number is going to be
+your salary, so you are very motivated to
+solve this problem!
+
+**Input**: Integers
+$1 \le a_1, a_2, \dotsc, a_n \le 10^3$, where
+$1 \le n \le 100$.
+
+**Output**: The largest number that
+can be composed out of $a_1, \dotsc, a_n$.
diff --git a/L04/T07/tests/maximum_salary_unit_tests.py b/L04/T07/tests/maximum_salary_unit_tests.py
new file mode 100644
index 0000000..c373e7e
--- /dev/null
+++ b/L04/T07/tests/maximum_salary_unit_tests.py
@@ -0,0 +1,31 @@
+import unittest
+from random import randint
+from L04.T07.maximum_salary import largest_number_naive, largest_number
+
+
+class TestLargestNumber(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for numbers in [
+ [1],
+ [1, 2],
+ [7, 8],
+ [1, 12],
+ [2, 12],
+ [2, 21],
+ [2, 21, 23, 211, 213, 231, 232]
+ ]:
+ self.assertEqual(largest_number(numbers),
+ largest_number_naive(numbers))
+
+ def test_random(self):
+ for n in range(2, 7):
+ for max_value in [10, 20, 100, 1000]:
+ for _ in range(10):
+ numbers = [randint(1, max_value) for _ in range(n)]
+ self.assertEqual(largest_number(numbers),
+ largest_number_naive(numbers))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/T07/tests/tests.py b/L04/T07/tests/tests.py
new file mode 100644
index 0000000..f46ca6b
--- /dev/null
+++ b/L04/T07/tests/tests.py
@@ -0,0 +1,30 @@
+import unittest
+from L04.T07.maximum_salary import largest_number
+
+
+def reference(numbers):
+ numbers = list(map(str, numbers))
+
+ for _ in numbers:
+ for i in range(len(numbers) - 1):
+ if numbers[i] + numbers[i + 1] < numbers[i + 1] + numbers[i]:
+ t = numbers[i]
+ numbers[i] = numbers[i + 1]
+ numbers[i + 1] = t
+
+ return int("".join(numbers))
+
+
+class TestCase(unittest.TestCase):
+
+ def test_case_1(self):
+ numbers = [2, 21, 23, 211, 213, 231, 232]
+ self.assertEqual(reference(numbers), largest_number(numbers), msg=f"Wrong answer for n={numbers}")
+
+ def test_case_2(self):
+ numbers = [56, 5, 6, 556, 566, 666, 665, 656]
+ self.assertEqual(reference(numbers), largest_number(numbers), msg=f"Wrong answer for n={numbers}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L04/lesson-info.yaml b/L04/lesson-info.yaml
new file mode 100644
index 0000000..e7b5360
--- /dev/null
+++ b/L04/lesson-info.yaml
@@ -0,0 +1,9 @@
+custom_name: Greedy Algorithms
+content:
+ - T01
+ - T02
+ - T03
+ - T04
+ - T05
+ - T06
+ - T07
diff --git a/L05/T01/binary_search.py b/L05/T01/binary_search.py
new file mode 100644
index 0000000..53319a9
--- /dev/null
+++ b/L05/T01/binary_search.py
@@ -0,0 +1,31 @@
+def linear_search(keys, query):
+ for i in range(len(keys)):
+ if keys[i] == query:
+ return i
+
+ return -1
+
+
+def binary_search(keys, query):
+ assert all(keys[i] < keys[i + 1] for i in range(len(keys) - 1))
+ assert 1 <= len(keys) <= 3 * 10 ** 4
+
+ left, right = 0, len(keys)
+ while left + 1 < right:
+ ave = (left + right) // 2
+ if keys[ave] <= query:
+ left = ave
+ else:
+ right = ave
+ if keys[left] != query:
+ return -1
+ else:
+ return left
+
+
+if __name__ == '__main__':
+ input_keys = list(map(int, input().split()))[1:]
+ input_queries = list(map(int, input().split()))[1:]
+
+ for q in input_queries:
+ print(binary_search(input_keys, q), end=' ')
diff --git a/L05/T01/logo.png b/L05/T01/logo.png
new file mode 100644
index 0000000..6e5a48e
Binary files /dev/null and b/L05/T01/logo.png differ
diff --git a/L05/T01/task-info.yaml b/L05/T01/task-info.yaml
new file mode 100644
index 0000000..216bf69
--- /dev/null
+++ b/L05/T01/task-info.yaml
@@ -0,0 +1,24 @@
+type: edu
+custom_name: Binary Search
+files:
+ - name: binary_search.py
+ visible: true
+ placeholders:
+ - offset: 276
+ length: 256
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/binary_search_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 297
+ length: 15
+ placeholder_text: None
+ - offset: 630
+ length: 28
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/w9YDz/programming-assignment-4-divide-and-conquer/discussions
diff --git a/L05/T01/task.md b/L05/T01/task.md
new file mode 100644
index 0000000..af58601
--- /dev/null
+++ b/L05/T01/task.md
@@ -0,0 +1,12 @@
+# Binary Search
+
+
+
+**Input:** A sorted array $K=[k_0, \dotsc, k_{n-1}]$
+of $1 \le n \le 3 \cdot 10^4$ distinct integers and
+an array $Q=\{q_0,\dotsc,q_{m-1}\}$ of
+$1 \le m \le 10^5$ integers.
+
+**Output:** For all $i$ from $0$ to $m-1$,
+output an index $0 \le j \le n-1$ such
+that $k_j=q_i$ or $-1$, if there is no such index.
diff --git a/L05/T01/tests/binary_search_unit_tests.py b/L05/T01/tests/binary_search_unit_tests.py
new file mode 100644
index 0000000..f13a61c
--- /dev/null
+++ b/L05/T01/tests/binary_search_unit_tests.py
@@ -0,0 +1,29 @@
+import unittest
+from L05.T01.binary_search import binary_search, linear_search
+
+
+class TestBinarySearch(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for (keys, query) in [
+ ([1, 2, 3], 1),
+ ([4, 5, 6], 7),
+ ([7, 8, 9], 1),
+ ]:
+ self.assertEqual(
+ linear_search(keys, query),
+ binary_search(keys, query)
+ )
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for (keys, query, answer) in [
+ (list(range(10 ** 4)), 10 ** 4, -1),
+ (list(range(10 ** 4)), 0, 0),
+ (list(range(10 ** 4)), 239, 239),
+ ]:
+ self.assertEqual(binary_search(keys, query), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T01/tests/tests.py b/L05/T01/tests/tests.py
new file mode 100644
index 0000000..9d66e37
--- /dev/null
+++ b/L05/T01/tests/tests.py
@@ -0,0 +1,41 @@
+import unittest
+from L05.T01.binary_search import binary_search
+from random import randrange
+
+
+def reference(keys, query):
+ assert all(keys[i] < keys[i + 1] for i in range(len(keys) - 1))
+ assert 1 <= len(keys) <= 10 ** 4
+
+ left, right = 0, len(keys)
+ while left + 1 < right:
+ ave = (left + right) // 2
+ if keys[ave] <= query:
+ left = ave
+ else:
+ right = ave
+ if keys[left] != query:
+ return -1
+ else:
+ return left
+
+
+class TestCase(unittest.TestCase):
+
+ def test_random_cases(self):
+ for _ in range(20):
+ keys = set()
+ for i in range(100):
+ x = randrange(239239) + 1
+ while x in keys:
+ x = randrange(239239) + 1
+ keys.add(x)
+ keys = sorted(list(keys))
+
+ for key in keys:
+ for query in (key, key + 1):
+ self.assertEqual(reference(keys, query), binary_search(keys, query), msg=f"Wrong answer for keys={keys}, query={query}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T02/logo.png b/L05/T02/logo.png
new file mode 100644
index 0000000..f2c25d6
Binary files /dev/null and b/L05/T02/logo.png differ
diff --git a/L05/T02/majority_element.py b/L05/T02/majority_element.py
new file mode 100644
index 0000000..4a1d314
--- /dev/null
+++ b/L05/T02/majority_element.py
@@ -0,0 +1,30 @@
+def majority_element_naive(elements):
+ assert len(elements) <= 10 ** 5
+ for e in elements:
+ if elements.count(e) > len(elements) / 2:
+ return 1
+
+ return 0
+
+
+def majority_element(elements):
+ assert len(elements) <= 10 ** 5
+ table = {}
+ for e in elements:
+ if e in table:
+ table[e] += 1
+ else:
+ table[e] = 1
+
+ for e in table:
+ if table[e] > len(elements) / 2:
+ return 1
+
+ return 0
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ input_elements = list(map(int, input().split()))
+ assert len(input_elements) == input_n
+ print(majority_element(input_elements))
diff --git a/L05/T02/task-info.yaml b/L05/T02/task-info.yaml
new file mode 100644
index 0000000..f483876
--- /dev/null
+++ b/L05/T02/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Majority Element
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: majority_element.py
+ visible: true
+ placeholders:
+ - offset: 256
+ length: 218
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/majority_element_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 352
+ length: 13
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/w9YDz/programming-assignment-4-divide-and-conquer/discussions
diff --git a/L05/T02/task.md b/L05/T02/task.md
new file mode 100644
index 0000000..f15df75
--- /dev/null
+++ b/L05/T02/task.md
@@ -0,0 +1,11 @@
+# Majority Element
+
+
+
+**Input.** A sequence of $n \le 10^5$ integers.
+
+**Output.** 1, if there is an element
+that is repeated more than $n/2$ times,
+and 0 otherwise.
+
+As you might have already guessed, this problem can be solved by the divide-and-conquer algorithm in time $O(n\log n)$. Indeed, if a sequence of length $n$ contains a majority element, then the same element is also a majority element for one of its halves. Thus, to solve this problem you first split a given sequence into halves and recursively solve it for each half. Do you see how to combine the results of two recursive calls?
diff --git a/L05/T02/tests/majority_element_unit_tests.py b/L05/T02/tests/majority_element_unit_tests.py
new file mode 100644
index 0000000..68e662f
--- /dev/null
+++ b/L05/T02/tests/majority_element_unit_tests.py
@@ -0,0 +1,31 @@
+import unittest
+from L05.T02.majority_element import majority_element, majority_element_naive
+
+
+class TestMajorityElement(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for elements in [
+ [7, 2, 7],
+ [7, 8, 9],
+ [2, 3, 2, 3],
+ [1, 2, 3, 4],
+ [1, 4, 1, 1],
+ ]:
+ self.assertEqual(
+ majority_element(list(elements)),
+ majority_element_naive(elements)
+ )
+
+ def test_large(self):
+ for (elements, answer) in [
+ ([0] * 5000 + [1] * 5000, 0)
+ ]:
+ self.assertEqual(
+ majority_element(elements),
+ answer
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T02/tests/tests.py b/L05/T02/tests/tests.py
new file mode 100644
index 0000000..519a136
--- /dev/null
+++ b/L05/T02/tests/tests.py
@@ -0,0 +1,36 @@
+import unittest
+from L05.T02.majority_element import majority_element
+
+
+def reference(elements):
+ table = {}
+ for e in elements:
+ if e in table:
+ table[e] += 1
+ else:
+ table[e] = 1
+
+ for e in table:
+ if table[e] > len(elements) / 2:
+ return 1
+
+ return 0
+
+
+class TestCase(unittest.TestCase):
+
+ def test_majority_exists_1(self):
+ elements, answer = [1, 1, 2], 1
+ self.assertEqual(answer, majority_element(elements), msg=f"Wrong answer for elements={elements}")
+
+ def test_no_majority(self):
+ elements, answer = [1, 2], 0
+ self.assertEqual(answer, majority_element(elements), msg=f"Wrong answer for elements={elements}")
+
+ def test_majority_exists_2(self):
+ elements, answer = [7, 8, 7], 1
+ self.assertEqual(answer, majority_element(elements), msg=f"Wrong answer for elements={elements}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T03/logo.png b/L05/T03/logo.png
new file mode 100644
index 0000000..2441f27
Binary files /dev/null and b/L05/T03/logo.png differ
diff --git a/L05/T03/quicksort.py b/L05/T03/quicksort.py
new file mode 100644
index 0000000..a38cd6d
--- /dev/null
+++ b/L05/T03/quicksort.py
@@ -0,0 +1,35 @@
+from random import randint
+
+
+def partition3(array, left, right):
+ pivot = array[left]
+ j = left
+ k = left + 1
+ for i in range(left + 1, right + 1):
+ if array[i] == pivot:
+ array[i], array[k] = array[k], array[i]
+ k += 1
+ elif array[i] < pivot:
+ array[i], array[j] = array[j], array[i]
+ j += 1
+ array[i], array[k] = array[k], array[i]
+ k += 1
+ return j, k - 1
+
+
+def randomized_quick_sort(array, left, right):
+ if left >= right:
+ return
+ k = randint(left, right)
+ array[left], array[k] = array[k], array[left]
+ m1, m2 = partition3(array, left, right)
+ randomized_quick_sort(array, left, m1 - 1)
+ randomized_quick_sort(array, m2 + 1, right)
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ elements = list(map(int, input().split()))
+ assert len(elements) == input_n
+ randomized_quick_sort(elements, 0, len(elements) - 1)
+ print(*elements)
diff --git a/L05/T03/task-info.yaml b/L05/T03/task-info.yaml
new file mode 100644
index 0000000..24d6440
--- /dev/null
+++ b/L05/T03/task-info.yaml
@@ -0,0 +1,24 @@
+type: edu
+custom_name: Improving QuickSort
+files:
+ - name: quicksort.py
+ visible: true
+ placeholders:
+ - offset: 69
+ length: 384
+ placeholder_text: "# TODO: write your code here"
+ - offset: 623
+ length: 134
+ placeholder_text: "# TODO: call partition3, then make two recursive calls to randomized_quick_sort"
+ - name: tests/quicksort_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 296
+ length: 12
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+ - name: logo.png
+ visible: false
+ is_binary: true
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/w9YDz/programming-assignment-4-divide-and-conquer/discussions
diff --git a/L05/T03/task.md b/L05/T03/task.md
new file mode 100644
index 0000000..f6ef1d1
--- /dev/null
+++ b/L05/T03/task.md
@@ -0,0 +1,7 @@
+# Improving QuickSort
+
+
+
+Modify the given implementation of the QuickSort algorithm
+so that it works fast even on sequences containing
+many identical elements.
diff --git a/L05/T03/tests/quicksort_unit_tests.py b/L05/T03/tests/quicksort_unit_tests.py
new file mode 100644
index 0000000..77080c4
--- /dev/null
+++ b/L05/T03/tests/quicksort_unit_tests.py
@@ -0,0 +1,28 @@
+import unittest
+from L05.T03.quicksort import randomized_quick_sort
+from random import randint
+
+
+class TestQuickSort(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for array in [
+ ([1, 2, 3]),
+ ([3, 2, 1]),
+ ([2, 3, 1]),
+ ]:
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(array, sorted_array)
+
+ def test_large(self):
+ for n in (10, 100, 10 ** 5):
+ for max_value in (1, 2, 10, 10 ** 5):
+ array = [randint(0, max_value) for _ in range(n)]
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(array, sorted_array)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T03/tests/tests.py b/L05/T03/tests/tests.py
new file mode 100644
index 0000000..c878a83
--- /dev/null
+++ b/L05/T03/tests/tests.py
@@ -0,0 +1,45 @@
+import unittest
+from L05.T03.quicksort import randomized_quick_sort
+from random import randint
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_3(self):
+ n = 3
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(sorted_array, array, msg=f"Wrong answer for array={array}")
+
+ def test_n_4(self):
+ n = 4
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(sorted_array, array, msg=f"Wrong answer for array={array}")
+
+ def test_n_5(self):
+ n = 5
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(sorted_array, array, msg=f"Wrong answer for array={array}")
+
+ def test_n_10(self):
+ n = 10
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(sorted_array, array, msg=f"Wrong answer for array={array}")
+
+ def test_n_100(self):
+ n = 100
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ sorted_array = sorted(list(array))
+ randomized_quick_sort(array, 0, len(array) - 1)
+ self.assertEqual(sorted_array, array, msg=f"Wrong answer for array={array}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T04/logo.png b/L05/T04/logo.png
new file mode 100644
index 0000000..509ac9d
Binary files /dev/null and b/L05/T04/logo.png differ
diff --git a/L05/T04/number_of_inversions.py b/L05/T04/number_of_inversions.py
new file mode 100644
index 0000000..a71a5ee
--- /dev/null
+++ b/L05/T04/number_of_inversions.py
@@ -0,0 +1,24 @@
+from itertools import combinations
+
+
+def compute_inversions_naive(a):
+ number_of_inversions = 0
+ for i, j in combinations(range(len(a)), 2):
+ if a[i] > a[j]:
+ number_of_inversions += 1
+ return number_of_inversions
+
+
+def compute_inversions(a):
+ number_of_inversions = 0
+ for i, j in combinations(range(len(a)), 2):
+ if a[i] > a[j]:
+ number_of_inversions += 1
+ return number_of_inversions
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ elements = list(map(int, input().split()))
+ assert len(elements) == input_n
+ print(compute_inversions(elements))
diff --git a/L05/T04/task-info.yaml b/L05/T04/task-info.yaml
new file mode 100644
index 0000000..d8ff96d
--- /dev/null
+++ b/L05/T04/task-info.yaml
@@ -0,0 +1,24 @@
+type: edu
+custom_name: Number of Inversions
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: number_of_inversions.py
+ visible: true
+ placeholders:
+ - offset: 274
+ length: 166
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/number_of_inversions_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 339
+ length: 12
+ placeholder_text: None
+ - offset: 873
+ length: 67
+ placeholder_text: "# TODO: Check reverse-ordered array"
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/w9YDz/programming-assignment-4-divide-and-conquer/discussions
diff --git a/L05/T04/task.md b/L05/T04/task.md
new file mode 100644
index 0000000..ddb199f
--- /dev/null
+++ b/L05/T04/task.md
@@ -0,0 +1,11 @@
+# Number of Inversions
+
+
+
+The number of inversions in a sequence measures how close the sequence is to being sorted. For example, a sequence sorted in the non-descending order contains no inversions, while a sequence sorted in the descending order contains $n(n-1)/2$ inversions (every two elements form an inversion).
+
+A naive algorithm for the Number of Inversions Problem goes through all possible pairs $(i,j)$ and has running time $O(n^2)$. To solve this problem in time $O(n\log n)$ using the divide-and-conquer technique split the input array
+into two halves and make a recursive call on both halves. What remains to be done is computing the number of inversions formed by two elements from different halves. If we do this naively, this will bring us back to $O(n^2)$ running time, since the total number of such pairs is $\frac{n}{2} \cdot \frac{n}{2}=\frac{n^2}{4}=O(n^2)$. It turns out that one can compute the number of inversions formed by two elements from different halves in time $O(n)$, if both halves are already sorted. This suggest that instead of solving the original problem we solve a more general problem: compute the number of inversions in the given array and sort it at the same time.
+
+Compute the number of inversions in
+a sequence of length at most $30000$.
diff --git a/L05/T04/tests/number_of_inversions_unit_tests.py b/L05/T04/tests/number_of_inversions_unit_tests.py
new file mode 100644
index 0000000..d3f1dfb
--- /dev/null
+++ b/L05/T04/tests/number_of_inversions_unit_tests.py
@@ -0,0 +1,29 @@
+import unittest
+from L05.T04.number_of_inversions import compute_inversions, compute_inversions_naive
+from random import randint
+
+
+class TestNumberOfInversions(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for array in [
+ ([1, 2, 3]),
+ ([3, 2, 1]),
+ ([2, 3, 1]),
+ ]:
+ self.assertEqual(compute_inversions(array),
+ compute_inversions_naive(array))
+
+ def test_random(self):
+ for n in (10, 100):
+ for max_value in (1, 2, 10, 10 ** 5):
+ array = [randint(0, max_value) for _ in range(n)]
+ self.assertEqual(compute_inversions(array),
+ compute_inversions_naive(array))
+
+ def test_large(self):
+ self.assertEqual(compute_inversions([1] * 100), 0)
+ self.assertEqual(compute_inversions(list(range(100, 0, -1))), 4950)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T04/tests/tests.py b/L05/T04/tests/tests.py
new file mode 100644
index 0000000..053b044
--- /dev/null
+++ b/L05/T04/tests/tests.py
@@ -0,0 +1,35 @@
+import unittest
+from L05.T04.number_of_inversions import compute_inversions, compute_inversions_naive
+from random import randint
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_3(self):
+ n = 3
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ self.assertEqual(compute_inversions_naive(array), compute_inversions(array), msg=f"Wrong answer for array={array}")
+
+ def test_n_4(self):
+ n = 4
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ self.assertEqual(compute_inversions_naive(array), compute_inversions(array), msg=f"Wrong answer for array={array}")
+
+ def test_n_5(self):
+ n = 5
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ self.assertEqual(compute_inversions_naive(array), compute_inversions(array), msg=f"Wrong answer for array={array}")
+
+ def test_n_10(self):
+ n = 10
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ self.assertEqual(compute_inversions_naive(array), compute_inversions(array), msg=f"Wrong answer for array={array}")
+
+ def test_n_100(self):
+ n = 100
+ for array in ([1] * n, [n - i for i in range(n)], [i for i in range(n)], [randint(0, n) for _ in range(n)]):
+ self.assertEqual(compute_inversions_naive(array), compute_inversions(array), msg=f"Wrong answer for array={array}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T05/logo.png b/L05/T05/logo.png
new file mode 100644
index 0000000..3c182d4
Binary files /dev/null and b/L05/T05/logo.png differ
diff --git a/L05/T05/organizing_lottery.py b/L05/T05/organizing_lottery.py
new file mode 100644
index 0000000..30fa284
--- /dev/null
+++ b/L05/T05/organizing_lottery.py
@@ -0,0 +1,37 @@
+from bisect import bisect_left, bisect_right
+from sys import stdin
+
+
+def points_cover_naive(starts, ends, points):
+ assert len(starts) == len(ends)
+ count = [0] * len(points)
+
+ for index, point in enumerate(points):
+ for start, end in zip(starts, ends):
+ if start <= point <= end:
+ count[index] += 1
+
+ return count
+
+
+def points_cover(starts, ends, points):
+ assert len(starts) == len(ends)
+
+ starts, ends = sorted(starts), sorted(ends)
+
+ count = [len(starts)] * len(points)
+ for index, point in enumerate(points):
+ count[index] -= bisect_left(ends, point)
+ count[index] -= len(starts) - bisect_right(starts, point)
+
+ return count
+
+
+if __name__ == '__main__':
+ data = list(map(int, stdin.read().split()))
+ n, m = data[0], data[1]
+ input_starts, input_ends = data[2:2 * n + 2:2], data[3:2 * n + 2:2]
+ input_points = data[2 * n + 2:]
+
+ output_count = points_cover(input_starts, input_ends, input_points)
+ print(*output_count)
diff --git a/L05/T05/task-info.yaml b/L05/T05/task-info.yaml
new file mode 100644
index 0000000..bff60c2
--- /dev/null
+++ b/L05/T05/task-info.yaml
@@ -0,0 +1,28 @@
+type: edu
+custom_name: Organizing a Lottery
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: organizing_lottery.py
+ visible: true
+ placeholders:
+ - offset: 406
+ length: 297
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/organizing_lottery_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 301
+ length: 30
+ placeholder_text: None
+ - offset: 533
+ length: 280
+ placeholder_text: "# TODO: add small test cases with different segment overlaps"
+ - offset: 849
+ length: 280
+ placeholder_text: "# TODO: add larger test cases with many segments and query\
+ \ points"
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/w9YDz/programming-assignment-4-divide-and-conquer/discussions
diff --git a/L05/T05/task.md b/L05/T05/task.md
new file mode 100644
index 0000000..b2360a6
--- /dev/null
+++ b/L05/T05/task.md
@@ -0,0 +1,22 @@
+# Points and Segments
+
+
+
+You are organizing an online lottery. To participate, a person bets on a single integer. You then draw several segments of consecutive integers at random. A participant’s payoff is proportional to the number of segments that contain the participant’s number. You need an efficient algorithm for computing the payoffs for all participants. A simple scan of the list of all ranges for each participant is too slow since your lottery is very popular: you have thousands of participants and thousands of ranges.
+
+
+**Input:** A list of $n \le 50000$ segments and
+a list of $m \le 50000$ points.
+
+**Output:** The number of segments
+containing each point.
+
+
+
+A detailed solution for this programming challenge is covered in the companion MOOCBook. But we strongly encourage you to do your best to solve the challenge yourself before looking into the book! There are at least three good reasons for this.
+
+
By solving this challenge, you practice solving algorithmic problems similar to those given at technical interviews.
+
The satisfaction and self confidence that you get when passing the grader is priceless =)
+
Even if you fail to pass the grader yourself, the time will not be lost as you will better understand the solution from the book and better appreciate the beauty of the underlying ideas.
+
+
diff --git a/L05/T05/tests/organizing_lottery_unit_tests.py b/L05/T05/tests/organizing_lottery_unit_tests.py
new file mode 100644
index 0000000..d26c6d5
--- /dev/null
+++ b/L05/T05/tests/organizing_lottery_unit_tests.py
@@ -0,0 +1,33 @@
+import unittest
+from L05.T05.organizing_lottery import points_cover, points_cover_naive
+
+
+class PointsAndSegments(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for starts, ends, points in [
+ ([0, 7], [5, 10], [1, 6, 11]),
+ ([-10], [10], [-100, 100, 0]),
+ ]:
+ self.assertEqual(points_cover(list(starts), list(ends), list(points)),
+ points_cover_naive(starts, ends, points))
+
+ def test_random(self):
+ for starts, ends, points in [
+ ([0, 7], [5, 10], [1, 6, 11]),
+ ([-10], [10], [-100, 100, 0]),
+ ]:
+ self.assertEqual(points_cover(list(starts), list(ends), list(points)),
+ points_cover_naive(starts, ends, points))
+
+ def test_large(self):
+ for starts, ends, points in [
+ ([0, 7], [5, 10], [1, 6, 11]),
+ ([-10], [10], [-100, 100, 0]),
+ ]:
+ self.assertEqual(points_cover(list(starts), list(ends), list(points)),
+ points_cover_naive(starts, ends, points))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T05/tests/tests.py b/L05/T05/tests/tests.py
new file mode 100644
index 0000000..e88ea55
--- /dev/null
+++ b/L05/T05/tests/tests.py
@@ -0,0 +1,150 @@
+import unittest
+from L05.T05.organizing_lottery import points_cover, points_cover_naive
+from random import randint
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_3_m_3(self):
+ n, m = 3, 3
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_3_m_4(self):
+ n, m = 3, 4
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_3_m_100(self):
+ n, m = 3, 100
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_3_m_200(self):
+ n, m = 3, 200
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_4_m_3(self):
+ n, m = 4, 3
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_4_m_4(self):
+ n, m = 4, 4
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_4_m_100(self):
+ n, m = 4, 100
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_4_m_200(self):
+ n, m = 4, 200
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_5_m_3(self):
+ n, m = 5, 3
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_5_m_4(self):
+ n, m = 5, 4
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_5_m_100(self):
+ n, m = 5, 100
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_5_m_200(self):
+ n, m = 5, 200
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_10_m_3(self):
+ n, m = 10, 3
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_10_m_4(self):
+ n, m = 10, 4
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_10_m_100(self):
+ n, m = 10, 100
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_10_m_200(self):
+ n, m = 10, 200
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_100_m_3(self):
+ n, m = 100, 3
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_100_m_4(self):
+ n, m = 100, 4
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_100_m_100(self):
+ n, m = 100, 100
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+ def test_n_100_m_200(self):
+ n, m = 100, 200
+ points = [randint(-10, 10) for _ in range(m)]
+ starts = [randint(-5, 0) for _ in range(n)]
+ ends = [randint(0, 5) for _ in range(n)]
+ self.assertEqual(points_cover_naive(starts, ends, points), points_cover(starts, ends, points), msg=f"Wrong answer for starts={starts}, ends={ends}, points={points}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T06/closest_points.py b/L05/T06/closest_points.py
new file mode 100644
index 0000000..deb554b
--- /dev/null
+++ b/L05/T06/closest_points.py
@@ -0,0 +1,41 @@
+from collections import namedtuple
+from itertools import combinations
+from math import sqrt
+
+
+Point = namedtuple('Point', 'x y')
+
+
+def distance_squared(first_point, second_point):
+ return (first_point.x - second_point.x) ** 2 + (first_point.y - second_point.y) ** 2
+
+
+def minimum_distance_squared_naive(points):
+ min_distance_squared = float("inf")
+
+ for p, q in combinations(points, 2):
+ min_distance_squared = min(min_distance_squared,
+ distance_squared(p, q))
+
+ return min_distance_squared
+
+
+def minimum_distance_squared(points):
+ min_distance_squared = float("inf")
+
+ for p, q in combinations(points, 2):
+ min_distance_squared = min(min_distance_squared,
+ distance_squared(p, q))
+
+ return min_distance_squared
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ input_points = []
+ for _ in range(input_n):
+ x, y = map(int, input().split())
+ input_point = Point(x, y)
+ input_points.append(input_point)
+
+ print("{0:.9f}".format(sqrt(minimum_distance_squared_naive(input_points))))
diff --git a/L05/T06/logo.png b/L05/T06/logo.png
new file mode 100644
index 0000000..4c0091d
Binary files /dev/null and b/L05/T06/logo.png differ
diff --git a/L05/T06/task-info.yaml b/L05/T06/task-info.yaml
new file mode 100644
index 0000000..db249af
--- /dev/null
+++ b/L05/T06/task-info.yaml
@@ -0,0 +1,24 @@
+type: edu
+custom_name: Closest Points
+files:
+ - name: closest_points.py
+ visible: true
+ placeholders:
+ - offset: 590
+ length: 226
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/closest_points_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 334
+ length: 27
+ placeholder_text: None
+ - offset: 1133
+ length: 238
+ placeholder_text: "# TODO: add larger point sets with different distances"
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/w9YDz/programming-assignment-4-divide-and-conquer/discussions
diff --git a/L05/T06/task.md b/L05/T06/task.md
new file mode 100644
index 0000000..e8a2983
--- /dev/null
+++ b/L05/T06/task.md
@@ -0,0 +1,6 @@
+# Closest Points
+
+
+
+Find the closest pair of points in a set of $n \le 10^5$
+points on a plane.
diff --git a/L05/T06/tests/closest_points_unit_tests.py b/L05/T06/tests/closest_points_unit_tests.py
new file mode 100644
index 0000000..46448c2
--- /dev/null
+++ b/L05/T06/tests/closest_points_unit_tests.py
@@ -0,0 +1,40 @@
+import unittest
+from L05.T06.closest_points import minimum_distance_squared, minimum_distance_squared_naive, Point
+from random import randint
+
+
+class ClosestPoints(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test_small(self):
+ for points in (
+ [Point(1, 0), Point(1, 1)],
+ [Point(2, 2), Point(3, 3)],
+ ):
+ self.assertAlmostEqual(minimum_distance_squared(points),
+ minimum_distance_squared_naive(points),
+ delta=1e-03)
+
+ def test_random(self):
+ for n in [2, 5, 10, 100]:
+ for max_value in [1, 2, 3, 1000]:
+ points = []
+ for _ in range(n):
+ x = randint(-max_value, max_value)
+ y = randint(-max_value, max_value)
+ points.append(Point(x, y))
+
+ self.assertAlmostEqual(minimum_distance_squared(points),
+ minimum_distance_squared_naive(points),
+ delta=1e-03)
+
+ def test_large(self):
+ for points in (
+ [Point(1, 0), Point(1, 1)],
+ [Point(2, 2), Point(3, 3)],
+ ):
+ self.assertEqual(minimum_distance_squared(points),
+ minimum_distance_squared_naive(points))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/T06/tests/tests.py b/L05/T06/tests/tests.py
new file mode 100644
index 0000000..53744b5
--- /dev/null
+++ b/L05/T06/tests/tests.py
@@ -0,0 +1,27 @@
+import unittest
+from L05.T06.closest_points import minimum_distance_squared, minimum_distance_squared_naive, Point
+from math import fabs
+from random import randint
+
+
+class TestCase(unittest.TestCase):
+
+ def test_extreme_points(self):
+ points = [Point(-10 ** 9, - 10 ** 9), Point(10 ** 9, 10 ** 9)]
+ self.assertTrue(fabs(minimum_distance_squared(points) - minimum_distance_squared_naive(points)) < 1e-03, msg=f"Wrong answer for points={points}")
+
+ def test_sequential_points(self):
+ points = [Point(i, i + 1) for i in range(100)]
+ self.assertTrue(fabs(minimum_distance_squared(points) - minimum_distance_squared_naive(points)) < 1e-03, msg=f"Wrong answer for points={points}")
+
+ def test_random_small(self):
+ points = [Point(randint(1, 10), randint(1, 10)) for _ in range(5)]
+ self.assertTrue(fabs(minimum_distance_squared(points) - minimum_distance_squared_naive(points)) < 1e-03, msg=f"Wrong answer for points={points}")
+
+ def test_random_large(self):
+ points = [Point(randint(1, 10), randint(1, 10)) for _ in range(500)]
+ self.assertTrue(fabs(minimum_distance_squared(points) - minimum_distance_squared_naive(points)) < 1e-03, msg=f"Wrong answer for points={points}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L05/lesson-info.yaml b/L05/lesson-info.yaml
new file mode 100644
index 0000000..895b81f
--- /dev/null
+++ b/L05/lesson-info.yaml
@@ -0,0 +1,8 @@
+custom_name: Divide-and-Conquer
+content:
+ - T01
+ - T02
+ - T03
+ - T04
+ - T05
+ - T06
diff --git a/L06/T01/logo.png b/L06/T01/logo.png
new file mode 100644
index 0000000..d85f938
Binary files /dev/null and b/L06/T01/logo.png differ
diff --git a/L06/T01/money_change_again.py b/L06/T01/money_change_again.py
new file mode 100644
index 0000000..c1bad6c
--- /dev/null
+++ b/L06/T01/money_change_again.py
@@ -0,0 +1,30 @@
+def change_naive(money):
+ min_coins = float("inf")
+
+ for num1 in range(money + 1):
+ for num3 in range(money // 3 + 1):
+ for num4 in range(money // 4 + 1):
+ if 1 * num1 + 3 * num3 + 4 * num4 == money:
+ min_coins = min(min_coins, num1 + num3 + num4)
+
+ return min_coins
+
+
+def change(money):
+ assert 1 <= money <= 1000
+
+ t = [0] * (money + 1)
+ coins = [1, 3, 4]
+
+ for i in range(1, money + 1):
+ t[i] = t[i - 1] + 1
+ for c in coins[1:]:
+ if i >= c:
+ t[i] = min(t[i], t[i - c] + 1)
+
+ return t[money]
+
+
+if __name__ == '__main__':
+ amount = int(input())
+ print(change(amount))
diff --git a/L06/T01/task-info.yaml b/L06/T01/task-info.yaml
new file mode 100644
index 0000000..d8b392f
--- /dev/null
+++ b/L06/T01/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Money Change Again
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: money_change_again.py
+ visible: true
+ placeholders:
+ - offset: 353
+ length: 256
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/money_change_again_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 374
+ length: 10
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T01/task.md b/L06/T01/task.md
new file mode 100644
index 0000000..33a0c0b
--- /dev/null
+++ b/L06/T01/task.md
@@ -0,0 +1,6 @@
+# Money Change Again
+
+
+
+Compute the minimum number of coins needed to change
+the given value into coins with denominations 1, 3, and 4.
diff --git a/L06/T01/tests/money_change_again_unit_tests.py b/L06/T01/tests/money_change_again_unit_tests.py
new file mode 100644
index 0000000..b686e0f
--- /dev/null
+++ b/L06/T01/tests/money_change_again_unit_tests.py
@@ -0,0 +1,17 @@
+import unittest
+from L06.T01.money_change_again import change, change_naive
+
+
+class MoneyChangeAgain(unittest.TestCase):
+ def test_small(self):
+ for money in range(1, 40):
+ self.assertEqual(change(money), change_naive(money))
+
+ # TODO: replace None with the correct value
+ def test_large(self):
+ for money, answer in ((200, 50), (239, 60), (971, 243)):
+ self.assertEqual(change(money), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T01/tests/tests.py b/L06/T01/tests/tests.py
new file mode 100644
index 0000000..dcf871b
--- /dev/null
+++ b/L06/T01/tests/tests.py
@@ -0,0 +1,15 @@
+import unittest
+from random import randint
+from L06.T01.money_change_again import change, change_naive
+
+
+class TestCase(unittest.TestCase):
+
+ def test_random_cases(self):
+ for _ in range(20):
+ money = randint(1, 100)
+ self.assertEqual(change_naive(money), change(money), msg=f"Wrong answer for money={money}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T02/logo.png b/L06/T02/logo.png
new file mode 100644
index 0000000..1ec9273
Binary files /dev/null and b/L06/T02/logo.png differ
diff --git a/L06/T02/primitive_calculator.py b/L06/T02/primitive_calculator.py
new file mode 100644
index 0000000..531d865
--- /dev/null
+++ b/L06/T02/primitive_calculator.py
@@ -0,0 +1,32 @@
+def compute_operations(n):
+ assert 1 <= n <= 10 ** 6
+ inf = n + 1
+ f = [inf] * (3 * n + 1)
+ p = [0] * (3 * n + 1)
+ f[1] = 0
+ for i in range(1, n):
+ ff = f[i] + 1
+ if f[i * 2] > ff:
+ f[i * 2] = ff
+ p[i * 2] = i
+ if f[i * 3] > ff:
+ f[i * 3] = ff
+ p[i * 3] = i
+ if f[i + 1] > ff:
+ f[i + 1] = ff
+ p[i + 1] = i
+
+ r = []
+ i = n
+ while i >= 1:
+ r = [i] + r
+ i = p[i]
+
+ return r
+
+
+if __name__ == '__main__':
+ input_n = int(input())
+ output_sequence = compute_operations(input_n)
+ print(len(output_sequence) - 1)
+ print(*output_sequence)
diff --git a/L06/T02/task-info.yaml b/L06/T02/task-info.yaml
new file mode 100644
index 0000000..7a82e18
--- /dev/null
+++ b/L06/T02/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Primitive Calculator
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: primitive_calculator.py
+ visible: true
+ placeholders:
+ - offset: 60
+ length: 448
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/primitive_calculator_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 242
+ length: 7
+ placeholder_text: None
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T02/task.md b/L06/T02/task.md
new file mode 100644
index 0000000..52943c5
--- /dev/null
+++ b/L06/T02/task.md
@@ -0,0 +1,21 @@
+# Primitive Calculator
+
+
+
+Find the minimum number of operations
+needed to get a positive integer $n$ from 1
+using only three operations:
+add 1, multiply by 2, and multiply by 3.
+
+**Input.** An integer $1 \le n \le 10^6$.
+
+**Output.** In the first line, output
+the minimum number $k$ of operations needed to
+get $n$ from 1. In the second line,
+output a sequence of intermediate numbers.
+That is, the second line should contain
+positive integers $a_0,a_1,\dotsc,a_k$
+such that $a_0 = 1$, $a_k = n$, and
+for all $1 \le i \le k$, $a_i$ is equal to
+either $a_{i−1} + 1$, $2a_{i−1}$, or $3a_{i−1}$.
+If there are many such sequences, output any one of them.
diff --git a/L06/T02/tests/primitive_calculator_unit_tests.py b/L06/T02/tests/primitive_calculator_unit_tests.py
new file mode 100644
index 0000000..2bffb3f
--- /dev/null
+++ b/L06/T02/tests/primitive_calculator_unit_tests.py
@@ -0,0 +1,19 @@
+import unittest
+from L06.T02.primitive_calculator import compute_operations
+
+
+class PrimitiveCalculator(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for n, answer in ((2, 1), (3, 1), (5, 3), (10, 3)):
+ sequence = compute_operations(n)
+ self.assertEqual(answer, len(sequence) - 1)
+ self.assertEqual(sequence[0], 1)
+ self.assertEqual(sequence[-1], n)
+ for i in range(len(sequence) - 1):
+ if sequence[i + 1] != sequence[i] + 1 and sequence[i + 1] != 2 * sequence[i]:
+ self.assertEqual(sequence[i + 1], 3 * sequence[i])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T02/tests/tests.py b/L06/T02/tests/tests.py
new file mode 100644
index 0000000..1e4c291
--- /dev/null
+++ b/L06/T02/tests/tests.py
@@ -0,0 +1,25 @@
+import unittest
+from L06.T02.primitive_calculator import compute_operations
+
+
+class TestCase(unittest.TestCase):
+
+ def test_n_20(self):
+ sequence = compute_operations(20)
+ self.assertEqual(4, len(sequence) - 1, msg="Wrong answer for n=20")
+
+ def test_n_200(self):
+ sequence = compute_operations(200)
+ self.assertEqual(8, len(sequence) - 1, msg="Wrong answer for n=200")
+
+ def test_n_239(self):
+ sequence = compute_operations(239)
+ self.assertEqual(10, len(sequence) - 1, msg="Wrong answer for n=239")
+
+ def test_n_69006(self):
+ sequence = compute_operations(69006)
+ self.assertEqual(19, len(sequence) - 1, msg="Wrong answer for n=69006")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T03/edit_distance.py b/L06/T03/edit_distance.py
new file mode 100644
index 0000000..4b4c07b
--- /dev/null
+++ b/L06/T03/edit_distance.py
@@ -0,0 +1,19 @@
+def edit_distance(first_string, second_string):
+ m, n = len(first_string), len(second_string)
+ prev = list(range(n + 1))
+ curr = [0] * (n + 1)
+
+ for i in range(1, m + 1):
+ curr = [0] * (n + 1)
+ curr[0] = i
+ for j in range(1, n + 1):
+ curr[j] = min(curr[j - 1] + 1,
+ prev[j] + 1,
+ prev[j - 1] + (first_string[i - 1] != second_string[j - 1]))
+ prev = curr
+
+ return curr[n]
+
+
+if __name__ == "__main__":
+ print(edit_distance(input(), input()))
diff --git a/L06/T03/logo.png b/L06/T03/logo.png
new file mode 100644
index 0000000..7ee163b
Binary files /dev/null and b/L06/T03/logo.png differ
diff --git a/L06/T03/task-info.yaml b/L06/T03/task-info.yaml
new file mode 100644
index 0000000..a6d45b1
--- /dev/null
+++ b/L06/T03/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Edit Distance
+files:
+ - name: edit_distance.py
+ visible: true
+ placeholders:
+ - offset: 52
+ length: 422
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/edit_distance_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 420
+ length: 16
+ placeholder_text: None
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T03/task.md b/L06/T03/task.md
new file mode 100644
index 0000000..b790e98
--- /dev/null
+++ b/L06/T03/task.md
@@ -0,0 +1,16 @@
+# Edit Distance
+
+
+
+Given two strings of length at most 100,
+compute the minimum number of single symbol
+insertions, deletions, and substitutions to
+transform one string into the other one.
+
+Edit distance has many applications
+in computational biology,
+natural language processing,
+spell checking, etc.
+For example, biologists often analyze edit
+distances when they search for
+disease-causing mutations.
diff --git a/L06/T03/tests/edit_distance_unit_tests.py b/L06/T03/tests/edit_distance_unit_tests.py
new file mode 100644
index 0000000..7fb86e7
--- /dev/null
+++ b/L06/T03/tests/edit_distance_unit_tests.py
@@ -0,0 +1,20 @@
+import unittest
+from L06.T03.edit_distance import edit_distance
+
+
+class EditDistance(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for first_string, second_string, answer in (
+ ("ab", "ab", 0),
+ ("short", "ports", 3),
+ ("editing", "distance", 5),
+ ("a" * 100, "a" * 100, 0),
+ ("ab" * 50, "ba" * 50, 2),
+ ("ab", "ab", 0),
+ ):
+ self.assertEqual(edit_distance(first_string, second_string), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T03/tests/tests.py b/L06/T03/tests/tests.py
new file mode 100644
index 0000000..7edfd54
--- /dev/null
+++ b/L06/T03/tests/tests.py
@@ -0,0 +1,32 @@
+import unittest
+from L06.T03.edit_distance import edit_distance
+
+
+def reference(s, t):
+ sn = len(s)
+ tn = len(t)
+
+ f = [[10**9] * (tn + 2) for _ in range(sn + 2)]
+ f[0][0] = 0
+
+ def relax(p, q, x):
+ f[p][q] = min(f[p][q], x)
+
+ for i in range(sn + 1):
+ for j in range(tn + 1):
+ if i < sn and j < tn:
+ relax(i + 1, j + 1, f[i][j] + (1 if s[i] != t[j] else 0))
+ relax(i + 1, j, f[i][j] + 1)
+ relax(i, j + 1, f[i][j] + 1)
+ return f[sn][tn]
+
+
+class TestCase(unittest.TestCase):
+
+ def test_long_strings(self):
+ first, second = "abacabadabacabaeabacab", "aeabacabad"
+ self.assertEqual(reference(first, second), edit_distance(first, second), msg=f"Wrong answer for {first} and {second}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T04/lcs2.py b/L06/T04/lcs2.py
new file mode 100644
index 0000000..4dc5aab
--- /dev/null
+++ b/L06/T04/lcs2.py
@@ -0,0 +1,26 @@
+def lcs2(first_sequence, second_sequence):
+ assert len(first_sequence) <= 100
+ assert len(second_sequence) <= 100
+
+ t = [[0] * (len(second_sequence) + 1) for _ in range(len(first_sequence) + 1)]
+
+ for i in range(1, len(first_sequence) + 1):
+ for j in range(1, len(second_sequence) + 1):
+ if first_sequence[i - 1] == second_sequence[j - 1]:
+ t[i][j] = max(t[i - 1][j - 1] + 1, t[i][j - 1], t[i - 1][j])
+ else:
+ t[i][j] = max(t[i - 1][j], t[i][j - 1])
+
+ return t[len(first_sequence)][len(second_sequence)]
+
+
+if __name__ == '__main__':
+ n = int(input())
+ a = list(map(int, input().split()))
+ assert len(a) == n
+
+ m = int(input())
+ b = list(map(int, input().split()))
+ assert len(b) == m
+
+ print(lcs2(a, b))
diff --git a/L06/T04/logo.png b/L06/T04/logo.png
new file mode 100644
index 0000000..46dc17f
Binary files /dev/null and b/L06/T04/logo.png differ
diff --git a/L06/T04/task-info.yaml b/L06/T04/task-info.yaml
new file mode 100644
index 0000000..ac3f653
--- /dev/null
+++ b/L06/T04/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Longest Common Subsequence of Two Sequences
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+ - name: lcs2.py
+ visible: true
+ placeholders:
+ - offset: 125
+ length: 452
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/lcs2_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 482
+ length: 20
+ placeholder_text: None
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T04/task.md b/L06/T04/task.md
new file mode 100644
index 0000000..1e89186
--- /dev/null
+++ b/L06/T04/task.md
@@ -0,0 +1,22 @@
+# Longest Common Subsequence of Two Sequences
+
+
+
+Compute the longest common subsequence of
+two integer sequences of length at most 100.
+
+Given two sequences
+$A=(a_1, a_2, \dotsc, a_n)$ and
+$B=(b_1, b_2, \dotsc, b_m)$, find the
+length of their longest common subsequence,
+i.e., the largest non-negative integer $p$ such that
+there exist indices
+$1 \leq i_1 < i_2 < \dotsb < i_p \leq n$ and
+$1 \leq j_1 < j_2 < \dotsb < j_p \leq m$
+such that
+$a_{i_1} = b_{j_1}, \dotsc, a_{i_p} = b_{j_p}$.
+The problem has applications in data comparison
+(e.g., diff utility, merge operation
+in various version control systems),
+bioinformatics (finding similarities
+between genes in various species), and others.
diff --git a/L06/T04/tests/lcs2_unit_tests.py b/L06/T04/tests/lcs2_unit_tests.py
new file mode 100644
index 0000000..73a49c9
--- /dev/null
+++ b/L06/T04/tests/lcs2_unit_tests.py
@@ -0,0 +1,22 @@
+import unittest
+from L06.T04.lcs2 import lcs2
+
+
+class LCS2(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for first_sequence, second_sequence, answer in (
+ ((1, 2), (2, 1), 1),
+ ((1, 2), (3, 4), 0),
+ ([17] * 50, [17] * 25, 25),
+ ([1] * 100, [1] * 100, 100),
+ ((2, 7, 5), (2, 5), 2),
+ ((7, ), (1, 2, 3, 4), 0),
+ ((2, 7, 8, 3), (5, 2, 8, 7), 2),
+ ((1, 2), (2, 1), 1),
+ ):
+ self.assertEqual(lcs2(first_sequence, second_sequence), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T04/tests/tests.py b/L06/T04/tests/tests.py
new file mode 100644
index 0000000..6725b79
--- /dev/null
+++ b/L06/T04/tests/tests.py
@@ -0,0 +1,31 @@
+import unittest
+from L06.T04.lcs2 import lcs2
+
+
+def reference(a, b):
+ n, m = len(a), len(b)
+ t = [[0] * (m + 1) for _ in range(n + 1)]
+
+ for i in range(1, n + 1):
+ for j in range(1, m + 1):
+ if a[i - 1] == b[j - 1]:
+ t[i][j] = max(t[i - 1][j - 1] + 1, t[i][j - 1], t[i - 1][j])
+ else:
+ t[i][j] = max(t[i - 1][j], t[i][j - 1])
+
+ return t[n][m]
+
+
+class TestCase(unittest.TestCase):
+
+ def test_alternating_sequences(self):
+ first, second = [1, 2] * 50, [2, 1] * 50
+ self.assertEqual(reference(first, second), lcs2(first, second), msg=f"Wrong answer for {first} and {second}")
+
+ def test_zeros_and_modulo(self):
+ first, second = [0] * 10, [i % 3 for i in range(10)]
+ self.assertEqual(reference(first, second), lcs2(first, second), msg=f"Wrong answer for {first} and {second}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T05/lcs3.py b/L06/T05/lcs3.py
new file mode 100644
index 0000000..8f5435d
--- /dev/null
+++ b/L06/T05/lcs3.py
@@ -0,0 +1,35 @@
+def lcs3(first_sequence, second_sequence, third_sequence):
+ assert len(first_sequence) <= 100
+ assert len(second_sequence) <= 100
+ assert len(third_sequence) <= 100
+
+ a, b, c = first_sequence, second_sequence, third_sequence
+ d = [[[0 for _ in range(len(c) + 1)] for _ in range(len(b) + 1)] for _ in range(len(a) + 1)]
+ for i in range(len(a) + 1):
+ for j in range(len(b) + 1):
+ for k in range(len(c) + 1):
+ if i < len(a):
+ d[i + 1][j][k] = max(d[i][j][k], d[i + 1][j][k])
+ if j < len(b):
+ d[i][j + 1][k] = max(d[i][j][k], d[i][j + 1][k])
+ if k < len(c):
+ d[i][j][k + 1] = max(d[i][j][k], d[i][j][k + 1])
+ if i < len(a) and j < len(b) and k < len(c) and a[i] == b[j] == c[k]:
+ d[i + 1][j + 1][k + 1] = max(d[i + 1][j + 1][k + 1], d[i][j][k] + 1)
+ return d[len(a)][len(b)][len(c)]
+
+
+if __name__ == '__main__':
+ n = int(input())
+ a = list(map(int, input().split()))
+ assert len(a) == n
+
+ m = int(input())
+ b = list(map(int, input().split()))
+ assert len(b) == m
+
+ q = int(input())
+ c = list(map(int, input().split()))
+ assert len(c) == q
+
+ print(lcs3(a, b, c))
diff --git a/L06/T05/logo.png b/L06/T05/logo.png
new file mode 100644
index 0000000..fba41cd
Binary files /dev/null and b/L06/T05/logo.png differ
diff --git a/L06/T05/task-info.yaml b/L06/T05/task-info.yaml
new file mode 100644
index 0000000..8c1e79f
--- /dev/null
+++ b/L06/T05/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Longest Common Subsequence of Three Sequences
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+ - name: lcs3.py
+ visible: true
+ placeholders:
+ - offset: 179
+ length: 774
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/lcs3_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 462
+ length: 37
+ placeholder_text: None
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T05/task.md b/L06/T05/task.md
new file mode 100644
index 0000000..892d03c
--- /dev/null
+++ b/L06/T05/task.md
@@ -0,0 +1,6 @@
+# Longest Common Subsequence of Three Sequences
+
+
+
+Compute the longest common subsequence of
+three integer sequences of length at most 100.
diff --git a/L06/T05/tests/lcs3_unit_tests.py b/L06/T05/tests/lcs3_unit_tests.py
new file mode 100644
index 0000000..0e4cc2f
--- /dev/null
+++ b/L06/T05/tests/lcs3_unit_tests.py
@@ -0,0 +1,19 @@
+import unittest
+from L06.T05.lcs3 import lcs3
+
+
+class LCS3(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for first_sequence, second_sequence, third_sequence, answer in (
+ ((1, 2, 3), (2, 1, 3), (1, 3, 5), 2),
+ ((8, 3, 2, 1, 7), (8, 2, 1, 3, 8, 10, 7), (6, 8, 3, 1, 4, 7), 3),
+ ([7] * 25, [6, 7] * 25, [7] * 25, 25),
+ ([7] * 25, [7] * 100, [5, 6] * 50, 0),
+ ((1, 2, 3), (2, 1, 3), (1, 3, 5), 2),
+ ):
+ self.assertEqual(lcs3(first_sequence, second_sequence, third_sequence), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T05/tests/tests.py b/L06/T05/tests/tests.py
new file mode 100644
index 0000000..b457fe2
--- /dev/null
+++ b/L06/T05/tests/tests.py
@@ -0,0 +1,39 @@
+import unittest
+from random import randint, seed
+from L06.T05.lcs3 import lcs3
+
+
+def ref(a, b, c):
+ d = [[[0 for _ in range(len(c) + 1)] for _ in range(len(b) + 1)] for _ in range(len(a) + 1)]
+ for i in range(len(a) + 1):
+ for j in range(len(b) + 1):
+ for k in range(len(c) + 1):
+ if i < len(a):
+ d[i + 1][j][k] = max(d[i][j][k], d[i + 1][j][k])
+ if j < len(b):
+ d[i][j + 1][k] = max(d[i][j][k], d[i][j + 1][k])
+ if k < len(c):
+ d[i][j][k + 1] = max(d[i][j][k], d[i][j][k + 1])
+ if i < len(a) and j < len(b) and k < len(c) and a[i] == b[j] == c[k]:
+ d[i + 1][j + 1][k + 1] = max(d[i + 1][j + 1][k + 1], d[i][j][k] + 1)
+ return d[len(a)][len(b)][len(c)]
+
+
+class TestCase(unittest.TestCase):
+
+ def test_random_cases(self):
+ seed(239)
+ for _ in range(10):
+ for n in (3, 5, 20, 10):
+ for m in (2, 3, 4, 10, 100):
+ lena = randint(1, n)
+ lenb = randint(1, n)
+ lenc = randint(1, n)
+ a = [randint(1, m) for _ in range(lena)]
+ b = [randint(1, m) for _ in range(lenb)]
+ c = [randint(1, m) for _ in range(lenc)]
+ self.assertEqual(ref(a, b, c), lcs3(a, b, c), msg=f"Wrong answer: {a}; {b}; {c}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T06/logo.png b/L06/T06/logo.png
new file mode 100644
index 0000000..cdeea39
Binary files /dev/null and b/L06/T06/logo.png differ
diff --git a/L06/T06/maximum_gold.py b/L06/T06/maximum_gold.py
new file mode 100644
index 0000000..5faae51
--- /dev/null
+++ b/L06/T06/maximum_gold.py
@@ -0,0 +1,25 @@
+from sys import stdin
+
+
+def maximum_gold(capacity, weights):
+ assert 1 <= capacity <= 10 ** 4
+ assert 1 <= len(weights) <= 10 ** 3
+ assert all(1 <= w <= 10 ** 5 for w in weights)
+
+ dp = [False] * (capacity + 1)
+ dp[0] = True
+ for i in range(len(weights)):
+ for s in range(capacity, weights[i] - 1, -1):
+ dp[s] = dp[s] or dp[s - weights[i]]
+ for s in range(capacity, -1, -1):
+ if dp[s]:
+ return s
+
+ assert False
+
+
+if __name__ == '__main__':
+ input_capacity, n, *input_weights = list(map(int, stdin.read().split()))
+ assert len(input_weights) == n
+
+ print(maximum_gold(input_capacity, input_weights))
diff --git a/L06/T06/task-info.yaml b/L06/T06/task-info.yaml
new file mode 100644
index 0000000..0aaf1bf
--- /dev/null
+++ b/L06/T06/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Maximum Amount of Gold
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+ - name: maximum_gold.py
+ visible: true
+ placeholders:
+ - offset: 193
+ length: 277
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/maximum_gold_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 334
+ length: 26
+ placeholder_text: None
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T06/task.md b/L06/T06/task.md
new file mode 100644
index 0000000..25215c3
--- /dev/null
+++ b/L06/T06/task.md
@@ -0,0 +1,21 @@
+# Maximum Amount of Gold
+
+
+
+Given a set of gold bars of various weights
+and a backpack that can hold at most $W$ pounds,
+place as much gold as possible into the backpack.
+
+**Constraints.** $1 \le W \le 10^4$. There are at most
+$10^3$ gold bars, the weight of each gold bar is at
+most $10^5$.
+
+
+
+A detailed solution for this programming challenge is covered in the companion MOOCBook. But we strongly encourage you to do your best to solve the challenge yourself before looking into the book! There are at least three good reasons for this.
+
+
By solving this challenge, you practice solving algorithmic problems similar to those given at technical interviews.
+
The satisfaction and self confidence that you get when passing the grader is priceless =)
+
Even if you fail to pass the grader yourself, the time will not be lost as you will better understand the solution from the book and better appreciate the beauty of the underlying ideas.
+
+
diff --git a/L06/T06/tests/maximum_gold_unit_tests.py b/L06/T06/tests/maximum_gold_unit_tests.py
new file mode 100644
index 0000000..30a76d2
--- /dev/null
+++ b/L06/T06/tests/maximum_gold_unit_tests.py
@@ -0,0 +1,18 @@
+import unittest
+from L06.T06.maximum_gold import maximum_gold
+
+
+class MaximumGold(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for capacity, weights, answer in (
+ (10, (1, 4, 8), 9),
+ (20, (5, 7, 12, 18), 19),
+ (10, (3, 5, 3, 3, 5), 10),
+ (10, (3, 5, 3, 3, 5), 10),
+ ):
+ self.assertEqual(maximum_gold(capacity, weights), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T06/tests/tests.py b/L06/T06/tests/tests.py
new file mode 100644
index 0000000..580d3ed
--- /dev/null
+++ b/L06/T06/tests/tests.py
@@ -0,0 +1,22 @@
+import unittest
+from L06.T06.maximum_gold import maximum_gold
+
+
+class TestCase(unittest.TestCase):
+
+ def test_capacity_10_weights_1_4_8(self):
+ self.assertEqual(9, maximum_gold(10, (1, 4, 8)), msg="Wrong answer for capacity=10, weights=(1, 4, 8)")
+
+ def test_capacity_20_weights_5_7_12_18(self):
+ self.assertEqual(19, maximum_gold(20, (5, 7, 12, 18)), msg="Wrong answer for capacity=20, weights=(5, 7, 12, 18)")
+
+ def test_capacity_10_weights_3_5_3_3_5(self):
+ self.assertEqual(10, maximum_gold(10, (3, 5, 3, 3, 5)), msg="Wrong answer for capacity=10, weights=(3, 5, 3, 3, 5)")
+
+ def test_large_case(self):
+ weights = (342, 381, 230, 381, 206, 393, 364, 319, 279, 385, 345, 263, 365, 281, 298, 247, 239, 201, 378, 351)
+ self.assertEqual(499, maximum_gold(500, weights), msg=f"Wrong answer for capacity=500, weights={weights}")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T07/logo.png b/L06/T07/logo.png
new file mode 100644
index 0000000..a84afb2
Binary files /dev/null and b/L06/T07/logo.png differ
diff --git a/L06/T07/partition_souvenirs.py b/L06/T07/partition_souvenirs.py
new file mode 100644
index 0000000..83f6e61
--- /dev/null
+++ b/L06/T07/partition_souvenirs.py
@@ -0,0 +1,48 @@
+from itertools import product
+from sys import stdin
+
+
+def partition3_naive(values):
+ for c in product(range(3), repeat=len(values)):
+ sums = [None] * 3
+ for i in range(3):
+ sums[i] = sum(values[k] for k in range(len(values)) if c[k] == i)
+
+ if sums[0] == sums[1] and sums[1] == sums[2]:
+ return 1
+
+ return 0
+
+
+def partition3(values):
+ assert 1 <= len(values) <= 20
+ assert all(1 <= v <= 30 for v in values)
+
+ s = sum(values)
+ n = len(values)
+
+ if s % 3 != 0:
+ return 0
+
+ t = [[[-1 for _ in range(s + 1)] for _ in range(s + 1)] for _ in range(n + 1)]
+ for (s1, s2) in product(range(s // 3 + 1), repeat=2):
+ if s1 == 0 and s2 == 0:
+ t[0][s1][s2] = 1
+ else:
+ t[0][s1][s2] = 0
+
+ for i in range(1, n + 1):
+ for (s1, s2) in product(range(s // 3 + 1), repeat=2):
+ t[i][s1][s2] = t[i - 1][s1][s2]
+ if s1 >= values[i - 1]:
+ t[i][s1][s2] = max(t[i][s1][s2], t[i - 1][s1 - values[i - 1]][s2])
+ if s2 >= values[i - 1]:
+ t[i][s1][s2] = max(t[i][s1][s2], t[i - 1][s1][s2 - values[i - 1]])
+
+ return t[n][s // 3][s // 3]
+
+
+if __name__ == '__main__':
+ input_n, *input_values = list(map(int, stdin.read().split()))
+ assert input_n == len(input_values)
+ print(partition3(input_values))
diff --git a/L06/T07/task-info.yaml b/L06/T07/task-info.yaml
new file mode 100644
index 0000000..87ec108
--- /dev/null
+++ b/L06/T07/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Partitioning Souvenirs
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+ - name: partition_souvenirs.py
+ visible: true
+ placeholders:
+ - offset: 467
+ length: 726
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/partition_souvenirs_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 557
+ length: 49
+ placeholder_text: None
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T07/task.md b/L06/T07/task.md
new file mode 100644
index 0000000..0084199
--- /dev/null
+++ b/L06/T07/task.md
@@ -0,0 +1,12 @@
+# Partitioning Souvenirs
+
+
+
+You and two of your friends have just returned back home after visiting various countries. Now you would like to evenly split all the souvenirs that all three of you bought.
+
+**Input.** A sequence of integers $v_1, v_2, \dotsc, v_n$.
+
+**Output.** Output 1, if it is possible to partition them into three subsets with equal sums.
+Output 0 otherwise.
+
+**Constrains.** $1 \le n \le 20$, $1 \le v_i \le 30$ for all $i$.
diff --git a/L06/T07/tests/partition_souvenirs_unit_tests.py b/L06/T07/tests/partition_souvenirs_unit_tests.py
new file mode 100644
index 0000000..306405f
--- /dev/null
+++ b/L06/T07/tests/partition_souvenirs_unit_tests.py
@@ -0,0 +1,26 @@
+import unittest
+from L06.T07.partition_souvenirs import partition3, partition3_naive
+
+
+class PartitionSouvenirs(unittest.TestCase):
+ def test_small(self):
+ for values in (
+ (20,),
+ (7, 7, 7),
+ (3, 3, 3),
+ (3, 3, 3, 3),
+ (3, 4, 3, 4, 3, 4),
+ ):
+ self.assertEqual(partition3(values), partition3_naive(values))
+
+ # TODO: replace None with the correct value
+ def test_medium(self):
+ for values, answer in (
+ ((3, 4, 5, 3, 4, 5, 3, 4, 5), 1),
+ ((1, 2, 3, 4, 5, 5, 7, 7, 8, 10, 12, 19, 25), 1),
+ ):
+ self.assertEqual(partition3(values), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T07/tests/tests.py b/L06/T07/tests/tests.py
new file mode 100644
index 0000000..c460ec5
--- /dev/null
+++ b/L06/T07/tests/tests.py
@@ -0,0 +1,36 @@
+import unittest
+from L06.T07.partition_souvenirs import partition3
+
+
+class TestCase(unittest.TestCase):
+
+ def test_single_element(self):
+ self.assertEqual(0, partition3((20,)), msg="Wrong answer for values=(20,)")
+
+ def test_three_equal_7(self):
+ self.assertEqual(1, partition3((7, 7, 7)), msg="Wrong answer for values=(7, 7, 7)")
+
+ def test_three_equal_3(self):
+ self.assertEqual(1, partition3((3, 3, 3)), msg="Wrong answer for values=(3, 3, 3)")
+
+ def test_four_equal_3(self):
+ self.assertEqual(0, partition3((3, 3, 3, 3)), msg="Wrong answer for values=(3, 3, 3, 3)")
+
+ def test_mixed_values(self):
+ self.assertEqual(1, partition3((1, 2, 3, 4, 5, 5, 7, 7, 8, 10, 12, 19, 25)), msg="Wrong answer for values=(1, 2, 3, 4, 5, 5, 7, 7, 8, 10, 12, 19, 25)")
+
+ def test_ten_ones(self):
+ self.assertEqual(0, partition3([1]*10), msg="Wrong answer for values=[1]*10")
+
+ def test_twelve_ones(self):
+ self.assertEqual(1, partition3([1]*12), msg="Wrong answer for values=[1]*12")
+
+ def test_twenty_thirties(self):
+ self.assertEqual(0, partition3([30]*20), msg="Wrong answer for values=[30]*20")
+
+ def test_eighteen_thirties(self):
+ self.assertEqual(1, partition3([30]*18), msg="Wrong answer for values=[30]*18")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T08/arithmetic_expression.py b/L06/T08/arithmetic_expression.py
new file mode 100644
index 0000000..05570fe
--- /dev/null
+++ b/L06/T08/arithmetic_expression.py
@@ -0,0 +1,39 @@
+def find_maximum_value(dataset):
+ assert 1 <= len(dataset) <= 29
+
+ def evaluate(p, q, op):
+ if op == '+':
+ return p + q
+ elif op == '-':
+ return p - q
+ elif op == '*':
+ return p * q
+ else:
+ assert False
+
+ n = len(dataset)
+ min_value = [[float('inf') for _ in range(n+1)] for _ in range(n+1)]
+ max_value = [[-float('inf') for _ in range(n+1)] for _ in range(n+1)]
+
+ for i in range(0, n + 1, 2):
+ min_value[i][i + 1] = int(dataset[i])
+ max_value[i][i + 1] = int(dataset[i])
+
+ for l in range(1, n + 1, 2):
+ for s in range(0, n, 2):
+ if s + l > n:
+ continue
+ e = s + l
+ for o in range(s + 1, e, 2):
+ a = evaluate(max_value[s][o], max_value[o + 1][e], dataset[o])
+ b = evaluate(max_value[s][o], min_value[o + 1][e], dataset[o])
+ c = evaluate(min_value[s][o], max_value[o + 1][e], dataset[o])
+ d = evaluate(min_value[s][o], min_value[o + 1][e], dataset[o])
+ min_value[s][e] = min(min_value[s][e], a, b, c, d)
+ max_value[s][e] = max(max_value[s][e], a, b, c, d)
+
+ return max_value[0][n]
+
+
+if __name__ == "__main__":
+ print(find_maximum_value(input()))
diff --git a/L06/T08/logo.png b/L06/T08/logo.png
new file mode 100644
index 0000000..773605b
Binary files /dev/null and b/L06/T08/logo.png differ
diff --git a/L06/T08/task-info.yaml b/L06/T08/task-info.yaml
new file mode 100644
index 0000000..d3a98e8
--- /dev/null
+++ b/L06/T08/task-info.yaml
@@ -0,0 +1,21 @@
+type: edu
+custom_name: Maximum Value of an Arithmetic Expression
+files:
+ - name: logo.png
+ visible: false
+ is_binary: true
+ - name: tests/tests.py
+ visible: false
+ - name: arithmetic_expression.py
+ visible: true
+ placeholders:
+ - offset: 73
+ length: 1161
+ placeholder_text: "# TODO: write your code here"
+ - name: tests/arithmetic_expression_unit_tests.py
+ visible: true
+ placeholders:
+ - offset: 338
+ length: 12
+ placeholder_text: None
+feedback_link: https://www.coursera.org/learn/algorithmic-toolbox/programming/ekN4T/programming-assignment-5-dynamic-programming-1/discussions
diff --git a/L06/T08/task.md b/L06/T08/task.md
new file mode 100644
index 0000000..ae180ea
--- /dev/null
+++ b/L06/T08/task.md
@@ -0,0 +1,15 @@
+# Maximum Value of an Arithmetic Expression
+
+
+
+Parenthesize an arithmetic expression to maxi- mize its value.
+
+**Input.** A string $s=s_0s_1\dotsb s_{2n}$ of length at most 29.
+Each symbol at an even position of $s$ is a digit
+(that is, an integer from 0 to 9)
+while each symbol at an odd position
+is one of three operations from {$+$,$-$,$*$}.
+
+**Output.** The maximum value of the given
+arithmetic expression among all possible
+orders of applying arithmetic operations.
diff --git a/L06/T08/tests/arithmetic_expression_unit_tests.py b/L06/T08/tests/arithmetic_expression_unit_tests.py
new file mode 100644
index 0000000..2e52f1f
--- /dev/null
+++ b/L06/T08/tests/arithmetic_expression_unit_tests.py
@@ -0,0 +1,19 @@
+import unittest
+from L06.T08.arithmetic_expression import find_maximum_value
+
+
+class ArithmeticExpression(unittest.TestCase):
+ # TODO: replace None with the correct value
+ def test(self):
+ for s, answer in (
+ ("5", 5),
+ ("2+3", 5),
+ ("2-3", -1),
+ ("5-8+7*4-8+9", 200),
+ ("2-3", -1),
+ ):
+ self.assertEqual(find_maximum_value(s), answer)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/T08/tests/tests.py b/L06/T08/tests/tests.py
new file mode 100644
index 0000000..42591d9
--- /dev/null
+++ b/L06/T08/tests/tests.py
@@ -0,0 +1,33 @@
+import unittest
+from L06.T08.arithmetic_expression import find_maximum_value
+
+
+class TestCase(unittest.TestCase):
+
+ def test_single_digit_5(self):
+ self.assertEqual(5, find_maximum_value("5"), msg="Wrong answer for 5")
+
+ def test_addition(self):
+ self.assertEqual(5, find_maximum_value("2+3"), msg="Wrong answer for 2+3")
+
+ def test_subtraction(self):
+ self.assertEqual(-1, find_maximum_value("2-3"), msg="Wrong answer for 2-3")
+
+ def test_complex_expression(self):
+ self.assertEqual(200, find_maximum_value("5-8+7*4-8+9"), msg="Wrong answer for 5-8+7*4-8+9")
+
+ def test_all_multiplication(self):
+ self.assertEqual(9 * 9 * 9 * 9, find_maximum_value("9*9*9*9"), msg="Wrong answer for 9*9*9*9")
+
+ def test_subtraction_to_zero(self):
+ self.assertEqual(0, find_maximum_value("1-1"), msg="Wrong answer for 1-1")
+
+ def test_single_digit_7(self):
+ self.assertEqual(7, find_maximum_value("7"), msg="Wrong answer for 7")
+
+ def test_all_addition(self):
+ self.assertEqual(45, find_maximum_value("1+2+3+4+5+6+7+8+9"), msg="Wrong answer for 1+2+3+4+5+6+7+8+9")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L06/lesson-info.yaml b/L06/lesson-info.yaml
new file mode 100644
index 0000000..ac82ece
--- /dev/null
+++ b/L06/lesson-info.yaml
@@ -0,0 +1,10 @@
+custom_name: Dynamic Programming
+content:
+ - T01
+ - T02
+ - T03
+ - T04
+ - T05
+ - T06
+ - T07
+ - T08
diff --git a/README.md b/README.md
index d1a0c9c..e091531 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,16 @@
# Algorithmic Toolbox in Python
+[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [](https://opensource.org/licenses/MIT)
+
+
+
+
-> TODO: badges
-
-> TODO: description
+Learn algorithms through solving programming challenges in the PyCharm IDE! This professional IDE will help you format and structure your code, as well as test and debug it.
+This course contains assignments for the Coursera Algorithmic Toolbox course and powers our popular Data Structures and Algorithms specialization on Coursera. We encourage you to sign up for a session and learn this material while interacting with thousands of other talented students from around the world.
## Want to know more?
-If you have questions about the course or the tasks, or if you find any errors, feel free to ask questions and participate in discussions within the repository [issues](https://github.com/jetbrains-academy/algorithmic-toolbox-python/issues).
+If you have questions about the course or the tasks, or if you find any errors, feel free to ask questions and participate
+in discussions within the repository [issues](https://github.com/jetbrains-academy/algorithmic-toolbox-python/issues).
## Contribution
Please be sure to review the [project's contributing guidelines](https://github.com/jetbrains-academy/.github/blob/main/contributing_guidelines.md) to learn how to help the project.
diff --git a/course-info.yaml b/course-info.yaml
new file mode 100644
index 0000000..fcebdf2
--- /dev/null
+++ b/course-info.yaml
@@ -0,0 +1,19 @@
+type: marketplace
+title: Algorithmic Toolbox
+language: English
+summary: "Learn algorithms through solving programming challenges in the PyCharm IDE!\
+ \ This professional IDE will help you to format and structure your code, as well\
+ \ as test and debug it. \n\nThis course powers our popular Data Structures and Algorithms specialization on Coursera. We encourage you\
+ \ to sign up for a session and learn this material while interacting with thousands\
+ \ of other talented students from around the world."
+programming_language: Python
+environment: unittest
+content:
+ - L01
+ - L02
+ - L03
+ - L04
+ - L05
+ - L06
+yaml_version: 5