diff --git a/.gitignore b/.gitignore index cececc156..f927dfb6e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ venv __pycache__ .pytest_cache .ruff_check +.DS_Store +.idea/ # Python Files *solved.ipynb diff --git a/conditions/sem02/lesson03/tasks.md b/conditions/sem02/lesson03/tasks.md index 31818efbe..4a019e2a8 100644 --- a/conditions/sem02/lesson03/tasks.md +++ b/conditions/sem02/lesson03/tasks.md @@ -1,4 +1,4 @@ -## Задача 1: Долой Python! Да здравствует NumPy! + ## Задача 1: Долой Python! Да здравствует NumPy! В этом задании вам будут предложены реализации некоторых функций на Python. Ваша задача - векторизовать код этих функций, используя NumPy. diff --git a/deprecated_tests/sem01/tests/test_lesson02_tasks.py b/deprecated_tests/sem01/tests/test_lesson02_tasks.py index f11403f4d..b7febe4ba 100644 --- a/deprecated_tests/sem01/tests/test_lesson02_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson02_tasks.py @@ -10,7 +10,7 @@ @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 0, @@ -44,7 +44,7 @@ def test_get_factorial(num: int, result_expected: int) -> None: @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 0, @@ -83,7 +83,7 @@ def test_get_doubled_factorial(num: int, result_expected: int) -> None: @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 1, @@ -125,7 +125,7 @@ def test_get_amount_of_ways_to_climb( @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 1, @@ -162,7 +162,7 @@ def test_get_multiplications_amount( @pytest.mark.parametrize( - "num1, num2, result_expected", + "num1, num2, result_expected", ( pytest.param( 1, @@ -229,7 +229,7 @@ def test_get_gcd( @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 1, @@ -273,10 +273,10 @@ def test_get_sum_of_prime_divisors(num: int, result_expected: int) -> None: @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( - -10**10, + -(10**10), False, id="negative-ten-billion", ), diff --git a/deprecated_tests/sem01/tests/test_lesson04_tasks.py b/deprecated_tests/sem01/tests/test_lesson04_tasks.py index 4110bdcc0..c98b6f22a 100644 --- a/deprecated_tests/sem01/tests/test_lesson04_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson04_tasks.py @@ -1,6 +1,7 @@ -import pytest import random +import pytest + from solutions.sem01.lesson04.task1 import is_arithmetic_progression from solutions.sem01.lesson04.task2 import merge_intervals from solutions.sem01.lesson04.task3 import find_single_number @@ -8,62 +9,89 @@ from solutions.sem01.lesson04.task5 import find_row_with_most_ones from solutions.sem01.lesson04.task6 import count_cycles -@pytest.mark.parametrize("lst, expected", [ - pytest.param([], True, id="empty_list"), - pytest.param([5], True, id="single_element"), - pytest.param([1, 3], True, id="two_elements"), - pytest.param([3, 1], True, id="two_elements_unsorted"), - pytest.param([1, 3, 5, 7], True, id="already_sorted_ap"), - pytest.param([3, 1, 5, 7], True, id="unsorted_ap"), - pytest.param([1, 2, 4], False, id="not_ap"), - pytest.param([10, 5, 0, -5], True, id="negative_difference"), - pytest.param([1, 1, 1, 1], True, id="constant_sequence"), - pytest.param([1, 2, 3, 5], False, id="almost_ap_but_not"), - pytest.param([0, 0, 1], False, id="two_same_one_different"), - pytest.param([10**5 + i*10**2 for i in range(1000)], True, id="long_list_true"), - pytest.param([10**5 + i*10**2 for i in range(999)] + [1], False, id="long_list_false"), -]) + +@pytest.mark.parametrize( + "lst, expected", + [ + pytest.param([], True, id="empty_list"), + pytest.param([5], True, id="single_element"), + pytest.param([1, 3], True, id="two_elements"), + pytest.param([3, 1], True, id="two_elements_unsorted"), + pytest.param([1, 3, 5, 7], True, id="already_sorted_ap"), + pytest.param([3, 1, 5, 7], True, id="unsorted_ap"), + pytest.param([1, 2, 4], False, id="not_ap"), + pytest.param([10, 5, 0, -5], True, id="negative_difference"), + pytest.param([1, 1, 1, 1], True, id="constant_sequence"), + pytest.param([1, 2, 3, 5], False, id="almost_ap_but_not"), + pytest.param([0, 0, 1], False, id="two_same_one_different"), + pytest.param([10**5 + i * 10**2 for i in range(1000)], True, id="long_list_true"), + pytest.param([10**5 + i * 10**2 for i in range(999)] + [1], False, id="long_list_false"), + ], +) def test_is_arithmetic_progression_parametrized(lst, expected): if len(lst) > 500: random.shuffle(lst) assert is_arithmetic_progression(lst) == expected -@pytest.mark.parametrize("intervals, expected", [ - pytest.param([], [], id="empty"), - pytest.param([[1, 3]], [[1, 3]], id="single_interval"), - pytest.param([[10, 13], [1, 3], [2, 6], [8, 10], [15, 18]], [[1, 6], [8, 13], [15, 18]], id="classic_merge"), - pytest.param([[1, 4], [4, 5]], [[1, 5]], id="touching_intervals"), - pytest.param([[1, 4], [2, 3]], [[1, 4]], id="nested_interval"), - pytest.param([[5, 7], [1, 3], [15, 20], [0, 0], [2, 4], [6, 10], [0, 2]], [[0, 4], [5, 10], [15, 20]], id="unsorted_input"), - pytest.param([[1, 2], [3, 4], [5, 6]], [[1, 2], [3, 4], [5, 6]], id="no_overlap"), - pytest.param([[1, 10], [2, 3], [4, 5], [6, 7]], [[1, 10]], id="all_merged"), -]) +@pytest.mark.parametrize( + "intervals, expected", + [ + pytest.param([], [], id="empty"), + pytest.param([[1, 3]], [[1, 3]], id="single_interval"), + pytest.param( + [[10, 13], [1, 3], [2, 6], [8, 10], [15, 18]], + [[1, 6], [8, 13], [15, 18]], + id="classic_merge", + ), + pytest.param([[1, 4], [4, 5]], [[1, 5]], id="touching_intervals"), + pytest.param([[1, 4], [2, 3]], [[1, 4]], id="nested_interval"), + pytest.param( + [[5, 7], [1, 3], [15, 20], [0, 0], [2, 4], [6, 10], [0, 2]], + [[0, 4], [5, 10], [15, 20]], + id="unsorted_input", + ), + pytest.param([[1, 2], [3, 4], [5, 6]], [[1, 2], [3, 4], [5, 6]], id="no_overlap"), + pytest.param([[1, 10], [2, 3], [4, 5], [6, 7]], [[1, 10]], id="all_merged"), + ], +) def test_merge_intervals(intervals, expected): assert merge_intervals(intervals) == expected -@pytest.mark.parametrize("nums, expected", [ - pytest.param([2, 2, 1], 1, id="simple_case"), - pytest.param([4, 1, 2, 1, 2], 4, id="middle_single"), - pytest.param([1], 1, id="single_element"), - pytest.param([100, 200, 300, 200, 100], 300, id="large_numbers"), - pytest.param([0, 1, 0], 1, id="with_zero"), - pytest.param([7, 8, 9, 8, 7], 9, id="unsorted"), - pytest.param([i + 10**5 for i in range(500)] + [i + 10**5 for i in range(500)] + [69], 69, id="long_list"), -]) + +@pytest.mark.parametrize( + "nums, expected", + [ + pytest.param([2, 2, 1], 1, id="simple_case"), + pytest.param([4, 1, 2, 1, 2], 4, id="middle_single"), + pytest.param([1], 1, id="single_element"), + pytest.param([100, 200, 300, 200, 100], 300, id="large_numbers"), + pytest.param([0, 1, 0], 1, id="with_zero"), + pytest.param([7, 8, 9, 8, 7], 9, id="unsorted"), + pytest.param( + [i + 10**5 for i in range(500)] + [i + 10**5 for i in range(500)] + [69], + 69, + id="long_list", + ), + ], +) def test_find_single_number(nums, expected): assert find_single_number(nums) == expected -@pytest.mark.parametrize("input_list, expected_list, expected_index", [ - pytest.param([0, 1, 0, 3, 12], [1, 3, 12, 0, 0], 3, id="basic"), - pytest.param([0, 0, 1], [1, 0, 0], 1, id="zeros_first"), - pytest.param([1, 2, 3], [1, 2, 3], 3, id="no_zeros"), - pytest.param([0, 0, 0], [0, 0, 0], 0, id="all_zeros"), - pytest.param([1, 0, 2, 0, 3, 0], [1, 2, 3, 0, 0, 0], 3, id="interleaved"), - pytest.param([], [], 0, id="empty"), - pytest.param([0], [0], 0, id="single_zero"), - pytest.param([42], [42], 1, id="single_nonzero"), -]) + +@pytest.mark.parametrize( + "input_list, expected_list, expected_index", + [ + pytest.param([0, 1, 0, 3, 12], [1, 3, 12, 0, 0], 3, id="basic"), + pytest.param([0, 0, 1], [1, 0, 0], 1, id="zeros_first"), + pytest.param([1, 2, 3], [1, 2, 3], 3, id="no_zeros"), + pytest.param([0, 0, 0], [0, 0, 0], 0, id="all_zeros"), + pytest.param([1, 0, 2, 0, 3, 0], [1, 2, 3, 0, 0, 0], 3, id="interleaved"), + pytest.param([], [], 0, id="empty"), + pytest.param([0], [0], 0, id="single_zero"), + pytest.param([42], [42], 1, id="single_nonzero"), + ], +) def test_move_zeros_to_end_parametrized(input_list, expected_list, expected_index): arr = input_list[:] result_index = move_zeros_to_end(arr) @@ -71,89 +99,54 @@ def test_move_zeros_to_end_parametrized(input_list, expected_list, expected_inde assert result_index == expected_index -@pytest.mark.parametrize("matrix, expected_row", [ - pytest.param( - [[0, 0, 1, 1], - [0, 1, 1, 1], - [0, 0, 0, 1], - [1, 1, 1, 1], - [0, 1, 1, 1]], - 3, - id="classic" - ), - pytest.param( - [[0, 0, 0], - [0, 0, 0], - [0, 0, 0]], - 0, - id="all_zeros" - ), - pytest.param( - [[1, 1, 1], - [1, 1, 1], - [1, 1, 1]], - 0, - id="all_ones_first" - ), - pytest.param( - [[0, 1], - [1, 1]], - 1, - id="two_rows" - ), - pytest.param( - [[0]], - 0, - id="single_zero" - ), - pytest.param( - [[1]], - 0, - id="single_one" - ), - pytest.param( - [], - 0, - id="empty_matrix" - ), - pytest.param( - [[0, 0, 1], - [0, 1, 1], - [0, 1, 1]], - 1, - id="tie" - ), -]) +@pytest.mark.parametrize( + "matrix, expected_row", + [ + pytest.param( + [[0, 0, 1, 1], [0, 1, 1, 1], [0, 0, 0, 1], [1, 1, 1, 1], [0, 1, 1, 1]], 3, id="classic" + ), + pytest.param([[0, 0, 0], [0, 0, 0], [0, 0, 0]], 0, id="all_zeros"), + pytest.param([[1, 1, 1], [1, 1, 1], [1, 1, 1]], 0, id="all_ones_first"), + pytest.param([[0, 1], [1, 1]], 1, id="two_rows"), + pytest.param([[0]], 0, id="single_zero"), + pytest.param([[1]], 0, id="single_one"), + pytest.param([], 0, id="empty_matrix"), + pytest.param([[0, 0, 1], [0, 1, 1], [0, 1, 1]], 1, id="tie"), + ], +) def test_find_row_with_most_ones(matrix, expected_row): assert find_row_with_most_ones(matrix) == expected_row def test_find_row_with_most_ones_big_data(): size = 10000 - matrix = [[0]*size for i in range(size)] - matrix[size-1][size-1] = 1 + matrix = [[0] * size for i in range(size)] + matrix[size - 1][size - 1] = 1 for i in range(50): assert find_row_with_most_ones(matrix) == 9999 size = 10000 - matrix = [[1]*size for i in range(size)] + matrix = [[1] * size for i in range(size)] matrix[0][0] = 0 for i in range(50): assert find_row_with_most_ones(matrix) == 1 -@pytest.mark.parametrize("input_arr, expected", [ - pytest.param([0], 1, id="self_loop"), - pytest.param([1, 0], 1, id="two_cycle"), - pytest.param([1, 2, 0], 1, id="three_cycle"), - pytest.param([0, 1, 2], 3, id="three_self_loops"), - pytest.param([1, 0, 3, 2], 2, id="two_2_cycles"), - pytest.param([2, 0, 1, 4, 3], 2, id="mixed_cycles"), - pytest.param([10, 6, 2, 9, 4, 0, 3, 8, 7, 1, 5], 5, id="mixed_cycles"), - pytest.param([], 0, id="empty"), -]) +@pytest.mark.parametrize( + "input_arr, expected", + [ + pytest.param([0], 1, id="self_loop"), + pytest.param([1, 0], 1, id="two_cycle"), + pytest.param([1, 2, 0], 1, id="three_cycle"), + pytest.param([0, 1, 2], 3, id="three_self_loops"), + pytest.param([1, 0, 3, 2], 2, id="two_2_cycles"), + pytest.param([2, 0, 1, 4, 3], 2, id="mixed_cycles"), + pytest.param([10, 6, 2, 9, 4, 0, 3, 8, 7, 1, 5], 5, id="mixed_cycles"), + pytest.param([], 0, id="empty"), + ], +) def test_count_cycles(input_arr, expected): arr = input_arr[:] assert count_cycles(arr) == expected diff --git a/deprecated_tests/sem01/tests/test_lesson05_tasks.py b/deprecated_tests/sem01/tests/test_lesson05_tasks.py index 72ad6bc8f..a43a2a456 100644 --- a/deprecated_tests/sem01/tests/test_lesson05_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson05_tasks.py @@ -1,4 +1,4 @@ -import pytest +import pytest from solutions.sem01.lesson05.task1 import is_palindrome from solutions.sem01.lesson05.task2 import are_anagrams @@ -7,145 +7,166 @@ from solutions.sem01.lesson05.task5 import reg_validator from solutions.sem01.lesson05.task6 import simplify_path -@pytest.mark.parametrize("s, expected", [ - pytest.param("", True, id="empty_string"), - pytest.param("a", True, id="single_char"), - pytest.param("aa", True, id="two_same"), - pytest.param("ab", False, id="two_different"), - pytest.param("aba", True, id="odd_palindrome"), - pytest.param("abba", True, id="even_palindrome"), - pytest.param("abcba", True, id="long_odd_palindrome"), - pytest.param("abccba", True, id="long_even_palindrome"), - pytest.param("abc", False, id="not_palindrome"), - pytest.param("Aa", True, id="case_sensitive_mismatch"), - pytest.param("Racecar", True, id="real_word_case_sensitive"), - pytest.param("aA", True, id="reverse_case"), - pytest.param("abcdefedcba", True, id="long_true"), - pytest.param("abcdefedcbx", False, id="long_false"), -]) + +@pytest.mark.parametrize( + "s, expected", + [ + pytest.param("", True, id="empty_string"), + pytest.param("a", True, id="single_char"), + pytest.param("aa", True, id="two_same"), + pytest.param("ab", False, id="two_different"), + pytest.param("aba", True, id="odd_palindrome"), + pytest.param("abba", True, id="even_palindrome"), + pytest.param("abcba", True, id="long_odd_palindrome"), + pytest.param("abccba", True, id="long_even_palindrome"), + pytest.param("abc", False, id="not_palindrome"), + pytest.param("Aa", True, id="case_sensitive_mismatch"), + pytest.param("Racecar", True, id="real_word_case_sensitive"), + pytest.param("aA", True, id="reverse_case"), + pytest.param("abcdefedcba", True, id="long_true"), + pytest.param("abcdefedcbx", False, id="long_false"), + ], +) def test_is_palindrome(s, expected): assert is_palindrome(s) == expected -@pytest.mark.parametrize("w1, w2, expected", [ - pytest.param("listen", "silent", True, id="classic_anagram"), - pytest.param("evil", "vile", True, id="another_anagram"), - pytest.param("a", "a", True, id="single_char_same"), - pytest.param("A", "A", True, id="single_upper_same"), - pytest.param("A", "a", False, id="case_sensitive_diff"), - pytest.param("Listen", "Silent", False, id="mixed_case_not_anagram"), - pytest.param("Aa", "aA", True, id="same_chars_permuted"), - pytest.param("Ab", "ab", False, id="one_letter_case_diff"), - pytest.param("abc", "cba", True, id="permuted_same_case"), - pytest.param("abc", "Cba", False, id="case_breaks_anagram"), - pytest.param("aabbcc", "abcabc", True, id="repeated_letters"), - pytest.param("aabbcc", "aabbcd", False, id="extra_different_char"), -]) +@pytest.mark.parametrize( + "w1, w2, expected", + [ + pytest.param("listen", "silent", True, id="classic_anagram"), + pytest.param("evil", "vile", True, id="another_anagram"), + pytest.param("a", "a", True, id="single_char_same"), + pytest.param("A", "A", True, id="single_upper_same"), + pytest.param("A", "a", False, id="case_sensitive_diff"), + pytest.param("Listen", "Silent", False, id="mixed_case_not_anagram"), + pytest.param("Aa", "aA", True, id="same_chars_permuted"), + pytest.param("Ab", "ab", False, id="one_letter_case_diff"), + pytest.param("abc", "cba", True, id="permuted_same_case"), + pytest.param("abc", "Cba", False, id="case_breaks_anagram"), + pytest.param("aabbcc", "abcabc", True, id="repeated_letters"), + pytest.param("aabbcc", "aabbcd", False, id="extra_different_char"), + ], +) def test_are_anagrams_linear(w1, w2, expected): assert are_anagrams(w1, w2) == expected -@pytest.mark.parametrize("s, expected", [ - pytest.param("!!!", True, id="only_exclamations"), - pytest.param("...?", True, id="dots_and_question"), - pytest.param("", False, id="empty_string"), - pytest.param("a", False, id="letter"), - pytest.param("1", False, id="digit"), - pytest.param(" ! ", False, id="space_inside"), - pytest.param("!?.", True, id="symbols_only"), - pytest.param("!a!", False, id="letter_in_middle"), - pytest.param(" ", False, id="only_space"), - pytest.param(".,;", True, id="commas_dots_semicolons"), - pytest.param("", False, id="commas_dots_semicolons"), -]) +@pytest.mark.parametrize( + "s, expected", + [ + pytest.param("!!!", True, id="only_exclamations"), + pytest.param("...?", True, id="dots_and_question"), + pytest.param("", False, id="empty_string"), + pytest.param("a", False, id="letter"), + pytest.param("1", False, id="digit"), + pytest.param(" ! ", False, id="space_inside"), + pytest.param("!?.", True, id="symbols_only"), + pytest.param("!a!", False, id="letter_in_middle"), + pytest.param(" ", False, id="only_space"), + pytest.param(".,;", True, id="commas_dots_semicolons"), + pytest.param("", False, id="commas_dots_semicolons"), + ], +) def test_is_only_punctuation(s, expected): assert is_punctuation(s) == expected -@pytest.mark.parametrize("compressed, expected", [ - pytest.param("AbcD*4 ef GhI*2", "AbcDAbcDAbcDAbcDefGhIGhI", id="example"), - pytest.param("a*3 b*2", "aaabb", id="simple_letters"), - pytest.param("Hello", "Hello", id="star_one"), - pytest.param("xyz", "xyz", id="no_compression"), - pytest.param("", "", id="empty_input"), - pytest.param("Test*2 Space", "TestTestSpace", id="mixed"), - pytest.param("a*10", "aaaaaaaaaa", id="ten_a"), - pytest.param("x y z", "xyz", id="three_plain"), - pytest.param("Word word", "Wordword", id="case_sensitive"), -]) + +@pytest.mark.parametrize( + "compressed, expected", + [ + pytest.param("AbcD*4 ef GhI*2", "AbcDAbcDAbcDAbcDefGhIGhI", id="example"), + pytest.param("a*3 b*2", "aaabb", id="simple_letters"), + pytest.param("Hello", "Hello", id="star_one"), + pytest.param("xyz", "xyz", id="no_compression"), + pytest.param("", "", id="empty_input"), + pytest.param("Test*2 Space", "TestTestSpace", id="mixed"), + pytest.param("a*10", "aaaaaaaaaa", id="ten_a"), + pytest.param("x y z", "xyz", id="three_plain"), + pytest.param("Word word", "Wordword", id="case_sensitive"), + ], +) def test_decompress(compressed, expected): assert unzip(compressed) == expected -@pytest.mark.parametrize("regexp, s, expected", [ - pytest.param("d", "123", True, id="d_valid_number"), - pytest.param("d", "0", True, id="d_zero"), - pytest.param("d", "abc", False, id="d_letters_instead_of_digits"), - pytest.param("d", "", False, id="d_empty_string"), - pytest.param("w", "hello", True, id="w_lowercase_word"), - pytest.param("w", "HelloWorld", True, id="w_mixed_case_word"), - pytest.param("w", "hello123", False, id="w_word_with_digits"), - pytest.param("w", "", False, id="w_empty_string"), - pytest.param("s", "abc123", True, id="s_alphanum"), - pytest.param("s", "ABC99", True, id="s_uppercase_and_digits"), - pytest.param("s", "abc_123", False, id="s_contains_underscore"), - pytest.param("s", "", False, id="s_empty_string"), - pytest.param("d-d", "12-34", True, id="d_dash_d_valid"), - pytest.param("d-d", "12--34", False, id="d_dash_d_double_dash"), - pytest.param("d-d", "12-abc", False, id="d_dash_d_letters_after_dash"), - pytest.param("d-d", "1234", False, id="d_dash_d_missing_dash"), - pytest.param("w.w", "hi.there", True, id="w_dot_w_valid"), - pytest.param("w.w", "hi..there", False, id="w_dot_w_double_dot"), - pytest.param("w.w", "hi1.there", False, id="w_dot_w_digit_in_first_word"), - pytest.param("s.s", "h1i.th32ere", True, id="s_dot_s_valid"), - pytest.param("s.s", "hi4..t2here", False, id="s_dot_s_double_dot"), - pytest.param("d-dw", "12-45abc", True, id="example_valid"), - pytest.param("d-dw", "1-abs", False, id="example_second_part_not_digit"), - pytest.param("d-dw", "1-b123r", False, id="example_letter_after_dash"), - pytest.param("d-dw", "1--123vdg", False, id="example_double_dash"), - pytest.param("d-dw", "123-456XYZ", True, id="d-dw_all_caps"), - pytest.param("d-dw", "0-0a", True, id="d-dw_minimal_valid"), - pytest.param("d@d", "5@7", True, id="d_at_d_valid"), - pytest.param("d@d", "5@@7", False, id="d_at_d_double_at"), - pytest.param("w s", "hi 123", True, id="w_space_s_valid"), - pytest.param("w s", "hi123", False, id="w_space_s_missing_space"), - pytest.param("w s", "hi 123!", False, id="w_space_s_extra_char_in_s"), - pytest.param("", "", True, id="empty_regexp_empty_string"), - pytest.param("", "a", False, id="empty_regexp_non_empty_string"), - pytest.param("d", "", False, id="non_empty_regexp_empty_string"), - pytest.param("d!", "5!", True, id="d_exclam_valid"), - pytest.param("d!", "5", False, id="d_exclam_missing_exclam"), - pytest.param("d!", "5!!", False, id="d_exclam_extra_exclam"), - pytest.param("s", "a1", True, id="s_letter_digit"), - pytest.param("s", "1a", True, id="s_digit_letter"), - pytest.param("s", "a!1", False, id="s_contains_exclamation"), - pytest.param("d-w-s", "123-abc-XY1Z23", True, id="d_w_s_valid"), - pytest.param("d-w-s", "123-abc-XYZ_123", False, id="d_w_s_underscore_in_s"), -]) + +@pytest.mark.parametrize( + "regexp, s, expected", + [ + pytest.param("d", "123", True, id="d_valid_number"), + pytest.param("d", "0", True, id="d_zero"), + pytest.param("d", "abc", False, id="d_letters_instead_of_digits"), + pytest.param("d", "", False, id="d_empty_string"), + pytest.param("w", "hello", True, id="w_lowercase_word"), + pytest.param("w", "HelloWorld", True, id="w_mixed_case_word"), + pytest.param("w", "hello123", False, id="w_word_with_digits"), + pytest.param("w", "", False, id="w_empty_string"), + pytest.param("s", "abc123", True, id="s_alphanum"), + pytest.param("s", "ABC99", True, id="s_uppercase_and_digits"), + pytest.param("s", "abc_123", False, id="s_contains_underscore"), + pytest.param("s", "", False, id="s_empty_string"), + pytest.param("d-d", "12-34", True, id="d_dash_d_valid"), + pytest.param("d-d", "12--34", False, id="d_dash_d_double_dash"), + pytest.param("d-d", "12-abc", False, id="d_dash_d_letters_after_dash"), + pytest.param("d-d", "1234", False, id="d_dash_d_missing_dash"), + pytest.param("w.w", "hi.there", True, id="w_dot_w_valid"), + pytest.param("w.w", "hi..there", False, id="w_dot_w_double_dot"), + pytest.param("w.w", "hi1.there", False, id="w_dot_w_digit_in_first_word"), + pytest.param("s.s", "h1i.th32ere", True, id="s_dot_s_valid"), + pytest.param("s.s", "hi4..t2here", False, id="s_dot_s_double_dot"), + pytest.param("d-dw", "12-45abc", True, id="example_valid"), + pytest.param("d-dw", "1-abs", False, id="example_second_part_not_digit"), + pytest.param("d-dw", "1-b123r", False, id="example_letter_after_dash"), + pytest.param("d-dw", "1--123vdg", False, id="example_double_dash"), + pytest.param("d-dw", "123-456XYZ", True, id="d-dw_all_caps"), + pytest.param("d-dw", "0-0a", True, id="d-dw_minimal_valid"), + pytest.param("d@d", "5@7", True, id="d_at_d_valid"), + pytest.param("d@d", "5@@7", False, id="d_at_d_double_at"), + pytest.param("w s", "hi 123", True, id="w_space_s_valid"), + pytest.param("w s", "hi123", False, id="w_space_s_missing_space"), + pytest.param("w s", "hi 123!", False, id="w_space_s_extra_char_in_s"), + pytest.param("", "", True, id="empty_regexp_empty_string"), + pytest.param("", "a", False, id="empty_regexp_non_empty_string"), + pytest.param("d", "", False, id="non_empty_regexp_empty_string"), + pytest.param("d!", "5!", True, id="d_exclam_valid"), + pytest.param("d!", "5", False, id="d_exclam_missing_exclam"), + pytest.param("d!", "5!!", False, id="d_exclam_extra_exclam"), + pytest.param("s", "a1", True, id="s_letter_digit"), + pytest.param("s", "1a", True, id="s_digit_letter"), + pytest.param("s", "a!1", False, id="s_contains_exclamation"), + pytest.param("d-w-s", "123-abc-XY1Z23", True, id="d_w_s_valid"), + pytest.param("d-w-s", "123-abc-XYZ_123", False, id="d_w_s_underscore_in_s"), + ], +) def test_match_pattern(regexp, s, expected): assert reg_validator(regexp, s) == expected -@pytest.mark.parametrize("path, expected", [ - pytest.param("/home/", "/home", id="trailing_slash"), - pytest.param("/../", "", id="go_above_root"), - pytest.param("/home//foo/", "/home/foo", id="double_slash"), - pytest.param("/home/./foo/", "/home/foo", id="current_dir_dot"), - pytest.param("/./././", "/", id="only_dots_and_slashes"), - pytest.param("/a/./b/../../c/", "/c", id="complex_up_and_down"), - pytest.param("/a/b/c/../../../", "/", id="back_to_root"), - pytest.param("/", "/", id="root_only"), - pytest.param("/.", "/", id="root_with_dot"), - pytest.param("/..", "", id="root_with_double_dot"), - pytest.param("/...", "/...", id="triple_dot_as_name"), - pytest.param("/..a", "/..a", id="dot_dot_a_as_name"), - pytest.param("/a.b/c.d", "/a.b/c.d", id="names_with_dots"), - pytest.param("/a//b////c/d//././/..", "/a/b/c", id="messy_path"), - pytest.param("/a/./b/./c/./d", "/a/b/c/d", id="dots_everywhere"), - pytest.param("/a/./b/../../c/./d/", "/c/d", id="up_down_with_dots"), - pytest.param("/../foo", "", id="up_then_valid"), - pytest.param("/../../foo", "", id="multiple_up_then_valid"), - pytest.param("/../../../", "", id="three_up_from_root"), - pytest.param("/home/foo/./../../../", "", id="too_many_up"), - pytest.param("/_a.b/c__1/..", "/_a.b", id="names_with_underscores_and_dots"), -]) +@pytest.mark.parametrize( + "path, expected", + [ + pytest.param("/home/", "/home", id="trailing_slash"), + pytest.param("/../", "", id="go_above_root"), + pytest.param("/home//foo/", "/home/foo", id="double_slash"), + pytest.param("/home/./foo/", "/home/foo", id="current_dir_dot"), + pytest.param("/./././", "/", id="only_dots_and_slashes"), + pytest.param("/a/./b/../../c/", "/c", id="complex_up_and_down"), + pytest.param("/a/b/c/../../../", "/", id="back_to_root"), + pytest.param("/", "/", id="root_only"), + pytest.param("/.", "/", id="root_with_dot"), + pytest.param("/..", "", id="root_with_double_dot"), + pytest.param("/...", "/...", id="triple_dot_as_name"), + pytest.param("/..a", "/..a", id="dot_dot_a_as_name"), + pytest.param("/a.b/c.d", "/a.b/c.d", id="names_with_dots"), + pytest.param("/a//b////c/d//././/..", "/a/b/c", id="messy_path"), + pytest.param("/a/./b/./c/./d", "/a/b/c/d", id="dots_everywhere"), + pytest.param("/a/./b/../../c/./d/", "/c/d", id="up_down_with_dots"), + pytest.param("/../foo", "", id="up_then_valid"), + pytest.param("/../../foo", "", id="multiple_up_then_valid"), + pytest.param("/../../../", "", id="three_up_from_root"), + pytest.param("/home/foo/./../../../", "", id="too_many_up"), + pytest.param("/_a.b/c__1/..", "/_a.b", id="names_with_underscores_and_dots"), + ], +) def test_simplify_path(path, expected): - assert simplify_path(path) == expected \ No newline at end of file + assert simplify_path(path) == expected diff --git a/deprecated_tests/sem01/tests/test_lesson06_tasks.py b/deprecated_tests/sem01/tests/test_lesson06_tasks.py index 707d6609f..e20a8dd81 100644 --- a/deprecated_tests/sem01/tests/test_lesson06_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson06_tasks.py @@ -1,4 +1,4 @@ -import pytest +import pytest from solutions.sem01.lesson06.task1 import int_to_roman from solutions.sem01.lesson06.task2 import get_len_of_longest_substring @@ -6,103 +6,119 @@ from solutions.sem01.lesson06.task4 import count_unique_words -@pytest.mark.parametrize("num, expected", [ - pytest.param(1, "I", id="one"), - pytest.param(2, "II", id="two"), - pytest.param(3, "III", id="three"), - pytest.param(4, "IV", id="four"), - pytest.param(5, "V", id="five"), - pytest.param(6, "VI", id="six"), - pytest.param(9, "IX", id="nine"), - pytest.param(10, "X", id="ten"), - pytest.param(11, "XI", id="eleven"), - pytest.param(14, "XIV", id="fourteen"), - pytest.param(19, "XIX", id="nineteen"), - pytest.param(27, "XXVII", id="twenty_seven"), - pytest.param(40, "XL", id="forty"), - pytest.param(44, "XLIV", id="forty_four"), - pytest.param(50, "L", id="fifty"), - pytest.param(58, "LVIII", id="fifty_eight"), - pytest.param(90, "XC", id="ninety"), - pytest.param(99, "XCIX", id="ninety_nine"), - pytest.param(100, "C", id="hundred"), - pytest.param(400, "CD", id="four_hundred"), - pytest.param(500, "D", id="five_hundred"), - pytest.param(900, "CM", id="nine_hundred"), - pytest.param(1000, "M", id="thousand"), - pytest.param(1994, "MCMXCIV", id="mcmxciv"), - pytest.param(3999, "MMMCMXCIX", id="max_value"), - pytest.param(2023, "MMXXIII", id="current_year"), - pytest.param(1984, "MCMLXXXIV", id="classic"), -]) +@pytest.mark.parametrize( + "num, expected", + [ + pytest.param(1, "I", id="one"), + pytest.param(2, "II", id="two"), + pytest.param(3, "III", id="three"), + pytest.param(4, "IV", id="four"), + pytest.param(5, "V", id="five"), + pytest.param(6, "VI", id="six"), + pytest.param(9, "IX", id="nine"), + pytest.param(10, "X", id="ten"), + pytest.param(11, "XI", id="eleven"), + pytest.param(14, "XIV", id="fourteen"), + pytest.param(19, "XIX", id="nineteen"), + pytest.param(27, "XXVII", id="twenty_seven"), + pytest.param(40, "XL", id="forty"), + pytest.param(44, "XLIV", id="forty_four"), + pytest.param(50, "L", id="fifty"), + pytest.param(58, "LVIII", id="fifty_eight"), + pytest.param(90, "XC", id="ninety"), + pytest.param(99, "XCIX", id="ninety_nine"), + pytest.param(100, "C", id="hundred"), + pytest.param(400, "CD", id="four_hundred"), + pytest.param(500, "D", id="five_hundred"), + pytest.param(900, "CM", id="nine_hundred"), + pytest.param(1000, "M", id="thousand"), + pytest.param(1994, "MCMXCIV", id="mcmxciv"), + pytest.param(3999, "MMMCMXCIX", id="max_value"), + pytest.param(2023, "MMXXIII", id="current_year"), + pytest.param(1984, "MCMLXXXIV", id="classic"), + ], +) def test_int_to_roman(num, expected): assert int_to_roman(num) == expected -@pytest.mark.parametrize("s, expected", [ - pytest.param("", 0, id="empty_string"), - pytest.param("a", 1, id="single_char"), - pytest.param("aa", 1, id="two_same_chars"), - pytest.param("ab", 2, id="two_different_chars"), - pytest.param("abcabcbb", 3, id="classic_example_abc"), - pytest.param("bbbbb", 1, id="all_same"), - pytest.param("pwwkew", 3, id="pwwkew_example"), - pytest.param("abcdef", 6, id="all_unique"), - pytest.param("abcabcbbxyz", 4, id="long_tail_unique"), - pytest.param("aab", 2, id="aab"), - pytest.param("dvdf", 3, id="dvdf"), - pytest.param(" ", 1, id="single_space"), - pytest.param("a b c", 3, id="letters_and_spaces_unique"), - pytest.param("a b a", 3, id="space_in_middle_with_repeat"), - pytest.param("1234567890", 10, id="digits_all_unique"), - pytest.param("112233", 2, id="repeating_digits"), - pytest.param("abcdefghijklmnopqrstuvwxyz", 26, id="all_lowercase_letters"), - pytest.param("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ", 63, id="max_unique_set"), - pytest.param("a" * 10000, 1, id="ten_thousand_same"), - pytest.param("abc" * 3333 + "d", 4, id="long_repeating_with_new_char"), -]) + +@pytest.mark.parametrize( + "s, expected", + [ + pytest.param("", 0, id="empty_string"), + pytest.param("a", 1, id="single_char"), + pytest.param("aa", 1, id="two_same_chars"), + pytest.param("ab", 2, id="two_different_chars"), + pytest.param("abcabcbb", 3, id="classic_example_abc"), + pytest.param("bbbbb", 1, id="all_same"), + pytest.param("pwwkew", 3, id="pwwkew_example"), + pytest.param("abcdef", 6, id="all_unique"), + pytest.param("abcabcbbxyz", 4, id="long_tail_unique"), + pytest.param("aab", 2, id="aab"), + pytest.param("dvdf", 3, id="dvdf"), + pytest.param(" ", 1, id="single_space"), + pytest.param("a b c", 3, id="letters_and_spaces_unique"), + pytest.param("a b a", 3, id="space_in_middle_with_repeat"), + pytest.param("1234567890", 10, id="digits_all_unique"), + pytest.param("112233", 2, id="repeating_digits"), + pytest.param("abcdefghijklmnopqrstuvwxyz", 26, id="all_lowercase_letters"), + pytest.param( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ", + 63, + id="max_unique_set", + ), + pytest.param("a" * 10000, 1, id="ten_thousand_same"), + pytest.param("abc" * 3333 + "d", 4, id="long_repeating_with_new_char"), + ], +) def test_get_len_of_longest_substring(s, expected): assert get_len_of_longest_substring(s) == expected -@pytest.mark.parametrize("nums, k, expected", [ - pytest.param([23, 2, 4, 6, 7], 6, True, id="subarray_2_4_sum_6"), - pytest.param([23, 2, 6, 4, 7], 6, True, id="total_sum_42_div_by_6"), - pytest.param([23, 2, 6, 4, 7], 13, False, id="no_valid_subarray"), - pytest.param([0, 0], 1, True, id="two_zeros_any_k"), - pytest.param([1, 0], 2, False, id="length_2_sum_1_not_div_by_2"), - pytest.param([1, 2, 3], 5, True, id="subarray_2_3_sum_5"), - pytest.param([1], 1, False, id="single_element_too_short"), - pytest.param([5, 0, 0], 3, True, id="zeros_after_nonzero"), - pytest.param([1, 2], 3, True, id="exact_sum_equals_k"), - pytest.param([1, 2], 4, False, id="sum_not_divisible_by_k"), - pytest.param([0], 1, False, id="single_zero_too_short"), - pytest.param([1, 0, 2], 2, True, id="subarray_0_2_sum_2"), - pytest.param([4, 2, 4], 6, True, id="first_two_sum_6"), - pytest.param([1, 1, 1], 2, True, id="first_two_ones_sum_2"), - pytest.param([1, 2, 4, 8], 8, False, id="no_subarray_divisible_by_8"), - pytest.param([0, 1, 0], 2, False, id="zeros_with_one_sum_1"), - pytest.param([0, 1, 0, 0], 2, True, id="last_two_zeros_sum_0_div_by_any"), -]) + +@pytest.mark.parametrize( + "nums, k, expected", + [ + pytest.param([23, 2, 4, 6, 7], 6, True, id="subarray_2_4_sum_6"), + pytest.param([23, 2, 6, 4, 7], 6, True, id="total_sum_42_div_by_6"), + pytest.param([23, 2, 6, 4, 7], 13, False, id="no_valid_subarray"), + pytest.param([0, 0], 1, True, id="two_zeros_any_k"), + pytest.param([1, 0], 2, False, id="length_2_sum_1_not_div_by_2"), + pytest.param([1, 2, 3], 5, True, id="subarray_2_3_sum_5"), + pytest.param([1], 1, False, id="single_element_too_short"), + pytest.param([5, 0, 0], 3, True, id="zeros_after_nonzero"), + pytest.param([1, 2], 3, True, id="exact_sum_equals_k"), + pytest.param([1, 2], 4, False, id="sum_not_divisible_by_k"), + pytest.param([0], 1, False, id="single_zero_too_short"), + pytest.param([1, 0, 2], 2, True, id="subarray_0_2_sum_2"), + pytest.param([4, 2, 4], 6, True, id="first_two_sum_6"), + pytest.param([1, 1, 1], 2, True, id="first_two_ones_sum_2"), + pytest.param([1, 2, 4, 8], 8, False, id="no_subarray_divisible_by_8"), + pytest.param([0, 1, 0], 2, False, id="zeros_with_one_sum_1"), + pytest.param([0, 1, 0, 0], 2, True, id="last_two_zeros_sum_0_div_by_any"), + ], +) def test_is_there_any_good_subarray(nums, k, expected): assert is_there_any_good_subarray(nums, k) == expected -import pytest - -@pytest.mark.parametrize("text, expected", [ - pytest.param("", 0, id="empty_string"), - pytest.param(" ", 0, id="only_spaces"), - pytest.param("hello", 1, id="single_word"), - pytest.param("Hello hello", 1, id="case_insensitive"), - pytest.param("Hello, world!", 2, id="punctuation_around"), - pytest.param("Hello, hello, world!", 2, id="duplicates_with_punct"), - pytest.param("The quick brown fox jumps over the lazy dog.", 8, id="classic_pangram"), - pytest.param("!!! ???", 0, id="only_punctuation"), - pytest.param("word1 word2 word1", 2, id="digits_in_words"), - pytest.param("Don't stop believing!", 3, id="apostrophe_inside"), - pytest.param(" Hello , World ! ", 2, id="extra_whitespace"), - pytest.param("A a A a", 1, id="repeated_case_variants"), - pytest.param("word... word!!!", 1, id="multiple_punct_at_end"), - pytest.param("123 456 123", 2, id="numbers_as_words"), -]) +@pytest.mark.parametrize( + "text, expected", + [ + pytest.param("", 0, id="empty_string"), + pytest.param(" ", 0, id="only_spaces"), + pytest.param("hello", 1, id="single_word"), + pytest.param("Hello hello", 1, id="case_insensitive"), + pytest.param("Hello, world!", 2, id="punctuation_around"), + pytest.param("Hello, hello, world!", 2, id="duplicates_with_punct"), + pytest.param("The quick brown fox jumps over the lazy dog.", 8, id="classic_pangram"), + pytest.param("!!! ???", 0, id="only_punctuation"), + pytest.param("word1 word2 word1", 2, id="digits_in_words"), + pytest.param("Don't stop believing!", 3, id="apostrophe_inside"), + pytest.param(" Hello , World ! ", 2, id="extra_whitespace"), + pytest.param("A a A a", 1, id="repeated_case_variants"), + pytest.param("word... word!!!", 1, id="multiple_punct_at_end"), + pytest.param("123 456 123", 2, id="numbers_as_words"), + ], +) def test_count_unique_words(text, expected): - assert count_unique_words(text) == expected \ No newline at end of file + assert count_unique_words(text) == expected diff --git a/deprecated_tests/sem01/tests/test_lesson08_tasks.py b/deprecated_tests/sem01/tests/test_lesson08_tasks.py index 962ba4bd2..06f04319d 100644 --- a/deprecated_tests/sem01/tests/test_lesson08_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson08_tasks.py @@ -1,10 +1,10 @@ -import pytest import math import time from solutions.sem01.lesson08.task1 import make_averager from solutions.sem01.lesson08.task2 import collect_statistic + def test_make_averager(): get_avg = make_averager(2) @@ -15,6 +15,7 @@ def test_make_averager(): assert math.isclose(get_avg(5), 1) assert math.isclose(get_avg(5), 5) + def test_make_averager2(): get_avg = make_averager(5) @@ -27,6 +28,7 @@ def test_make_averager2(): assert math.isclose(get_avg(-7), 0) assert math.isclose(get_avg(-2), -1) + def test_collect_statistic(): statistics: list[str, list[float, int]] = {} @@ -37,7 +39,7 @@ def func1() -> None: @collect_statistic(statistics) def func2() -> None: time.sleep(0.1) - + for _ in range(3): func1() @@ -58,10 +60,11 @@ def test_collect_statistic_inout(): @collect_statistic(statistics) def func(a, b, *, c, d): return a + b + c + d - + assert func(1, 2, c=3, d=4) == 10 assert statistics[func.__name__][1] == 1 + def test_collect_statistic_count_call(): statistics: list[str, list[float, int]] = {} @@ -76,7 +79,7 @@ def func(): count_call += 1 return func - + func = func_fab() func() - assert statistics[func.__name__][1] == 1 \ No newline at end of file + assert statistics[func.__name__][1] == 1 diff --git a/deprecated_tests/sem01/tests/test_lesson11_tasks.py b/deprecated_tests/sem01/tests/test_lesson11_tasks.py index d0cd02efc..941eed777 100644 --- a/deprecated_tests/sem01/tests/test_lesson11_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson11_tasks.py @@ -1,9 +1,8 @@ -import pytest - import math import sys from io import StringIO +import pytest from solutions.sem01.lesson11.task1 import Vector2D @@ -84,13 +83,12 @@ def test_print(): sys.stdout = old_stdout output = captured_output.getvalue() assert ( - output == "Vector2D(abscissa=1, ordinate=-2)" + output == "Vector2D(abscissa=1, ordinate=-2)" or output == "Vector2D(abscissa=1., ordinate=-2.)" - or output == "Vector2D(abscissa=1.0, ordinate=-2.0)" + or output == "Vector2D(abscissa=1.0, ordinate=-2.0)" ) - @pytest.mark.parametrize( "abscissa1, ordinate1, abscissa2, ordinate2, expected", [ diff --git a/deprecated_tests/sem01/tests_hw/test_hw1_tasks.py b/deprecated_tests/sem01/tests_hw/test_hw1_tasks.py index 0ecf8a108..c600f76d5 100644 --- a/deprecated_tests/sem01/tests_hw/test_hw1_tasks.py +++ b/deprecated_tests/sem01/tests_hw/test_hw1_tasks.py @@ -1,17 +1,15 @@ -import pytest import uuid -from unittest.mock import MagicMock, patch, Mock +from unittest.mock import MagicMock, Mock, patch + +import pytest -from homeworks.sem01.hw1.aggregate_segmentation import aggregate_segmentation, ALLOWED_TYPES +from homeworks.sem01.hw1.aggregate_segmentation import ALLOWED_TYPES, aggregate_segmentation from homeworks.sem01.hw1.backoff import backoff from homeworks.sem01.hw1.cache import lru_cache from homeworks.sem01.hw1.convert_exception import convert_exceptions_to_api_compitable_ones -from .hw1_test_data.cache_test_data import ( - TESTCASE_DATA, - TESTCASE_IDS, -) -NAME_BACKOFF_MODULE = "homeworks.hw1.backoff" # название модуля с backoff +NAME_BACKOFF_MODULE = "homeworks.hw1.backoff" # название модуля с backoff + def test_valid_segments() -> None: """Тест: валидные сегменты.""" @@ -32,53 +30,53 @@ def test_valid_segments() -> None: "segment_id": segment_id_1, "segment_start": 0.0, "segment_end": 1.0, - "type": list_allow_types[0] + "type": list_allow_types[0], }, { "audio_id": audio_id_1, "segment_id": segment_id_2, "segment_start": 2.5, "segment_end": 3.5, - "type": list_allow_types[1] + "type": list_allow_types[1], }, { "audio_id": audio_id_2, "segment_id": segment_id_3, "segment_start": 4.5, "segment_end": 4.6, - "type": list_allow_types[0] + "type": list_allow_types[0], }, { "audio_id": audio_id_2, "segment_id": segment_id_4, "segment_start": 5.5, "segment_end": 6.5, - "type": list_allow_types[1] + "type": list_allow_types[1], }, { "audio_id": audio_id_3, "segment_id": segment_id_5, "segment_start": None, "segment_end": None, - "type": None + "type": None, }, { "audio_id": "audio3", "segment_id": "seg5", "segment_start": 0.0, "segment_end": 1.0, - "type": "invalid_type" + "type": "invalid_type", }, ] expected_valid = { audio_id_1: { segment_id_1: {"start": 0.0, "end": 1.0, "type": list_allow_types[0]}, - segment_id_2: {"start": 2.5, "end": 3.5, "type": list_allow_types[1]} + segment_id_2: {"start": 2.5, "end": 3.5, "type": list_allow_types[1]}, }, audio_id_2: { segment_id_3: {"start": 4.5, "end": 4.6, "type": list_allow_types[0]}, - segment_id_4: {"start": 5.5, "end": 6.5, "type": list_allow_types[1]} + segment_id_4: {"start": 5.5, "end": 6.5, "type": list_allow_types[1]}, }, audio_id_3: {}, } @@ -88,6 +86,7 @@ def test_valid_segments() -> None: assert result_valid == expected_valid assert result_forbidden == expected_forbidden + def test_convert_matching_exception() -> None: """Тест: исключение заменяется на API-совместимое.""" @@ -97,7 +96,7 @@ class ApiValueError(Exception): @convert_exceptions_to_api_compitable_ones({ValueError: ApiValueError}) def func(): raise ValueError("Внутренняя ошибка") - + @convert_exceptions_to_api_compitable_ones({ValueError: ApiValueError}) def func2(): raise KeyError("Внутренняя ошибка") @@ -108,7 +107,8 @@ def func2(): with pytest.raises(KeyError): func2() -@patch(NAME_BACKOFF_MODULE + '.sleep') + +@patch(NAME_BACKOFF_MODULE + ".sleep") def test_exponential_backoff_and_jitter(mock_sleep: MagicMock) -> None: """Тест: задержки увеличиваются, но не выше timeout_max и к ним добавляется дрожь.""" attempts = 0 @@ -116,12 +116,7 @@ def test_exponential_backoff_and_jitter(mock_sleep: MagicMock) -> None: retry_amount = 4 timeouts = [1, 2, 4, 4] - @backoff( - retry_amount=retry_amount, - timeout_start=1, - timeout_max=timeout_max, - backoff_scale=2.0 - ) + @backoff(retry_amount=retry_amount, timeout_start=1, timeout_max=timeout_max, backoff_scale=2.0) def func(): nonlocal attempts attempts += 1 @@ -138,22 +133,23 @@ def func(): for av_time, args in zip(timeouts, args_list): count_more_av_time += args > av_time assert av_time <= args <= av_time + 0.5 - - assert count_more_av_time # есть добавление "дрожи" + + assert count_more_av_time # есть добавление "дрожи" + def test_success() -> None: capacity = 2 - call_args = [ + call_args = [ (1, 2), (1, 2), (2, 2), ] call_count_expected = 2 - + mock_func = Mock() func_cached = lru_cache(capacity=capacity)(mock_func) for args in call_args: func_cached(args) - assert mock_func.call_count == call_count_expected \ No newline at end of file + assert mock_func.call_count == call_count_expected diff --git a/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py b/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py index 37d249b77..81312dea0 100644 --- a/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py +++ b/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py @@ -333,12 +333,8 @@ class TestTask2: ), ], ) - def test_get_dominant_color_info( - self, image, threshold, expected_color, expected_ratio - ): - color, ratio_percent = get_dominant_color_info( - image.astype(np.uint8), threshold - ) + def test_get_dominant_color_info(self, image, threshold, expected_color, expected_ratio): + color, ratio_percent = get_dominant_color_info(image.astype(np.uint8), threshold) assert color in expected_color assert (abs(ratio_percent - expected_ratio * 100) < 1e-6) or ( diff --git a/deprecated_tests/sem02/tests/task5/test_lesson05_tasks.py b/deprecated_tests/sem02/tests/task5/test_lesson05_tasks.py index aeb37ebcc..4cc9188d4 100644 --- a/deprecated_tests/sem02/tests/task5/test_lesson05_tasks.py +++ b/deprecated_tests/sem02/tests/task5/test_lesson05_tasks.py @@ -89,9 +89,7 @@ class TestTask1: ), ], ) - def test_can_satisfy_demand( - self, costs, resource_amounts, demand_expected, expected - ): + def test_can_satisfy_demand(self, costs, resource_amounts, demand_expected, expected): assert can_satisfy_demand(costs, resource_amounts, demand_expected) == expected def test_can_satisfy_demand_validate(self): @@ -172,9 +170,7 @@ class TestTask2: ), ], ) - def test_get_projections_components( - self, matrix, vector, proj_expected, orth_expected - ): + def test_get_projections_components(self, matrix, vector, proj_expected, orth_expected): projections, orthogonals = get_projections_components(matrix, vector) if proj_expected is None: diff --git a/homeworks/sem01/hw1/backoff.py b/homeworks/sem01/hw1/backoff.py index 696ffa73a..a3373a982 100644 --- a/homeworks/sem01/hw1/backoff.py +++ b/homeworks/sem01/hw1/backoff.py @@ -1,5 +1,3 @@ -from random import uniform -from time import sleep from typing import ( Callable, ParamSpec, diff --git a/requirements-ci.txt b/requirements-ci.txt index 581b008d7..c1860423d 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -4,4 +4,4 @@ pandas==2.2.2 pytest==8.4.2 pytest-cov==7.0.0 -ruff==0.13.0 +ruff==0.13.0 \ No newline at end of file diff --git a/solutions/.DS_Store b/solutions/.DS_Store new file mode 100644 index 000000000..6ccbe3484 Binary files /dev/null and b/solutions/.DS_Store differ diff --git a/solutions/sem01/lesson02/task1.py b/solutions/sem01/lesson02/task1.py index c782dcd88..bb484d934 100644 --- a/solutions/sem01/lesson02/task1.py +++ b/solutions/sem01/lesson02/task1.py @@ -1,4 +1,5 @@ def get_factorial(num: int) -> int: factorial = 1 - # ваш код + for i in range(1, num + 1): + factorial *= i return factorial diff --git a/solutions/sem01/lesson02/task2.py b/solutions/sem01/lesson02/task2.py index b91420c56..6f5d49b93 100644 --- a/solutions/sem01/lesson02/task2.py +++ b/solutions/sem01/lesson02/task2.py @@ -1,4 +1,7 @@ def get_doubled_factorial(num: int) -> int: factorial = 1 - # ваш код - return factorial + if num > 1: + for i in range(1 + int(num % 2 != 1), num + 1, 2): + factorial *= i + return factorial + return 1 diff --git a/solutions/sem01/lesson02/task3.py b/solutions/sem01/lesson02/task3.py index ee2a84ecf..6a55a3f68 100644 --- a/solutions/sem01/lesson02/task3.py +++ b/solutions/sem01/lesson02/task3.py @@ -1,4 +1,8 @@ def get_amount_of_ways_to_climb(stair_amount: int) -> int: step_prev, step_curr = 1, 1 - # ваш код - return step_curr + if stair_amount <= 1: + return 1 + else: + for i in range(2, stair_amount + 1): + step_prev, step_curr = step_curr, step_curr + step_prev + return step_curr diff --git a/solutions/sem01/lesson02/task4.py b/solutions/sem01/lesson02/task4.py index 45ff4bb42..8cb8340b9 100644 --- a/solutions/sem01/lesson02/task4.py +++ b/solutions/sem01/lesson02/task4.py @@ -1,4 +1,9 @@ -def get_multiplications_amount(num: int) -> int: +def get_multiplications_amount(deg: int) -> int: multiplications_amount = 0 - # ваш код + while deg > 1: + if deg % 2 == 0: + deg = deg // 2 + else: + deg -= 1 + multiplications_amount += 1 return multiplications_amount diff --git a/solutions/sem01/lesson02/task5.py b/solutions/sem01/lesson02/task5.py index 8fb9a048d..107546119 100644 --- a/solutions/sem01/lesson02/task5.py +++ b/solutions/sem01/lesson02/task5.py @@ -1,3 +1,7 @@ def get_gcd(num1: int, num2: int) -> int: - # ваш код + while num1 != num2: + if num1 > num2: + num1 -= num2 + else: + num2 -= num1 return num1 diff --git a/solutions/sem01/lesson02/task6.py b/solutions/sem01/lesson02/task6.py index bec4b6cd9..f088121a3 100644 --- a/solutions/sem01/lesson02/task6.py +++ b/solutions/sem01/lesson02/task6.py @@ -1,4 +1,19 @@ +def prime(x): + if x == 1: + return 0 + if x == 2: + return 1 + for t in range(2, int(x**0.5) + 1): + if x % t == 0: + return 0 + return 1 + + def get_sum_of_prime_divisors(num: int) -> int: sum_of_divisors = 0 - # ваш код + for i in range(2, int(num**0.5) + 1): + if num % i == 0 and prime(i): + sum_of_divisors += i + if prime(num): + sum_of_divisors += num return sum_of_divisors diff --git a/solutions/sem01/lesson02/task7.py b/solutions/sem01/lesson02/task7.py index 4b2d73beb..fcf476bbe 100644 --- a/solutions/sem01/lesson02/task7.py +++ b/solutions/sem01/lesson02/task7.py @@ -1,5 +1,22 @@ def is_palindrome(num: int) -> bool: - num_reversed = 0 - num_origin = num - # ваш код - return num_origin == num_reversed + if num < 0: + return False + + elif num < 10: + return True + + else: + num_copy = num + category = 0 + while num_copy > 0: + category += 1 + num_copy = num_copy // 10 + i = 0 + num_copy = num + while i < category: + if num_copy % (10) != (num // (10 ** (category - 1 - i))) % 10: + return False + else: + i += 1 + num_copy //= 10 + return True diff --git a/solutions/sem01/lesson03/task1.py b/solutions/sem01/lesson03/task1.py index f1d8fe26b..2d1a3786b 100644 --- a/solutions/sem01/lesson03/task1.py +++ b/solutions/sem01/lesson03/task1.py @@ -1,3 +1,5 @@ def flip_bits_in_range(num: int, left_bit: int, right_bit: int) -> int: - # ваш код - return num \ No newline at end of file + for i in range(left_bit - 1, right_bit): + xor = 1 << i + num = num ^ xor + return num diff --git a/solutions/sem01/lesson03/task2.py b/solutions/sem01/lesson03/task2.py index a3a738c2a..dd74966a2 100644 --- a/solutions/sem01/lesson03/task2.py +++ b/solutions/sem01/lesson03/task2.py @@ -1,3 +1,23 @@ -def get_cube_root(n: float, eps: float) -> float: - # ваш код - return n \ No newline at end of file +def get_cube_root(num: float, pogreshnost: float) -> float: + if num == 0: + return 0 + if num < 100: + maxi = 100 + else: + maxi = num + flag = False + if num < 0: + flag = True + num = abs(num) + left = 0.0 + right = 2 + num // 3 + while left < right: + m = (right + left) / 2 + if abs(num - m * m * m) < pogreshnost: + if flag: + return -1 * m + return m + elif pogreshnost > num - m * m * m: + right = m - pogreshnost / maxi + elif m * m * m - num < pogreshnost: + left = m + pogreshnost / maxi diff --git a/solutions/sem01/lesson03/task3.py b/solutions/sem01/lesson03/task3.py index 5e91a6ac5..3303d7360 100644 --- a/solutions/sem01/lesson03/task3.py +++ b/solutions/sem01/lesson03/task3.py @@ -1,3 +1,14 @@ def get_nth_digit(num: int) -> int: - # ваш код - return 0 + le = 1 + while True: + first = 10 ** (le - 1) * int(le != 1) + if num <= (b := (10**le - first) // 2 * le): + m = (num - 1) % le + number = first + 2 * ((num - 1) // le) + while number >= 0: + le -= 1 + if le == m: + return number % 10 + number //= 10 + le += 1 + num -= b diff --git a/solutions/sem01/lesson04/task1.py b/solutions/sem01/lesson04/task1.py index 47384423a..3303d7360 100644 --- a/solutions/sem01/lesson04/task1.py +++ b/solutions/sem01/lesson04/task1.py @@ -1,3 +1,14 @@ -def is_arithmetic_progression(lst: list[list[int]]) -> bool: - # ваш код - return False \ No newline at end of file +def get_nth_digit(num: int) -> int: + le = 1 + while True: + first = 10 ** (le - 1) * int(le != 1) + if num <= (b := (10**le - first) // 2 * le): + m = (num - 1) % le + number = first + 2 * ((num - 1) // le) + while number >= 0: + le -= 1 + if le == m: + return number % 10 + number //= 10 + le += 1 + num -= b diff --git a/solutions/sem01/lesson04/task2.py b/solutions/sem01/lesson04/task2.py index 4591d0a3e..09a888dd9 100644 --- a/solutions/sem01/lesson04/task2.py +++ b/solutions/sem01/lesson04/task2.py @@ -1,3 +1,15 @@ def merge_intervals(intervals: list[list[int, int]]) -> list[list[int, int]]: - # ваш код - return [[0,0]] \ No newline at end of file + if len(intervals) == 0: + return [] + intervals = sorted(intervals) + i = 0 + while i != len(intervals) - 1: + if intervals[i][1] >= intervals[i + 1][0]: + if intervals[i][1] <= intervals[i + 1][1]: + intervals[i] = [intervals[i][0], intervals[i + 1][1]] + intervals.pop(i + 1) + else: + intervals.pop(i + 1) + else: + i += 1 + return intervals diff --git a/solutions/sem01/lesson04/task3.py b/solutions/sem01/lesson04/task3.py index 7253f6cbd..d5385ca53 100644 --- a/solutions/sem01/lesson04/task3.py +++ b/solutions/sem01/lesson04/task3.py @@ -1,3 +1,5 @@ def find_single_number(nums: list[int]) -> int: - # ваш код - return 0 + binary_form = 0 + for num in nums: + binary_form ^= num + return binary_form diff --git a/solutions/sem01/lesson04/task4.py b/solutions/sem01/lesson04/task4.py index b21bc5a39..4ed024e40 100644 --- a/solutions/sem01/lesson04/task4.py +++ b/solutions/sem01/lesson04/task4.py @@ -1,3 +1,12 @@ def move_zeros_to_end(nums: list[int]) -> list[int]: - # ваш код - return 0 \ No newline at end of file + j = 0 + ln_nums = len(nums) + hod = 0 + while hod != ln_nums: + if nums[j] == 0: + nums.insert(ln_nums, nums.pop(j)) + hod += 1 + else: + hod += 1 + j += 1 + return j diff --git a/solutions/sem01/lesson04/task5.py b/solutions/sem01/lesson04/task5.py index 02d7742bb..0a9987601 100644 --- a/solutions/sem01/lesson04/task5.py +++ b/solutions/sem01/lesson04/task5.py @@ -1,3 +1,22 @@ def find_row_with_most_ones(matrix: list[list[int]]) -> int: - # ваш код - return 0 \ No newline at end of file + if not matrix: + return 0 + row = 0 + line = len(matrix[0]) - 1 + index = 0 + mini_ind = len(matrix[0]) + + while row != len(matrix): + if line == -1: + return row + + if matrix[row][line] == 0: + if line < mini_ind: + mini_ind = line + index = row + + row += 1 + + if row != len(matrix) and matrix[row][line] == 1: + line -= 1 + return index diff --git a/solutions/sem01/lesson04/task6.py b/solutions/sem01/lesson04/task6.py index 16df27ca6..9fe204963 100644 --- a/solutions/sem01/lesson04/task6.py +++ b/solutions/sem01/lesson04/task6.py @@ -1,3 +1,12 @@ -def count_cycles(arr: list[int]) -> int: - # ваш код - return 0 \ No newline at end of file +def count_cycles(arr: list[int]) -> int: + counter = 0 + visited = [False] * len(arr) + while not all(visited): + start = visited.index(False) + i = start + visited[start] = True + while arr[i] != start: + visited[arr[i]] = True + i = arr[i] + counter += 1 + return counter diff --git a/solutions/sem01/lesson05/task1.py b/solutions/sem01/lesson05/task1.py index 9a17211e5..8d94d0acb 100644 --- a/solutions/sem01/lesson05/task1.py +++ b/solutions/sem01/lesson05/task1.py @@ -1,3 +1,3 @@ def is_palindrome(text: str) -> bool: - # ваш код - return False \ No newline at end of file + text = text.lower() + return text == text[::-1] diff --git a/solutions/sem01/lesson05/task2.py b/solutions/sem01/lesson05/task2.py index 367503802..55dd904e0 100644 --- a/solutions/sem01/lesson05/task2.py +++ b/solutions/sem01/lesson05/task2.py @@ -1,3 +1,19 @@ def are_anagrams(word1: str, word2: str) -> bool: - # ваш код - return False \ No newline at end of file + summa1 = 0 + summa2 = 0 + multiplication1 = 1 + multiplication2 = 1 + if len(word1) == len(word2): + for i in range(len(word1)): + litter1 = word1[i] + litter2 = word2[i] + summa1 += ord(litter1) + summa2 += ord(litter2) + multiplication1 *= ord(litter1) + multiplication2 *= ord(litter2) + if summa1 == summa2 and multiplication1 == multiplication2: + return True + return False + + +print(are_anagrams("Listen", "Silent")) diff --git a/solutions/sem01/lesson05/task3.py b/solutions/sem01/lesson05/task3.py index e368e2f49..16f41f199 100644 --- a/solutions/sem01/lesson05/task3.py +++ b/solutions/sem01/lesson05/task3.py @@ -1,3 +1,8 @@ def is_punctuation(text: str) -> bool: - # ваш код - return False + if len(text) == 0: + return False + symbol_of_punctuation = '!"#$%&()*+,-./:;<=>?@[\]^_{|}~`' + for char in text: + if char not in symbol_of_punctuation and ord(char) != 39: + return False + return True diff --git a/solutions/sem01/lesson05/task4.py b/solutions/sem01/lesson05/task4.py index 4c4e9086e..2b49a984e 100644 --- a/solutions/sem01/lesson05/task4.py +++ b/solutions/sem01/lesson05/task4.py @@ -1,3 +1,20 @@ def unzip(compress_text: str) -> str: - # ваш код - return compress_text \ No newline at end of file + numbers = "0123456789" + litters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + unfolded_text = "" + word = "" + multiplier = "" + for i in range(len(compress_text)): + char = compress_text[i] + if char in litters: + word += char + if char in numbers: + multiplier += char + if char == " " or i == len(compress_text) - 1: + print(word, multiplier) + if multiplier: + unfolded_text += word * (int(multiplier) - 1) + unfolded_text += word + word = "" + multiplier = "" + return unfolded_text diff --git a/solutions/sem01/lesson05/task5.py b/solutions/sem01/lesson05/task5.py index 076c5bb6c..3bf08d158 100644 --- a/solutions/sem01/lesson05/task5.py +++ b/solutions/sem01/lesson05/task5.py @@ -1,3 +1,30 @@ -def reg_validator(reg_expr: str, text: str) -> bool: - # ваш код - return False \ No newline at end of file +def reg_validator(reg_expr: str, text: str) -> bool: + j = 0 + i = 0 + while i < len(text) and j < len(reg_expr): + char = reg_expr[j] + match char: + case "d": + if not text[i].isdigit(): + return False + while i < len(text) and text[i].isdigit(): + i += 1 + j += 1 + case "w": + if not text[i].isalpha(): + return False + while i < len(text) and text[i].isalpha(): + i += 1 + j += 1 + case "s": + if not text[i].isalnum(): + return False + while i < len(text) and text[i].isalnum(): + i += 1 + j += 1 + case _: + if text[i] != char: + return False + i += 1 + j += 1 + return i == len(text) and j == len(reg_expr) diff --git a/solutions/sem01/lesson05/task6.py b/solutions/sem01/lesson05/task6.py index 1b914ada7..a69748c71 100644 --- a/solutions/sem01/lesson05/task6.py +++ b/solutions/sem01/lesson05/task6.py @@ -1,3 +1,17 @@ def simplify_path(path: str) -> str: - # ваш код - return path \ No newline at end of file + result = [] + parts = path.split("/") + for part in parts: + if part == "." or part == "": + continue + elif part == "..": + if result: + result.pop() + else: + return "" + else: + result.append(part) + return "/" + "/".join(result) + + +print(simplify_path("/home/user/./Documents/../Pictures")) diff --git a/solutions/sem01/lesson06/task1.py b/solutions/sem01/lesson06/task1.py index 2d1e30e96..17f399b13 100644 --- a/solutions/sem01/lesson06/task1.py +++ b/solutions/sem01/lesson06/task1.py @@ -1,3 +1,25 @@ def int_to_roman(num: int) -> str: - # ваш код - return "" \ No newline at end of file + numbers = { + "1000": "M", + "900": "CM", + "500": "D", + "400": "CD", + "100": "C", + "90": "XC", + "50": "L", + "40": "XL", + "10": "X", + "9": "IX", + "5": "V", + "4": "IV", + "1": "I", + } + result = "" + for value in numbers.keys(): + while num >= int(value): + result += numbers[value] + num -= int(value) + return result + + +print(int_to_roman(77)) diff --git a/solutions/sem01/lesson06/task2.py b/solutions/sem01/lesson06/task2.py index f535b5a0c..b1ba2a5e8 100644 --- a/solutions/sem01/lesson06/task2.py +++ b/solutions/sem01/lesson06/task2.py @@ -1,3 +1,26 @@ def get_len_of_longest_substring(text: str) -> int: - # ваш код - return 0 \ No newline at end of file + chars_in_text = {} + + start = 0 + max_len_of_longest_substring = 0 + i = 0 + + while start < len(text): + char = text[i] + + if char in chars_in_text: + chars_in_text[char] += 1 + if chars_in_text.setdefault(char): + max_len_of_longest_substring = max(len(chars_in_text), max_len_of_longest_substring) + chars_in_text.clear() + start += 1 + if i == len(text) - 1: + return max_len_of_longest_substring + i = start + + else: + chars_in_text[char] = 1 + if i == len(text) - 1: + return max(max_len_of_longest_substring, i - start + 1) + i += 1 + return max_len_of_longest_substring diff --git a/solutions/sem01/lesson06/task3.py b/solutions/sem01/lesson06/task3.py index 7449a1e72..2c5d6d8cc 100644 --- a/solutions/sem01/lesson06/task3.py +++ b/solutions/sem01/lesson06/task3.py @@ -1,7 +1,14 @@ -def is_there_any_good_subarray( - nums: list[int], - k: int, -) -> bool: - - # ваш код +def is_there_any_good_subarray(nums: list[int], k: int) -> bool: + amount_by_mod_k = {0: -1} + summa = 0 + for index in range(len(nums)): + summa = (summa + nums[index]) % k + if summa in amount_by_mod_k: + if index - amount_by_mod_k[summa] >= 2: + return True + else: + amount_by_mod_k[summa % k] = index return False + + +print(is_there_any_good_subarray([5, 2], 3)) diff --git a/solutions/sem01/lesson06/task4.py b/solutions/sem01/lesson06/task4.py index 5b75a110c..2c5d6d8cc 100644 --- a/solutions/sem01/lesson06/task4.py +++ b/solutions/sem01/lesson06/task4.py @@ -1,3 +1,14 @@ -def count_unique_words(text: str) -> int: - # ваш код - return 0 \ No newline at end of file +def is_there_any_good_subarray(nums: list[int], k: int) -> bool: + amount_by_mod_k = {0: -1} + summa = 0 + for index in range(len(nums)): + summa = (summa + nums[index]) % k + if summa in amount_by_mod_k: + if index - amount_by_mod_k[summa] >= 2: + return True + else: + amount_by_mod_k[summa % k] = index + return False + + +print(is_there_any_good_subarray([5, 2], 3)) diff --git a/solutions/sem01/lesson08/task1.py b/solutions/sem01/lesson08/task1.py index 4390f6c84..0b762f3a9 100644 --- a/solutions/sem01/lesson08/task1.py +++ b/solutions/sem01/lesson08/task1.py @@ -1,5 +1,23 @@ +import collections from typing import Callable + def make_averager(accumulation_period: int) -> Callable[[float], float]: - # ваш код - pass \ No newline at end of file + summa = 0 + number = 0 + deq = collections.deque() + + def get_avg(value: float) -> float: + nonlocal summa, number, deq + if number < accumulation_period: + number += 1 + print(number) + else: + summa -= deq.popleft() + print(number) + summa += value + deq.append(value) + print(deq) + return summa / number + + return get_avg diff --git a/solutions/sem01/lesson08/task2.py b/solutions/sem01/lesson08/task2.py index 6e4af8707..0422def68 100644 --- a/solutions/sem01/lesson08/task2.py +++ b/solutions/sem01/lesson08/task2.py @@ -1,10 +1,33 @@ +import functools +import time from typing import Callable, TypeVar T = TypeVar("T") -def collect_statistic( - statistics: dict[str, list[float, int]] -) -> Callable[[T], T]: - - # ваш код - pass \ No newline at end of file + +def collect_statistic(statistics: dict[str, list[float | int]]) -> Callable[[T], T]: + def decorator(func: Callable[[T], T]) -> Callable[[T], T]: + func_name = func.__name__ + summa = 0 + counter = 0 + + @functools.wraps(func) + def wrapper(*args: T, **kwargs: T) -> T: + nonlocal summa, counter + start = time.time() + result = func(*args, **kwargs) + end = time.time() + + working_time = end - start + summa += working_time + counter += 1 + + if func_name not in statistics: + statistics[func_name] = [working_time, counter] + else: + statistics[func_name] = [summa / counter, counter] + return result + + return wrapper + + return decorator diff --git a/solutions/sem01/lesson11/task1.py b/solutions/sem01/lesson11/task1.py index 6a15f31fb..88e2346b0 100644 --- a/solutions/sem01/lesson11/task1.py +++ b/solutions/sem01/lesson11/task1.py @@ -1,10 +1,158 @@ +from math import acos, hypot, isclose +from numbers import Real + + class Vector2D: - def conj(self) -> "Vector2D": - # ваш код - return Vector2D() + _abscissa: float + _ordinate: float + + def __init__(self, abscissa: float = 0.0, ordinate: float = 0.0) -> None: + self._abscissa = abscissa + self._ordinate = ordinate + + @property + def abscissa(self) -> float: + return self._abscissa + + @property + def ordinate(self) -> float: + return self._ordinate + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(abscissa={self._abscissa}, ordinate={self._ordinate})" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Vector2D): + return NotImplemented + return isclose(self._ordinate, other._ordinate) and isclose(self._abscissa, other._abscissa) + + def __ne__(self, other: object) -> bool: + if not isinstance(other, Vector2D): + return NotImplemented + return not self == other + + def __lt__(self, other) -> bool: + if not isinstance(other, Vector2D): + return NotImplemented + return ( + (not isclose(self._abscissa, other._abscissa)) + and self._abscissa < other._abscissa + or ( + isclose(self._abscissa, other._abscissa) + and self._ordinate < other._ordinate + and not isclose(self._ordinate, other._ordinate) + ) + ) + + def __le__(self, other) -> bool: + if not isinstance(other, Vector2D): + return NotImplemented + return self._abscissa < other._abscissa or ( + isclose(self._abscissa, other._abscissa) and self._ordinate <= other._ordinate + ) + + def __gt__(self, other) -> bool: + if not isinstance(other, Vector2D): + return NotImplemented + return ( + (not isclose(self._abscissa, other._abscissa)) + and self._abscissa > other._abscissa + or ( + isclose(self._abscissa, other._abscissa) + and self._ordinate > other._ordinate + and not isclose(self._ordinate, other._ordinate) + ) + ) + + def __ge__(self, other) -> bool: + if not isinstance(other, Vector2D): + return NotImplemented + return self._abscissa > other._abscissa or ( + isclose(self._abscissa, other._abscissa) and self._ordinate >= other._ordinate + ) + + def __bool__(self) -> bool: + return not ( + isclose(self._abscissa, 0.0, abs_tol=1e-15) + and isclose(self._ordinate, 0.0, abs_tol=1e-15) + ) + + def __abs__(self) -> float: + return hypot(self._abscissa, self._ordinate) - def get_angle(self, other: "Vector2D") -> float: - # ваш код - return 0 + def __mul__(self, other: Real) -> "Vector2D": + if not isinstance(other, Real): + return NotImplemented + return Vector2D(self._abscissa * other, self._ordinate * other) - # ваш код + def __rmul__(self, other) -> "Vector2D": + if not isinstance(other, Real): + return NotImplemented + return self * other + + def __truediv__(self, other: Real) -> "Vector2D": + if not isinstance(other, Real): + return NotImplemented + return Vector2D(self._abscissa / other, self._ordinate / other) + + def __rtruediv__(self, other: Real): + if not isinstance(other, Real): + return NotImplemented + raise TypeError("Non-existent operation") + + def __add__(self, other) -> "Vector2D": + if isinstance(other, Vector2D): + return Vector2D(self._abscissa + other._abscissa, self._ordinate + other._ordinate) + elif isinstance(other, Real): + return Vector2D(self._abscissa + other, self._ordinate + other) + return NotImplemented + + def __radd__(self, other) -> "Vector2D": + return self + other + + def __sub__(self, other) -> "Vector2D": + if isinstance(other, Real): + return Vector2D(self._abscissa - other, self._ordinate - other) + elif isinstance(other, Vector2D): + return Vector2D(self._abscissa - other._abscissa, self._ordinate - other._ordinate) + return NotImplemented + + def __rsub__(self, other) -> "Vector2D": + if not isinstance(other, Real | Vector2D): + return NotImplemented + raise TypeError("Non-existent operation") + + def __neg__(self) -> "Vector2D": + return Vector2D(-self._abscissa, -self._ordinate) + + def __complex__(self) -> complex: + return complex(self._abscissa, self._ordinate) + + def __int__(self) -> int: + return int(abs(self)) + + def __float__(self) -> float: + return abs(self) + + def __matmul__(self, other) -> float: + if not isinstance(other, Vector2D): + return NotImplemented + return self._abscissa * other._abscissa + self._ordinate * other._ordinate + + def get_angle(self, other) -> float: + if not isinstance(other, Vector2D): + raise TypeError("other is not a Vector2D") + if not bool(self) or not bool(other): + raise ValueError( + f"It is not possible to calculate the angle between {self} and {other}" + ) + + cos = self @ other / (abs(self) * abs(other)) + if cos >= 1: + return acos(1.0) + elif cos <= -1: + return acos(-1) + return acos(cos) + + def conj(self) -> "Vector2D": + return Vector2D(self._abscissa, -self._ordinate) diff --git a/solutions/sem01/lesson12/task1.py b/solutions/sem01/lesson12/task1.py index d1bb828c1..4c483d066 100644 --- a/solutions/sem01/lesson12/task1.py +++ b/solutions/sem01/lesson12/task1.py @@ -2,5 +2,15 @@ def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: - # ваш код - ... + rez = tuple() + i = 0 + for el in iter(iterable): + if i % size == 0 and i != 0: + yield rez + rez = () + rez += (el,) + else: + rez += (el,) + i += 1 + if rez: + yield rez diff --git a/solutions/sem01/lesson12/task2.py b/solutions/sem01/lesson12/task2.py index 3ad802ee7..42187aa91 100644 --- a/solutions/sem01/lesson12/task2.py +++ b/solutions/sem01/lesson12/task2.py @@ -1,6 +1,29 @@ from typing import Any, Generator, Iterable +def infinite_generator(): + i = 1 + while True: + yield i + i += 1 + + +def finite_generator(): + yield 1 + yield 2 + yield 3 + yield 4 + yield 5 + + def circle(iterable: Iterable) -> Generator[Any, None, None]: - # ваш код - ... + if not iterable: + return + el = [] + for x in iterable: + el.append(x) + print(x, el) + yield x + while True: + for item in el: + yield item diff --git a/solutions/sem01/lesson12/task3.py b/solutions/sem01/lesson12/task3.py index 64c112ccc..d03255780 100644 --- a/solutions/sem01/lesson12/task3.py +++ b/solutions/sem01/lesson12/task3.py @@ -1,12 +1,28 @@ import sys +from builtins import bool +from types import TracebackType +from typing import Optional class FileOut: - def __init__( - self, - path_to_file: str, - ) -> None: - # ваш код - ... + __path: str + + def __init__(self, path_to_file: str) -> None: + self.__path = path_to_file - # ваш код + def __enter__(self) -> "FileOut": + self.__file = open(self.__path, "w") + self.__stdout = sys.stdout + sys.stdout = self.__file + return self + + def __exit__( + self, + exc_type: Optional[type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> bool: + sys.stdout = self.__stdout + if exc_type is None: + self.__file.close() + return False diff --git a/solutions/sem02/.DS_Store b/solutions/sem02/.DS_Store new file mode 100644 index 000000000..ca6c051b4 Binary files /dev/null and b/solutions/sem02/.DS_Store differ diff --git a/solutions/sem02/lesson03/task1.py b/solutions/sem02/lesson03/task1.py index 2c3fc0b58..19ce53735 100644 --- a/solutions/sem02/lesson03/task1.py +++ b/solutions/sem02/lesson03/task1.py @@ -8,13 +8,22 @@ class ShapeMismatchError(Exception): def sum_arrays_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs.shape != rhs.shape: + raise ShapeMismatchError("lhs and rhs must have same shape") + return np.add(lhs, rhs) -def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: ... + +def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: + return 3 * (abscissa**2) + 2 * abscissa + 1 def get_mutual_l2_distances_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs[0].shape != rhs[0].shape: + raise ShapeMismatchError("lhs and rhs must have same shape") + + return np.sqrt(np.sum((lhs[:, np.newaxis] - rhs) ** 2, 2)) diff --git a/solutions/sem02/lesson03/task2.py b/solutions/sem02/lesson03/task2.py index fc823c1d6..6242a28d0 100644 --- a/solutions/sem02/lesson03/task2.py +++ b/solutions/sem02/lesson03/task2.py @@ -9,11 +9,27 @@ def convert_from_sphere( distances: np.ndarray, azimuth: np.ndarray, inclination: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + if not (distances.shape == azimuth.shape == inclination.shape): + raise ShapeMismatchError("distances, azimuth, inclination must have same shape") + + x = distances * np.cos(azimuth) * np.sin(inclination) + y = distances * np.sin(azimuth) * np.sin(inclination) + z = distances * np.cos(inclination) + + return x, y, z def convert_to_sphere( abscissa: np.ndarray, ordinates: np.ndarray, applicates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + if not (abscissa.shape == ordinates.shape == applicates.shape): + raise ShapeMismatchError("abscissa, ordinates, applicates must have same shape") + + distances = np.sqrt(abscissa**2 + ordinates**2 + applicates**2) + inclination = np.arctan2(ordinates, abscissa) + azimuth = np.arctan2(np.sqrt(abscissa**2 + ordinates**2), applicates) + + return distances, inclination, azimuth diff --git a/solutions/sem02/lesson03/task3.py b/solutions/sem02/lesson03/task3.py index 477acd0ce..b4c92666d 100644 --- a/solutions/sem02/lesson03/task3.py +++ b/solutions/sem02/lesson03/task3.py @@ -3,4 +3,14 @@ def get_extremum_indices( ordinates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray]: + if ordinates.size < 3: + raise ValueError("ordinates is too small") + + indexes = np.arange(1, np.size(ordinates) - 1) + l_value = ordinates[indexes - 1] + r_value = ordinates[indexes + 1] + + return indexes[(ordinates[indexes] < l_value) & (ordinates[indexes] < r_value)], indexes[ + (ordinates[indexes] > l_value) & (ordinates[indexes] > r_value) + ] diff --git a/solutions/sem02/lesson04/task1.py b/solutions/sem02/lesson04/task1.py index 1b5526c1f..1c6660ed9 100644 --- a/solutions/sem02/lesson04/task1.py +++ b/solutions/sem02/lesson04/task1.py @@ -2,16 +2,64 @@ def pad_image(image: np.ndarray, pad_size: int) -> np.ndarray: - # ваш код - return image + if pad_size < 1: + raise ValueError("pad_size must be a positive integer") + if image.ndim not in (2, 3): + raise ValueError("image must be a 2D or 3D array") + + new_shape = list(image.shape) + new_shape[0] += 2 * pad_size + new_shape[1] += 2 * pad_size + + new_image = np.zeros(new_shape, dtype=image.dtype) + new_image[ + pad_size : pad_size + image.shape[0], + pad_size : pad_size + image.shape[1], + ] = image + return new_image def blur_image( image: np.ndarray, kernel_size: int, ) -> np.ndarray: - # ваш код - return image + if kernel_size < 1 or kernel_size % 2 == 0: + raise ValueError("kernel_size must be a more then 1 and odd integer") + + pad_size = kernel_size // 2 + try: + new_image = pad_image(image, pad_size) + except ValueError: + new_image = image.copy() + + if image.ndim == 2: + padded_sum = new_image.astype(np.uint64) + matrix = np.zeros((padded_sum.shape[0] + 1, padded_sum.shape[1] + 1), dtype=np.uint64) + matrix[1:, 1:] = padded_sum.cumsum(axis=0).cumsum(axis=1) + + summa = ( + matrix[kernel_size:, kernel_size:] + - matrix[:-kernel_size, kernel_size:] + - matrix[kernel_size:, :-kernel_size] + + matrix[:-kernel_size, :-kernel_size] + ) + else: + padded_sum = new_image.astype(np.uint64) + matrix = np.zeros( + (padded_sum.shape[0] + 1, padded_sum.shape[1] + 1, padded_sum.shape[2]), + dtype=np.uint64, + ) + matrix[1:, 1:, :] = padded_sum.cumsum(axis=0).cumsum(axis=1) + + summa = ( + matrix[kernel_size:, kernel_size:, :] + - matrix[:-kernel_size, kernel_size:, :] + - matrix[kernel_size:, :-kernel_size, :] + + matrix[:-kernel_size, :-kernel_size, :] + ) + + result = summa / (kernel_size * kernel_size) + return result.astype(np.uint8) if __name__ == "__main__": diff --git a/solutions/sem02/lesson04/task2.py b/solutions/sem02/lesson04/task2.py index be9a2288f..496f5e486 100644 --- a/solutions/sem02/lesson04/task2.py +++ b/solutions/sem02/lesson04/task2.py @@ -5,6 +5,32 @@ def get_dominant_color_info( image: np.ndarray[np.uint8], threshold: int = 5, ) -> tuple[np.uint8, float]: - # ваш код + if threshold < 1: + raise ValueError("threshold must be positive") - return 0, 0 + flatten_image = image.astype(np.uint8).flatten() + unique_colors, unique_counts = np.unique(flatten_image, return_counts=True) + + color_counts = np.zeros(256, dtype=np.int64) + color_counts[unique_colors] = unique_counts + + prefix_sums = np.zeros(257, dtype=np.int64) + prefix_sums[1:] = np.cumsum(color_counts) + + colors = np.arange(256) + left = np.max( + np.array([colors - threshold + 1, np.zeros_like(colors)]), + axis=0, + ) + right = np.min( + np.array([colors + threshold - 1, np.full_like(colors, 255)]), + axis=0, + ) + pix_cnt = prefix_sums[right + 1] - prefix_sums[left] + + pix_cnt[color_counts == 0] = -1 + + main_color = int(np.argmax(pix_cnt)) + percent = pix_cnt[main_color] / flatten_image.size * 100 + + return np.uint8(main_color), float(percent) diff --git a/solutions/sem02/lesson05/task1.py b/solutions/sem02/lesson05/task1.py index e9c7c3c56..8fb36d3f8 100644 --- a/solutions/sem02/lesson05/task1.py +++ b/solutions/sem02/lesson05/task1.py @@ -9,4 +9,8 @@ def can_satisfy_demand( costs: np.ndarray, resource_amounts: np.ndarray, demand_expected: np.ndarray, -) -> bool: ... +) -> bool: + if costs.shape[0] != resource_amounts.shape[0] or costs.shape[1] != demand_expected.shape[0]: + raise ShapeMismatchError() + + return np.all(np.sum(np.multiply(demand_expected, costs), axis=1) <= resource_amounts) diff --git a/solutions/sem02/lesson05/task2.py b/solutions/sem02/lesson05/task2.py index be1fb9d2b..559030f76 100644 --- a/solutions/sem02/lesson05/task2.py +++ b/solutions/sem02/lesson05/task2.py @@ -8,4 +8,13 @@ class ShapeMismatchError(Exception): def get_projections_components( matrix: np.ndarray, vector: np.ndarray, -) -> tuple[np.ndarray | None, np.ndarray | None]: ... +) -> tuple[np.ndarray | None, np.ndarray | None]: + if (matrix.shape[0] != matrix.shape[1]) or (matrix.shape[1] != vector.shape[0]): + raise ShapeMismatchError + + if np.linalg.matrix_rank(matrix) != matrix.shape[0]: + return None, None + + proekziya = ((matrix @ vector) / (np.linalg.norm(matrix, axis=1) ** 2))[:, np.newaxis] * matrix + + return proekziya, vector - proekziya diff --git a/solutions/sem02/lesson05/task3.py b/solutions/sem02/lesson05/task3.py index 0c66906cb..125b0ac7b 100644 --- a/solutions/sem02/lesson05/task3.py +++ b/solutions/sem02/lesson05/task3.py @@ -9,4 +9,12 @@ def adaptive_filter( Vs: np.ndarray, Vj: np.ndarray, diag_A: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if (Vs.shape[0] != Vj.shape[0]) or (Vj.shape[1] != diag_A.shape[0]): + raise ShapeMismatchError + + A = np.diag(diag_A) + E = np.eye(diag_A.shape[0]) + VjH = np.transpose(Vj.real - 1j * Vj.imag) + + return Vs - Vj @ np.linalg.inv(E + VjH @ Vj @ A) @ (VjH @ Vs) diff --git a/solutions/sem02/lesson07/task1.py b/solutions/sem02/lesson07/task1.py index 3a505d89b..6ed96a37a 100644 --- a/solutions/sem02/lesson07/task1.py +++ b/solutions/sem02/lesson07/task1.py @@ -13,8 +13,95 @@ def visualize_diagrams( ordinates: np.ndarray, diagram_type: Any, ) -> None: - # ваш код - pass + if abscissa.shape != ordinates.shape: + raise ShapeMismatchError("abscissa and ordinates must be of equal size") + + if diagram_type not in ("hist", "violin", "box"): + raise ValueError("diagram_type must be one of: hist, violin, box") + + plt.style.use("ggplot") + + figure = plt.figure(figsize=(10, 10)) + grid = plt.GridSpec(4, 4, wspace=0.15, hspace=0.15) + + axis_scatter = figure.add_subplot(grid[:-1, 1:]) + axis_left = figure.add_subplot(grid[:-1, 0], sharey=axis_scatter) + axis_bottom = figure.add_subplot(grid[-1, 1:], sharex=axis_scatter) + + axis_scatter.scatter(abscissa, ordinates, color="royalblue", alpha=0.55, s=30) + axis_scatter.set_title( + "Диаграмма рассеяния", + fontsize=16, + fontweight="bold", + c="dimgray", + ) + axis_scatter.set_xlabel("abscissa", fontsize=12, fontweight="bold", c="dimgray") + axis_scatter.set_ylabel("ordinates", fontsize=12, fontweight="bold", c="dimgray") + + if diagram_type == "hist": + axis_bottom.hist( + abscissa, + bins=30, + color="cornflowerblue", + edgecolor="blue", + alpha=0.65, + ) + axis_left.hist( + ordinates, + bins=30, + color="sandybrown", + edgecolor="chocolate", + alpha=0.65, + orientation="horizontal", + ) + axis_bottom.invert_yaxis() + axis_left.invert_xaxis() + elif diagram_type == "box": + axis_bottom.boxplot( + abscissa, + vert=False, + patch_artist=True, + boxprops=dict(facecolor="cornflowerblue"), + medianprops=dict(color="navy"), + ) + axis_left.boxplot( + ordinates, + patch_artist=True, + boxprops=dict(facecolor="sandybrown"), + medianprops=dict(color="maroon"), + ) + else: + violin_bottom = axis_bottom.violinplot( + abscissa, + vert=False, + showmedians=True, + ) + for body in violin_bottom["bodies"]: + body.set_facecolor("cornflowerblue") + body.set_edgecolor("blue") + + for part in violin_bottom: + if part == "bodies": + continue + violin_bottom[part].set_edgecolor("cornflowerblue") + + violin_left = axis_left.violinplot( + ordinates, + showmedians=True, + ) + for body in violin_left["bodies"]: + body.set_facecolor("sandybrown") + body.set_edgecolor("chocolate") + + for part in violin_left: + if part == "bodies": + continue + violin_left[part].set_edgecolor("sandybrown") + + axis_left.tick_params(axis="y", labelleft=False) + axis_bottom.tick_params(axis="x", labelbottom=False) + + plt.show() if __name__ == "__main__": diff --git a/solutions/sem02/lesson07/task2.py b/solutions/sem02/lesson07/task2.py index decd607ef..d7f713174 100644 --- a/solutions/sem02/lesson07/task2.py +++ b/solutions/sem02/lesson07/task2.py @@ -1 +1,70 @@ -# ваш код (используйте функции или классы для решения данной задачи) +import json + +import matplotlib.pyplot as plt +import numpy as np + + +def solve_cardio_task(file_path: str) -> tuple[np.ndarray, np.ndarray]: + with open(file_path, "r", encoding="utf-8") as file: + data = json.load(file) + + stages = ["I", "II", "III", "IV"] + + def get_stage_counts(values: list[str]) -> list[int]: + stage_array = np.array(values) + unique_stages, counts = np.unique(stage_array, return_counts=True) + counts_by_stage = dict(zip(unique_stages, counts)) + return [counts_by_stage.get(stage, 0) for stage in stages] + + before = get_stage_counts(data["before"]) + after = get_stage_counts(data["after"]) + + positions = np.arange(len(stages)) + width = 0.35 + + plt.style.use("ggplot") + figure, axis = plt.subplots(figsize=(10, 6)) + + before_bars = axis.bar( + positions - width / 2, + before, + width, + label="Before", + color="lightsteelblue", + edgecolor="royalblue", + ) + after_bars = axis.bar( + positions + width / 2, + after, + width, + label="After", + color="salmon", + edgecolor="crimson", + ) + + axis.set_ylabel("Patients count", fontsize=12) + axis.set_title( + "Mitral insufficiency stages", + fontsize=14, + fontweight="bold", + ) + axis.set_xticks(positions, labels=stages) + axis.legend() + axis.grid() + axis.bar_label(before_bars) + axis.bar_label(after_bars) + + plt.savefig("heart_implant_efficiency.png", dpi=300) + plt.show() + + return np.array(before), np.array(after) + + +if __name__ == "__main__": + before_counts, after_counts = solve_cardio_task("data/medic_data.json") + + if after_counts[0] + after_counts[1] > before_counts[0] + before_counts[1]: + print( + "Имплант можно считать эффективным: после установки стало больше пациентов " + "с лёгкими стадиями I-II и меньше пациентов с тяжёлыми стадиями III-IV." + ) diff --git a/solutions/sem02/lesson08/loaded_labyrinth.gif b/solutions/sem02/lesson08/loaded_labyrinth.gif new file mode 100644 index 000000000..cf45da0ec Binary files /dev/null and b/solutions/sem02/lesson08/loaded_labyrinth.gif differ diff --git a/solutions/sem02/lesson08/modulated_signal.gif b/solutions/sem02/lesson08/modulated_signal.gif new file mode 100644 index 000000000..7982b788d Binary files /dev/null and b/solutions/sem02/lesson08/modulated_signal.gif differ diff --git a/solutions/sem02/lesson08/task1.py b/solutions/sem02/lesson08/task1.py index 89f88572f..c366c0542 100644 --- a/solutions/sem02/lesson08/task1.py +++ b/solutions/sem02/lesson08/task1.py @@ -1,34 +1,137 @@ from functools import partial +from typing import Callable import matplotlib.pyplot as plt import numpy as np - from IPython.display import HTML from matplotlib.animation import FuncAnimation +def get_signal( + time: np.ndarray, + modulation: Callable[[np.ndarray], np.ndarray] | None, + fc: float, +) -> np.ndarray: + carrier = np.sin(2 * np.pi * fc * time) + if modulation is None: + return carrier + return modulation(time) * carrier + + +def get_signal_limits( + modulation: Callable[[np.ndarray], np.ndarray] | None, + fc: float, + num_frames: int, + plot_duration: float, + time_step: float, + animation_step: float, +) -> tuple[float, float]: + max_time = plot_duration + animation_step * max(num_frames - 1, 0) + time = np.arange(0, max_time + time_step, time_step) + signal = get_signal(time, modulation, fc) + max_abs_value = np.max(np.abs(signal)) + + if max_abs_value == 0: + max_abs_value = 1.0 + + limit = max_abs_value * 2.01 + return -limit, limit + + def create_modulation_animation( - modulation, - fc, - num_frames, - plot_duration, - time_step=0.001, + modulation, + fc, + num_frames, + plot_duration, + time_step=0.001, animation_step=0.01, - save_path="" + save_path="", ) -> FuncAnimation: - # ваш код - return FuncAnimation() + start_time = 0 + time = np.arange(start_time, start_time + plot_duration, time_step) + signal = get_signal(time, modulation, fc) + + plt.style.use("ggplot") + figure, axis = plt.subplots(figsize=(12, 6)) + (line,) = axis.plot(time, signal, c="royalblue", label="signal") + + axis.set_title( + "Анимация модулированного сигнала", + fontsize=16, + fontweight="bold", + c="dimgray", + ) + axis.set_xlabel("Время (с)", fontsize=12, fontweight="bold", c="dimgray") + axis.set_ylabel("Амплитуда", fontsize=12, fontweight="bold", c="dimgray") + axis.set_xlim(time.min(), time.max()) + axis.set_ylim( + *get_signal_limits( + modulation, + fc, + num_frames, + plot_duration, + time_step, + animation_step, + ) + ) + axis.legend() + + def update_frame( + frame_id: int, + *, + line: plt.Line2D, + axis: plt.Axes, + modulation: Callable[[np.ndarray], np.ndarray] | None, + fc: float, + plot_duration: float, + time_step: float, + animation_step: float, + ) -> tuple[plt.Line2D]: + frame_start = frame_id * animation_step + frame_time = np.arange( + frame_start, + frame_start + plot_duration, + time_step, + ) + frame_signal = get_signal(frame_time, modulation, fc) + + line.set_data(frame_time, frame_signal) + axis.set_xlim(frame_time.min(), frame_time.max()) + return (line,) + + animation = FuncAnimation( + figure, + partial( + update_frame, + line=line, + axis=axis, + modulation=modulation, + fc=fc, + plot_duration=plot_duration, + time_step=time_step, + animation_step=animation_step, + ), + frames=num_frames, + interval=50, + blit=True, + ) + + if save_path != "": + animation.save(save_path, writer="pillow", fps=24) + + return animation if __name__ == "__main__": + def modulation_function(t): - return np.cos(t * 6) + return np.cos(t * 6) - num_frames = 100 - plot_duration = np.pi / 2 - time_step = 0.001 - animation_step = np.pi / 200 - fc = 50 + num_frames = 100 + plot_duration = np.pi / 2 + time_step = 0.001 + animation_step = np.pi / 200 + fc = 50 save_path_with_modulation = "modulated_signal.gif" animation = create_modulation_animation( @@ -38,6 +141,6 @@ def modulation_function(t): plot_duration=plot_duration, time_step=time_step, animation_step=animation_step, - save_path=save_path_with_modulation + save_path=save_path_with_modulation, ) - HTML(animation.to_jshtml()) \ No newline at end of file + HTML(animation.to_jshtml()) diff --git a/solutions/sem02/lesson08/task2.py b/solutions/sem02/lesson08/task2.py index b677c0702..4e47dd477 100644 --- a/solutions/sem02/lesson08/task2.py +++ b/solutions/sem02/lesson08/task2.py @@ -2,52 +2,297 @@ import matplotlib.pyplot as plt import numpy as np - from IPython.display import HTML from matplotlib.animation import FuncAnimation +WALL_VALUE = -2 +UNVISITED_VALUE = -1 +PATH_VALUE = 1 + + +def get_neighbours( + cell: tuple[int, int], + shape: tuple[int, int], +) -> list[tuple[int, int]]: + row, col = cell + rows, cols = shape + neighbours: list[tuple[int, int]] = [] + + if row > 0: + neighbours.append((row - 1, col)) + if row + 1 < rows: + neighbours.append((row + 1, col)) + if col > 0: + neighbours.append((row, col - 1)) + if col + 1 < cols: + neighbours.append((row, col + 1)) + + return neighbours + + +def validate_points( + maze: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], +) -> None: + rows, cols = maze.shape + start_row, start_col = start + end_row, end_col = end + + if not (0 <= start_row < rows and 0 <= start_col < cols): + raise ValueError("start point is out of maze bounds") + if not (0 <= end_row < rows and 0 <= end_col < cols): + raise ValueError("end point is out of maze bounds") + if maze[start] == 0: + raise ValueError("start point must be on a free cell") + if maze[end] == 0: + raise ValueError("end point must be on a free cell") + + +def run_wave_algorithm( + maze: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], +) -> tuple[list[np.ndarray], np.ndarray]: + distances = np.full(maze.shape, UNVISITED_VALUE, dtype=int) + distances[maze == 0] = WALL_VALUE + wave_frames: list[np.ndarray] = [] + + frontier = [start] + distances[start] = 0 + wave_frames.append(distances.astype(float).copy()) + + while frontier and distances[end] == UNVISITED_VALUE: + next_frontier: list[tuple[int, int]] = [] + + for cell in frontier: + for neighbour in get_neighbours(cell, maze.shape): + row, col = neighbour + if distances[row, col] != UNVISITED_VALUE: + continue + + distances[row, col] = distances[cell] + 1 + next_frontier.append(neighbour) + + if not next_frontier: + break + + wave_frames.append(distances.astype(float).copy()) + frontier = next_frontier + + return wave_frames, distances + + +def build_reverse_path( + distances: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], +) -> list[tuple[int, int]]: + if distances[end] < 0: + return [] + + path = [end] + current = end + + while current != start: + for neighbour in get_neighbours(current, distances.shape): + if distances[neighbour] == distances[current] - 1: + current = neighbour + path.append(current) + break + + return path + +def create_path_frames( + path: list[tuple[int, int]], + shape: tuple[int, int], +) -> list[np.ndarray]: + if not path: + return [] + + path_frames: list[np.ndarray] = [] + + for path_end in range(1, len(path) + 1): + frame = np.zeros(shape, dtype=float) + for row, col in path[:path_end]: + frame[row, col] = PATH_VALUE + path_frames.append(frame) + + return path_frames + + +def create_point_frame( + point: tuple[int, int], + shape: tuple[int, int], +) -> np.ndarray: + frame = np.zeros(shape, dtype=float) + frame[point] = 1 + return frame + + +def create_alpha_mask( + frame: np.ndarray, + threshold: float, + alpha_value: float = 1.0, +) -> np.ndarray: + alpha = np.zeros(frame.shape, dtype=float) + alpha[frame >= threshold] = alpha_value + return alpha + + +def update_frame( + frame_id: int, + *, + wave_image, + path_image, + wave_frames: list[np.ndarray], + path_frames: list[np.ndarray], +) -> tuple: + if frame_id < len(wave_frames): + current_wave = wave_frames[frame_id] + current_path = np.zeros_like(current_wave, dtype=float) + else: + current_wave = wave_frames[-1] + current_path = path_frames[frame_id - len(wave_frames)] + + wave_image.set_data(np.where(current_wave >= 0, current_wave, 0)) + wave_image.set_alpha(create_alpha_mask(current_wave, 0, 0.95)) + path_image.set_data(current_path) + path_image.set_alpha(create_alpha_mask(current_path, 1)) + return wave_image, path_image def animate_wave_algorithm( - maze: np.ndarray, - start: tuple[int, int], - end: tuple[int, int], - save_path: str = "" -) -> FuncAnimation: - # ваш код - return FuncAnimation() + maze: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], + save_path: str = "", +) -> FuncAnimation: + validate_points(maze, start, end) + + wave_frames, distances = run_wave_algorithm(maze, start, end) + path = build_reverse_path(distances, start, end) + path_frames = create_path_frames(path, maze.shape) + wave_max_value = max(float(distances.max()), 1.0) + + figure, axis = plt.subplots(figsize=(9, 9)) + + free_frame = maze * 0.75 + wall_frame = (maze == 0).astype(float) + start_frame = create_point_frame(start, maze.shape) + end_frame = create_point_frame(end, maze.shape) + + axis.imshow( + free_frame, + origin="lower", + cmap="Blues", + vmin=0, + vmax=1, + alpha=create_alpha_mask(free_frame, 0.1), + ) + axis.imshow( + wall_frame, + origin="lower", + cmap="Blues", + vmin=0, + vmax=1, + alpha=create_alpha_mask(wall_frame, 1), + ) + wave_image = axis.imshow( + np.where(wave_frames[0] >= 0, wave_frames[0], 0), + origin="lower", + cmap="YlGn", + vmin=0, + vmax=wave_max_value, + alpha=create_alpha_mask(wave_frames[0], 0, 0.95), + ) + path_image = axis.imshow( + np.zeros_like(maze, dtype=float), + origin="lower", + cmap="Reds", + vmin=0, + vmax=1, + alpha=np.zeros(maze.shape, dtype=float), + ) + axis.imshow( + start_frame, + origin="lower", + cmap="YlGn", + vmin=0, + vmax=1, + alpha=create_alpha_mask(start_frame, 1), + ) + axis.imshow( + end_frame, + origin="lower", + cmap="Reds", + vmin=0, + vmax=1, + alpha=create_alpha_mask(end_frame, 1), + ) + + axis.axis("image") + axis.grid(False) + figure.colorbar(wave_image, ax=axis) + + animation = FuncAnimation( + figure, + partial( + update_frame, + wave_image=wave_image, + path_image=path_image, + wave_frames=wave_frames, + path_frames=path_frames, + ), + frames=len(wave_frames) + len(path_frames), + interval=300, + blit=True, + ) + + if save_path != "": + animation.save(save_path, writer="pillow", fps=24) + + if not path: + print("Путь от старта до финиша не найден") + + return animation + if __name__ == "__main__": # Пример 1 - maze = np.array([ - [0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 1, 0, 1, 0], - [0, 0, 1, 1, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0], - [1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0], - ]) + maze = np.array( + [ + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 1, 0, 1, 0], + [0, 0, 1, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0], + [1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + ] + ) start = (2, 0) end = (5, 0) - save_path = "labyrinth.gif" # Укажите путь для сохранения анимации + save_path = "labyrinth.gif" animation = animate_wave_algorithm(maze, start, end, save_path) HTML(animation.to_jshtml()) - + # Пример 2 - maze_path = "./data/maze.npy" loaded_maze = np.load(maze_path) # можете поменять, если захотите запустить из других точек - start = (2, 0) - end = (5, 0) + start = (0, 18) + end = (101, 43) loaded_save_path = "loaded_labyrinth.gif" - loaded_animation = animate_wave_algorithm(loaded_maze, start, end, loaded_save_path) + loaded_animation = animate_wave_algorithm( + loaded_maze, + start, + end, + loaded_save_path, + ) HTML(loaded_animation.to_jshtml()) - - \ No newline at end of file diff --git a/solutions/sem02/lesson10/task1.ipynb b/solutions/sem02/lesson10/task1.ipynb index 4b4e9e335..c774a58a5 100644 --- a/solutions/sem02/lesson10/task1.ipynb +++ b/solutions/sem02/lesson10/task1.ipynb @@ -34,14 +34,172 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "d7b00711", - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2026-05-01T20:11:29.037623Z", + "start_time": "2026-05-01T20:11:29.026390Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
| \n", + " | survived | \n", + "pclass | \n", + "sex | \n", + "age | \n", + "sibsp | \n", + "parch | \n", + "fare | \n", + "embarked | \n", + "class | \n", + "who | \n", + "adult_male | \n", + "deck | \n", + "embark_town | \n", + "alive | \n", + "alone | \n", + "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 216 | \n", + "1 | \n", + "3 | \n", + "female | \n", + "27.0 | \n", + "0 | \n", + "0 | \n", + "7.9250 | \n", + "S | \n", + "Third | \n", + "woman | \n", + "False | \n", + "NaN | \n", + "Southampton | \n", + "yes | \n", + "True | \n", + "
| 43 | \n", + "1 | \n", + "2 | \n", + "female | \n", + "3.0 | \n", + "1 | \n", + "2 | \n", + "41.5792 | \n", + "C | \n", + "Second | \n", + "child | \n", + "False | \n", + "NaN | \n", + "Cherbourg | \n", + "yes | \n", + "False | \n", + "
| 735 | \n", + "0 | \n", + "3 | \n", + "male | \n", + "28.5 | \n", + "0 | \n", + "0 | \n", + "16.1000 | \n", + "S | \n", + "Third | \n", + "man | \n", + "True | \n", + "NaN | \n", + "Southampton | \n", + "no | \n", + "True | \n", + "
| 196 | \n", + "0 | \n", + "3 | \n", + "male | \n", + "NaN | \n", + "0 | \n", + "0 | \n", + "7.7500 | \n", + "Q | \n", + "Third | \n", + "man | \n", + "True | \n", + "NaN | \n", + "Queenstown | \n", + "no | \n", + "True | \n", + "
| 608 | \n", + "1 | \n", + "2 | \n", + "female | \n", + "22.0 | \n", + "1 | \n", + "2 | \n", + "41.5792 | \n", + "C | \n", + "Second | \n", + "woman | \n", + "False | \n", + "NaN | \n", + "Cherbourg | \n", + "yes | \n", + "False | \n", + "