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..f2f6a282b 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,122 @@ 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/solutions/sem01/lesson03/task1.py b/solutions/sem01/lesson03/task1.py index f1d8fe26b..7b048e654 100644 --- a/solutions/sem01/lesson03/task1.py +++ b/solutions/sem01/lesson03/task1.py @@ -1,3 +1,3 @@ def flip_bits_in_range(num: int, left_bit: int, right_bit: int) -> int: # ваш код - return num \ No newline at end of file + return num diff --git a/solutions/sem01/lesson03/task2.py b/solutions/sem01/lesson03/task2.py index a3a738c2a..5cf2b6316 100644 --- a/solutions/sem01/lesson03/task2.py +++ b/solutions/sem01/lesson03/task2.py @@ -1,3 +1,3 @@ def get_cube_root(n: float, eps: float) -> float: # ваш код - return n \ No newline at end of file + return n diff --git a/solutions/sem01/lesson04/task1.py b/solutions/sem01/lesson04/task1.py index 47384423a..0135e399b 100644 --- a/solutions/sem01/lesson04/task1.py +++ b/solutions/sem01/lesson04/task1.py @@ -1,3 +1,3 @@ def is_arithmetic_progression(lst: list[list[int]]) -> bool: # ваш код - return False \ No newline at end of file + return False diff --git a/solutions/sem01/lesson04/task2.py b/solutions/sem01/lesson04/task2.py index 4591d0a3e..5d6f8ee8a 100644 --- a/solutions/sem01/lesson04/task2.py +++ b/solutions/sem01/lesson04/task2.py @@ -1,3 +1,3 @@ def merge_intervals(intervals: list[list[int, int]]) -> list[list[int, int]]: # ваш код - return [[0,0]] \ No newline at end of file + return [[0, 0]] diff --git a/solutions/sem01/lesson04/task4.py b/solutions/sem01/lesson04/task4.py index b21bc5a39..2664384d8 100644 --- a/solutions/sem01/lesson04/task4.py +++ b/solutions/sem01/lesson04/task4.py @@ -1,3 +1,3 @@ def move_zeros_to_end(nums: list[int]) -> list[int]: # ваш код - return 0 \ No newline at end of file + return 0 diff --git a/solutions/sem01/lesson04/task5.py b/solutions/sem01/lesson04/task5.py index 02d7742bb..ec6932ee4 100644 --- a/solutions/sem01/lesson04/task5.py +++ b/solutions/sem01/lesson04/task5.py @@ -1,3 +1,3 @@ def find_row_with_most_ones(matrix: list[list[int]]) -> int: # ваш код - return 0 \ No newline at end of file + return 0 diff --git a/solutions/sem01/lesson04/task6.py b/solutions/sem01/lesson04/task6.py index 16df27ca6..d16e77dda 100644 --- a/solutions/sem01/lesson04/task6.py +++ b/solutions/sem01/lesson04/task6.py @@ -1,3 +1,3 @@ -def count_cycles(arr: list[int]) -> int: +def count_cycles(arr: list[int]) -> int: # ваш код - return 0 \ No newline at end of file + return 0 diff --git a/solutions/sem01/lesson05/task1.py b/solutions/sem01/lesson05/task1.py index 9a17211e5..fdf3b5488 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 + return False diff --git a/solutions/sem01/lesson05/task2.py b/solutions/sem01/lesson05/task2.py index 367503802..c70b40298 100644 --- a/solutions/sem01/lesson05/task2.py +++ b/solutions/sem01/lesson05/task2.py @@ -1,3 +1,3 @@ def are_anagrams(word1: str, word2: str) -> bool: # ваш код - return False \ No newline at end of file + return False diff --git a/solutions/sem01/lesson05/task4.py b/solutions/sem01/lesson05/task4.py index 4c4e9086e..7c2c26f17 100644 --- a/solutions/sem01/lesson05/task4.py +++ b/solutions/sem01/lesson05/task4.py @@ -1,3 +1,3 @@ def unzip(compress_text: str) -> str: # ваш код - return compress_text \ No newline at end of file + return compress_text diff --git a/solutions/sem01/lesson05/task5.py b/solutions/sem01/lesson05/task5.py index 076c5bb6c..da9e6d08c 100644 --- a/solutions/sem01/lesson05/task5.py +++ b/solutions/sem01/lesson05/task5.py @@ -1,3 +1,3 @@ -def reg_validator(reg_expr: str, text: str) -> bool: +def reg_validator(reg_expr: str, text: str) -> bool: # ваш код - return False \ No newline at end of file + return False diff --git a/solutions/sem01/lesson05/task6.py b/solutions/sem01/lesson05/task6.py index 1b914ada7..63207797d 100644 --- a/solutions/sem01/lesson05/task6.py +++ b/solutions/sem01/lesson05/task6.py @@ -1,3 +1,3 @@ def simplify_path(path: str) -> str: # ваш код - return path \ No newline at end of file + return path diff --git a/solutions/sem01/lesson06/task1.py b/solutions/sem01/lesson06/task1.py index 2d1e30e96..353cb3616 100644 --- a/solutions/sem01/lesson06/task1.py +++ b/solutions/sem01/lesson06/task1.py @@ -1,3 +1,3 @@ def int_to_roman(num: int) -> str: # ваш код - return "" \ No newline at end of file + return "" diff --git a/solutions/sem01/lesson06/task2.py b/solutions/sem01/lesson06/task2.py index f535b5a0c..f1034e24e 100644 --- a/solutions/sem01/lesson06/task2.py +++ b/solutions/sem01/lesson06/task2.py @@ -1,3 +1,3 @@ def get_len_of_longest_substring(text: str) -> int: # ваш код - return 0 \ No newline at end of file + return 0 diff --git a/solutions/sem01/lesson06/task3.py b/solutions/sem01/lesson06/task3.py index 7449a1e72..b160d615a 100644 --- a/solutions/sem01/lesson06/task3.py +++ b/solutions/sem01/lesson06/task3.py @@ -2,6 +2,5 @@ def is_there_any_good_subarray( nums: list[int], k: int, ) -> bool: - # ваш код return False diff --git a/solutions/sem01/lesson06/task4.py b/solutions/sem01/lesson06/task4.py index 5b75a110c..95a7098e4 100644 --- a/solutions/sem01/lesson06/task4.py +++ b/solutions/sem01/lesson06/task4.py @@ -1,3 +1,3 @@ def count_unique_words(text: str) -> int: # ваш код - return 0 \ No newline at end of file + return 0 diff --git a/solutions/sem01/lesson08/task1.py b/solutions/sem01/lesson08/task1.py index 4390f6c84..7fa724ef8 100644 --- a/solutions/sem01/lesson08/task1.py +++ b/solutions/sem01/lesson08/task1.py @@ -1,5 +1,6 @@ from typing import Callable + def make_averager(accumulation_period: int) -> Callable[[float], float]: # ваш код - pass \ No newline at end of file + pass diff --git a/solutions/sem01/lesson08/task2.py b/solutions/sem01/lesson08/task2.py index 6e4af8707..cc2ae4303 100644 --- a/solutions/sem01/lesson08/task2.py +++ b/solutions/sem01/lesson08/task2.py @@ -2,9 +2,7 @@ T = TypeVar("T") -def collect_statistic( - statistics: dict[str, list[float, int]] -) -> Callable[[T], T]: - + +def collect_statistic(statistics: dict[str, list[float, int]]) -> Callable[[T], T]: # ваш код - pass \ No newline at end of file + pass diff --git a/solutions/sem01/lesson12/task3.py b/solutions/sem01/lesson12/task3.py index 64c112ccc..58b0986e1 100644 --- a/solutions/sem01/lesson12/task3.py +++ b/solutions/sem01/lesson12/task3.py @@ -1,6 +1,3 @@ -import sys - - class FileOut: def __init__( self, diff --git a/solutions/sem02/lesson03/task1.py b/solutions/sem02/lesson03/task1.py index 2c3fc0b58..571192389 100644 --- a/solutions/sem02/lesson03/task1.py +++ b/solutions/sem02/lesson03/task1.py @@ -8,13 +8,23 @@ class ShapeMismatchError(Exception): def sum_arrays_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs.size != rhs.size: + raise ShapeMismatchError + return lhs + rhs -def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: ... +def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: + return abscissa**2 * 3 + abscissa * 2 + 1 def get_mutual_l2_distances_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs.shape[1] != rhs.shape[1]: + raise ShapeMismatchError + lsq = (lhs**2) @ np.ones(shape=(lhs.shape[1], 1)) + rsq = np.ones(shape=(1, rhs.shape[1])) @ (rhs**2).T + double = lhs @ rhs.T + return np.sqrt(lsq - 2 * double + rsq) diff --git a/solutions/sem02/lesson03/task2.py b/solutions/sem02/lesson03/task2.py index fc823c1d6..c9f173421 100644 --- a/solutions/sem02/lesson03/task2.py +++ b/solutions/sem02/lesson03/task2.py @@ -9,11 +9,23 @@ 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 + x = distances * np.sin(inclination) * np.cos(azimuth) + y = distances * np.sin(inclination) * np.sin(azimuth) + 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 + r = np.sqrt(abscissa**2 + ordinates**2 + applicates**2) + azimuth = np.arctan2(ordinates, abscissa) + inclination = np.arctan2(np.sqrt(abscissa**2 + ordinates**2), applicates) + return (r, azimuth, inclination) diff --git a/solutions/sem02/lesson03/task3.py b/solutions/sem02/lesson03/task3.py index 477acd0ce..4dd444aa8 100644 --- a/solutions/sem02/lesson03/task3.py +++ b/solutions/sem02/lesson03/task3.py @@ -3,4 +3,13 @@ def get_extremum_indices( ordinates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray]: + if ordinates.size < 3: + raise ValueError + lhs = ordinates[:-2] + middle = ordinates[1:-1] + rhs = ordinates[2:] + ind = np.arange(1, ordinates.size - 1) + mini = ind[(middle < lhs) & (middle < rhs)] + maxi = ind[(middle > lhs) & (middle > rhs)] + return (mini, maxi) diff --git a/solutions/sem02/lesson04/requirements.txt b/solutions/sem02/lesson04/requirements.txt index 039f1d503..f15ac080a 100644 --- a/solutions/sem02/lesson04/requirements.txt +++ b/solutions/sem02/lesson04/requirements.txt @@ -1,3 +1,2 @@ matplotlib==3.8.0 -numpy==1.26.1 opencv-python-headless==4.9.0.80 diff --git a/solutions/sem02/lesson04/task1.py b/solutions/sem02/lesson04/task1.py index 1b5526c1f..99a30a16d 100644 --- a/solutions/sem02/lesson04/task1.py +++ b/solutions/sem02/lesson04/task1.py @@ -2,16 +2,41 @@ def pad_image(image: np.ndarray, pad_size: int) -> np.ndarray: - # ваш код - return image + if pad_size < 1: + raise ValueError + new_shape = (image.shape[0] + 2 * pad_size, image.shape[1] + 2 * pad_size) + image.shape[2:] + result = np.zeros(new_shape, dtype=np.uint8) + result[pad_size:-pad_size, pad_size:-pad_size] = image + return result -def blur_image( - image: np.ndarray, - kernel_size: int, -) -> np.ndarray: - # ваш код - return image +def blur_image(image: np.ndarray, kernel_size: int) -> np.ndarray: + if kernel_size % 2 == 0 or kernel_size < 1: + raise ValueError + + pad_size = kernel_size // 2 + + if pad_size == 0: + return image.copy() + + padded = pad_image(image, pad_size).astype(np.float64) + cumsum = np.cumsum(np.cumsum(padded, axis=0), axis=1) + + c = np.zeros((padded.shape[0] + 1, padded.shape[1] + 1) + image.shape[2:], dtype=np.float64) + c[1:, 1:] = cumsum + cumsum = c + + H, W = image.shape[:2] + k = kernel_size + + result = ( + cumsum[k : k + H, k : k + W] + - cumsum[0:H, k : k + W] + - cumsum[k : k + H, 0:W] + + cumsum[0:H, 0:W] + ) + + return (result / k**2).astype(np.uint8) if __name__ == "__main__": diff --git a/solutions/sem02/lesson04/task2.py b/solutions/sem02/lesson04/task2.py index be9a2288f..15c692c37 100644 --- a/solutions/sem02/lesson04/task2.py +++ b/solutions/sem02/lesson04/task2.py @@ -5,6 +5,24 @@ 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 + values, freq = np.unique(image.reshape(-1), return_counts=True) + + hist = np.zeros(256, dtype=np.int32) + hist[values] = freq + + cumsum = np.zeros(257, dtype=np.int32) + cumsum[1:] = np.cumsum(hist) + + v = np.arange(256) + left = np.maximum(0, v - threshold + 1) + right = np.minimum(256, v + threshold) + + counts = cumsum[right] - cumsum[left] + counts[hist == 0] = -1 + + best = np.argsort(counts)[-1] + + return np.uint8(best), counts[best] / image.size diff --git a/solutions/sem02/lesson05/task1.py b/solutions/sem02/lesson05/task1.py index e9c7c3c56..dc667767d 100644 --- a/solutions/sem02/lesson05/task1.py +++ b/solutions/sem02/lesson05/task1.py @@ -9,4 +9,9 @@ def can_satisfy_demand( costs: np.ndarray, resource_amounts: np.ndarray, demand_expected: np.ndarray, -) -> bool: ... +) -> bool: + m, n = costs.shape + if resource_amounts.size != m or demand_expected.size != n: + raise ShapeMismatchError + new_costs = costs @ demand_expected.T + return ((resource_amounts - new_costs) >= 0).all() diff --git a/solutions/sem02/lesson05/task2.py b/solutions/sem02/lesson05/task2.py index be1fb9d2b..4f796daeb 100644 --- a/solutions/sem02/lesson05/task2.py +++ b/solutions/sem02/lesson05/task2.py @@ -8,4 +8,11 @@ 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]: + m, n = matrix.shape + if m != n or n != vector.shape[0]: + raise ShapeMismatchError + if np.linalg.matrix_rank(matrix) != m: + return None, None + proj = ((matrix @ vector) / (np.linalg.norm(matrix, axis=-1) ** 2))[:, np.newaxis] * matrix + return proj, vector - proj diff --git a/solutions/sem02/lesson05/task3.py b/solutions/sem02/lesson05/task3.py index 0c66906cb..1e9d4e3e4 100644 --- a/solutions/sem02/lesson05/task3.py +++ b/solutions/sem02/lesson05/task3.py @@ -9,4 +9,11 @@ def adaptive_filter( Vs: np.ndarray, Vj: np.ndarray, diag_A: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + m, n = Vs.shape + k = diag_A.size + if Vj.shape != (m, k): + raise ShapeMismatchError + Vh = Vj.conj().T + matrix = np.eye(k) + (Vh @ Vj) * diag_A + return Vs - Vj @ np.linalg.solve(matrix, Vh @ Vs) diff --git a/solutions/sem02/lesson07/heart_implant_efficiency.png b/solutions/sem02/lesson07/heart_implant_efficiency.png new file mode 100644 index 000000000..b3eca5c8b Binary files /dev/null and b/solutions/sem02/lesson07/heart_implant_efficiency.png differ diff --git a/solutions/sem02/lesson07/task1.py b/solutions/sem02/lesson07/task1.py index 3a505d89b..dd16218a4 100644 --- a/solutions/sem02/lesson07/task1.py +++ b/solutions/sem02/lesson07/task1.py @@ -5,6 +5,8 @@ class ShapeMismatchError(Exception): + """Исключение, выбрасываемое при несовпадении размеров массивов.""" + pass @@ -13,16 +15,87 @@ def visualize_diagrams( ordinates: np.ndarray, diagram_type: Any, ) -> None: - # ваш код - pass + if abscissa.shape != ordinates.shape: + raise ShapeMismatchError + + allowed_types = {"hist", "violin", "box"} + if diagram_type not in allowed_types: + raise ValueError + + main_color = "mediumseagreen" + edge_color = "seagreen" + + figure = plt.figure(figsize=(9, 9)) + grid = plt.GridSpec(4, 4, wspace=0.3, hspace=0.3) + + axis_scatter = figure.add_subplot(grid[:-1, 1:]) + axis_dist_x = figure.add_subplot(grid[-1, 1:], sharex=axis_scatter) + axis_dist_y = figure.add_subplot(grid[:-1, 0], sharey=axis_scatter) + + axis_dist_x.tick_params(axis="x", labelbottom=False) + axis_dist_y.tick_params(axis="y", labelleft=False) + + axis_scatter.scatter(abscissa, ordinates, color=main_color, alpha=0.5, edgecolor=edge_color) + + if diagram_type == "hist": + axis_dist_x.hist( + abscissa, bins=40, color=main_color, edgecolor=edge_color, density=True, alpha=0.7 + ) + axis_dist_y.hist( + ordinates, + bins=40, + color=main_color, + edgecolor=edge_color, + density=True, + alpha=0.7, + orientation="horizontal", + ) + axis_dist_x.invert_yaxis() + axis_dist_y.invert_xaxis() + + elif diagram_type == "box": + axis_dist_x.boxplot( + abscissa, + vert=False, + patch_artist=True, + boxprops=dict(facecolor=main_color, color=edge_color), + medianprops=dict(color="k"), + ) + axis_dist_y.boxplot( + ordinates, + vert=True, + patch_artist=True, + boxprops=dict(facecolor=main_color, color=edge_color), + medianprops=dict(color="k"), + ) + + axis_dist_x.axis("off") + axis_dist_y.axis("off") + + elif diagram_type == "violin": + v_x = axis_dist_x.violinplot(abscissa, vert=False, showmedians=True) + v_y = axis_dist_y.violinplot(ordinates, vert=True, showmedians=True) + + for parts in [v_x, v_y]: + for body in parts["bodies"]: + body.set_facecolor(main_color) + body.set_edgecolor(edge_color) + body.set_alpha(0.7) + parts["cmedians"].set_color("k") + parts["cmins"].set_color(edge_color) + parts["cmaxes"].set_color(edge_color) + parts["cbars"].set_color(edge_color) + + axis_dist_x.axis("off") + axis_dist_y.axis("off") if __name__ == "__main__": mean = [2, 3] cov = [[1, 1], [1, 2]] - space = 0.2 abscissa, ordinates = np.random.multivariate_normal(mean, cov, size=1000).T - visualize_diagrams(abscissa, ordinates, "hist") + visualize_diagrams(abscissa, ordinates, "violin") + plt.show() diff --git a/solutions/sem02/lesson07/task2.py b/solutions/sem02/lesson07/task2.py index decd607ef..2e904c819 100644 --- a/solutions/sem02/lesson07/task2.py +++ b/solutions/sem02/lesson07/task2.py @@ -1 +1,67 @@ -# ваш код (используйте функции или классы для решения данной задачи) +import json + +import matplotlib.pyplot as plt +import numpy as np + + +def solve_cardio_task(file_path: str): + with open(file_path, "r", encoding="utf-8") as f: + data = json.load(f) + + stages = ["I", "II", "III", "IV"] + + def get_ordered_counts(data_list): + arr = np.array(data_list) + unique_vals, counts = np.unique(arr, return_counts=True) + count_dict = dict(zip(unique_vals, counts)) + return [count_dict.get(s, 0) for s in stages] + + before_values = get_ordered_counts(data["before"]) + after_values = get_ordered_counts(data["after"]) + + x = np.arange(len(stages)) + width = 0.35 + + fig, ax = plt.subplots(figsize=(10, 6)) + + rects1 = ax.bar( + x - width / 2, + before_values, + width, + label="До установки", + color="lightsteelblue", + edgecolor="royalblue", + ) + rects2 = ax.bar( + x + width / 2, + after_values, + width, + label="После установки", + color="salmon", + edgecolor="crimson", + ) + + ax.set_ylabel("Количество пациентов", fontsize=12) + ax.set_title( + "Распределение степеней митральной недостаточности", fontsize=14, fontweight="bold" + ) + ax.set_xticks(x, labels=stages) + ax.legend() + + ax.grid() + + ax.bar_label(rects1) + ax.bar_label(rects2) + + plt.savefig("heart_implant_efficiency.png", dpi=300) + plt.show() + + +if __name__ == "__main__": + solve_cardio_task("data/medic_data.json") + + """ + Общее количество пациентов перераспределилось из правой части графика (тяжелые состояния) + в левую (легкие состояния), поэтому имплант можно считать эффективным + и рекомендовать к серийному производству. + """ diff --git a/solutions/sem02/lesson08/task1.py b/solutions/sem02/lesson08/task1.py index 89f88572f..15f5f97d1 100644 --- a/solutions/sem02/lesson08/task1.py +++ b/solutions/sem02/lesson08/task1.py @@ -1,34 +1,89 @@ from functools import partial +import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np - from IPython.display import HTML from matplotlib.animation import FuncAnimation +from traitlets import Callable + + +def get_modulated_signal(t: np.ndarray, modulation: Callable, fc: float) -> np.ndarray: + m_t = modulation(t) if modulation is not None else 1.0 + return m_t * np.sin(2 * np.pi * fc * t) + + +def update_frame( + frame_id: int, + *, + line: plt.Line2D, + axis: plt.Axes, + base_abscissa: np.ndarray, + modulation: Callable, + fc: float, + plot_duration: float, + animation_step: float, +) -> tuple[plt.Line2D]: + shift = frame_id * animation_step + + current_t = base_abscissa + shift + + ordinates = get_modulated_signal(current_t, modulation, fc) + + line.set_data(current_t, ordinates) + + axis.set_xlim(shift, shift + plot_duration) + + return (line,) def create_modulation_animation( - modulation, - fc, - num_frames, - plot_duration, - time_step=0.001, - animation_step=0.01, - save_path="" + modulation, fc, num_frames, plot_duration, time_step=0.001, animation_step=0.01, save_path="" ) -> FuncAnimation: - # ваш код - return FuncAnimation() + num_points = int(plot_duration / time_step) + base_abscissa = np.linspace(0, plot_duration, num_points) + + figure, axis = plt.subplots(figsize=(12, 6)) + + axis.set_ylabel("Амплитуда") + axis.set_xlabel("Время (с)") + axis.set_ylim(-2.0, 2.0) + + (line,) = axis.plot([], [], c="royalblue", label="Сигнал") + + axis.legend() + + # Привязываем функцию обновления + anim_func = partial( + update_frame, + line=line, + axis=axis, + base_abscissa=base_abscissa, + modulation=modulation, + fc=fc, + plot_duration=plot_duration, + animation_step=animation_step, + ) + + anim = FuncAnimation(figure, anim_func, frames=num_frames, interval=50, blit=True) + + if save_path: + anim.save(save_path, writer="pillow", fps=30) + + return anim if __name__ == "__main__": + mpl.rcParams["animation.embed_limit"] = 100.0 + 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 +93,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..3bba7cc63 100644 --- a/solutions/sem02/lesson08/task2.py +++ b/solutions/sem02/lesson08/task2.py @@ -1,53 +1,209 @@ from functools import partial +import matplotlib as mpl +import matplotlib.image as mpimg import matplotlib.pyplot as plt import numpy as np - from IPython.display import HTML from matplotlib.animation import FuncAnimation +from matplotlib.cm import ScalarMappable +from matplotlib.colors import LinearSegmentedColormap, Normalize + + +def calculate_wave_propagation( + maze: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], +) -> tuple[list[np.ndarray], list[tuple[int, int]]]: + rows, cols = maze.shape + distances = np.full((rows, cols), -1, dtype=int) + distances[start] = 0 + + queue = [start] + history = [distances.copy()] + directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + found = False + + while queue and not found: + next_queue = [] + for r, c in queue: + if (r, c) == end: + found = True + break + + for dr, dc in directions: + nr, nc = r + dr, c + dc + if 0 <= nr < rows and 0 <= nc < cols: + if maze[nr, nc] == 1 and distances[nr, nc] == -1: + distances[nr, nc] = distances[r, c] + 1 + next_queue.append((nr, nc)) + + if not found: + queue = next_queue + if queue: + history.append(distances.copy()) + else: + history.append(distances.copy()) + + path = [] + if found: + curr = end + path.append(curr) + while curr != start: + r, c = curr + curr_dist = distances[r, c] + for dr, dc in directions: + nr, nc = r + dr, c + dc + if 0 <= nr < rows and 0 <= nc < cols: + if distances[nr, nc] == curr_dist - 1: + curr = (nr, nc) + path.append(curr) + break + path.reverse() + + return history, path +def update_frame( + frame_id: int, + *, + ax_img: mpimg.AxesImage, + history: list[np.ndarray], + path: list[tuple[int, int]], + start: tuple[int, int], + end: tuple[int, int], + base: np.ndarray, +) -> tuple[mpimg.AxesImage]: + max_dist = np.max(history[-1]) if history else 1 + history_len = len(history) + path_len = len(path) if path else 0 + + if frame_id < history_len: + distances = history[frame_id] + current_path = [] + elif frame_id < history_len + path_len: + distances = history[-1] + step = frame_id - history_len + 1 + current_path = path[-step:] + else: + distances = history[-1] + current_path = path + + img = base.copy() + + mask = distances >= 0 + if np.any(mask): + norm_dist = distances[mask] / max(max_dist, 1) + img[mask, 0] = 0.2 + img[mask, 1] = 1.0 - 0.6 * norm_dist + img[mask, 2] = 1.0 + + if current_path: + path_x, path_y = zip(*current_path) + img[path_x, path_y] = [1.0, 0.8, 0.0] + + img[start] = [0.0, 0.8, 0.0] + img[end] = [0.8, 0.0, 0.0] + + ax_img.set_data(img) + return (ax_img,) 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: + history, path = calculate_wave_propagation(maze, start, end) + + if not path: + print(f"Путь от {start} к {end} не существует!") + + fig, ax = plt.subplots(figsize=(6, 6)) + + colors = [(0.2, 1.0, 1.0), (0.2, 0.4, 1.0)] + custom_cmap = LinearSegmentedColormap.from_list("cmap", colors) + + norm = Normalize(vmin=0, vmax=np.max(history[-1])) + sm = ScalarMappable(norm=norm, cmap=custom_cmap) + fig.colorbar(sm, ax=ax) + + ax.set_xticks([]) + ax.set_yticks([]) + + ax.set_xticks(np.arange(-0.5, maze.shape[1], 1), minor=True) + ax.set_yticks(np.arange(-0.5, maze.shape[0], 1), minor=True) + ax.grid(which="minor", color="black", linestyle="-", linewidth=1) + ax.tick_params(which="minor", size=0) + + img = np.zeros((maze.shape[0], maze.shape[1], 3)) + ax_img = ax.imshow(img, interpolation="nearest") + + total_frames = len(history) + (len(path) if path else 0) + 10 + + base_img = np.zeros((maze.shape[0], maze.shape[1], 3)) + base_img[maze == 0] = [0.2, 0.2, 0.2] + base_img[maze == 1] = [1.0, 1.0, 1.0] + + anim_func = partial( + update_frame, + ax_img=ax_img, + history=history, + path=path, + start=start, + end=end, + base=base_img, + ) + + anim = FuncAnimation( + fig, + anim_func, + frames=total_frames, + interval=5, + blit=True, + ) + + if save_path: + anim.save(save_path, writer="pillow", fps=10) + + return anim + if __name__ == "__main__": + mpl.rcParams["animation.embed_limit"] = 100.0 # Пример 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], - ]) - - start = (2, 0) - end = (5, 0) - save_path = "labyrinth.gif" # Укажите путь для сохранения анимации - - animation = animate_wave_algorithm(maze, start, end, save_path) + maze_1 = 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_1 = (2, 0) + end_1 = (5, 0) + save_path_1 = "labyrinth.gif" + + animation = animate_wave_algorithm(maze_1, start_1, end_1, save_path_1) HTML(animation.to_jshtml()) - + # Пример 2 - maze_path = "./data/maze.npy" - loaded_maze = np.load(maze_path) - # можете поменять, если захотите запустить из других точек - start = (2, 0) - end = (5, 0) + loaded_maze = np.load(maze_path) + start_2 = (0, 18) + end_2 = (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_2, + end_2, + 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..876f18f6c 100644 --- a/solutions/sem02/lesson10/task1.ipynb +++ b/solutions/sem02/lesson10/task1.ipynb @@ -34,14 +34,168 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "d7b00711", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivedpclasssexagesibspparchfareembarkedclasswhoadult_maledeckembark_townalivealone
69502male52.00013.5000SSecondmanTrueNaNSouthamptonnoTrue
3401male28.01082.1708CFirstmanTrueNaNCherbourgnoFalse
61303maleNaN007.7500QThirdmanTrueNaNQueenstownnoTrue
5511maleNaN0035.5000SFirstmanTrueCSouthamptonyesTrue
43712female24.02318.7500SSecondwomanFalseNaNSouthamptonyesFalse
\n", + "
" + ], + "text/plain": [ + " survived pclass sex age sibsp parch fare embarked class \\\n", + "695 0 2 male 52.0 0 0 13.5000 S Second \n", + "34 0 1 male 28.0 1 0 82.1708 C First \n", + "613 0 3 male NaN 0 0 7.7500 Q Third \n", + "55 1 1 male NaN 0 0 35.5000 S First \n", + "437 1 2 female 24.0 2 3 18.7500 S Second \n", + "\n", + " who adult_male deck embark_town alive alone \n", + "695 man True NaN Southampton no True \n", + "34 man True NaN Cherbourg no False \n", + "613 man True NaN Queenstown no True \n", + "55 man True C Southampton yes True \n", + "437 woman False NaN Southampton yes False " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ + "import numpy as np\n", "import seaborn as sns\n", "\n", - "\n", "titanic_data = sns.load_dataset(\"titanic\")\n", "titanic_data.sample(5)" ] @@ -66,12 +220,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "cd2f1773", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "survived 0\n", + "pclass 0\n", + "sex 0\n", + "age 177\n", + "sibsp 0\n", + "parch 0\n", + "fare 0\n", + "embarked 2\n", + "class 0\n", + "who 0\n", + "adult_male 0\n", + "deck 688\n", + "embark_town 2\n", + "alive 0\n", + "alone 0\n", + "dtype: int64" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "titanic_data.isnull().sum()" ] }, { @@ -88,12 +268,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "7e458447", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "survived 0\n", + "pclass 0\n", + "sex 0\n", + "age 177\n", + "sibsp 0\n", + "parch 0\n", + "fare 0\n", + "embarked 2\n", + "class 0\n", + "who 0\n", + "adult_male 0\n", + "embark_town 2\n", + "alive 0\n", + "alone 0\n", + "dtype: int64" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "titanic_data = titanic_data.dropna(axis=1, thresh=titanic_data.shape[0] // 2 + 1)\n", + "titanic_data = titanic_data.dropna(axis=0, thresh=titanic_data.shape[1] // 2 + 1)\n", + "\n", + "titanic_data.isnull().sum()" ] }, { @@ -111,12 +319,51 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "f7db5e99", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "survived 0\n", + "pclass 0\n", + "sex 0\n", + "age 0\n", + "sibsp 0\n", + "parch 0\n", + "fare 0\n", + "embarked 2\n", + "class 0\n", + "who 0\n", + "adult_male 0\n", + "embark_town 2\n", + "alive 0\n", + "alone 0\n", + "dtype: int64" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "man_med = np.round(titanic_data.loc[titanic_data[\"who\"] == \"man\", \"age\"].median())\n", + "woman_med = np.round(titanic_data.loc[titanic_data[\"who\"] == \"woman\", \"age\"].median())\n", + "child_med = np.round(titanic_data.loc[titanic_data[\"who\"] == \"child\", \"age\"].median())\n", + "\n", + "titanic_data.loc[titanic_data[\"who\"] == \"man\", \"age\"] = titanic_data.loc[\n", + " titanic_data[\"who\"] == \"man\", \"age\"\n", + "].fillna(man_med)\n", + "titanic_data.loc[titanic_data[\"who\"] == \"woman\", \"age\"] = titanic_data.loc[\n", + " titanic_data[\"who\"] == \"woman\", \"age\"\n", + "].fillna(woman_med)\n", + "titanic_data.loc[titanic_data[\"who\"] == \"child\", \"age\"] = titanic_data.loc[\n", + " titanic_data[\"who\"] == \"child\", \"age\"\n", + "].fillna(child_med)\n", + "\n", + "titanic_data.isnull().sum()" ] }, { @@ -131,12 +378,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "67a0c6bc", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "survived 0\n", + "pclass 0\n", + "sex 0\n", + "age 0\n", + "sibsp 0\n", + "parch 0\n", + "fare 0\n", + "embarked 2\n", + "class 0\n", + "who 0\n", + "adult_male 0\n", + "embark_town 2\n", + "alive 0\n", + "alone 0\n", + "dtype: int64" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "titanic_data = titanic_data.dropna(axis=0, thresh=1)\n", + "titanic_data.isnull().sum()" ] }, { @@ -151,12 +424,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "6a2d0ae9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'Southampton'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "titanic_data.loc[np.argmax(titanic_data[\"embark_town\"]), \"embark_town\"]" ] }, { @@ -171,12 +455,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "11acb4b4", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(38.38)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "counts = titanic_data.shape[0]\n", + "alive = titanic_data[\"survived\"].sum()\n", + "\n", + "np.round(alive / counts * 100, 2)" ] }, { @@ -191,12 +489,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "59ec7295", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "embark_town\n", + "Southampton 217\n", + "Cherbourg 93\n", + "Queenstown 30\n", + "Name: count, dtype: int64" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "titanic_data.loc[titanic_data[\"survived\"] == 1, \"embark_town\"].value_counts()" ] }, { @@ -211,12 +524,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "34daca28", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 class: 62.96\n", + "2 class: 47.28\n", + "3 class: 24.24\n" + ] + } + ], "source": [ - "# ваш код" + "for i in range(1, 4):\n", + " counts = titanic_data.loc[titanic_data[\"pclass\"] == i].shape[0]\n", + " alive = titanic_data.loc[(titanic_data[\"survived\"] == 1) & (titanic_data[\"pclass\"] == i)].shape[\n", + " 0\n", + " ]\n", + " print(f\"{i} class:\", np.round(alive / counts * 100, 2))" ] }, { @@ -231,12 +559,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "1fc0d667", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(73.58)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "counts = titanic_data.loc[titanic_data[\"fare\"] >= 100].shape[0]\n", + "alive = titanic_data.loc[(titanic_data[\"survived\"] == 1) & (titanic_data[\"fare\"] >= 100)].shape[0]\n", + "np.round(alive / counts * 100, 2)" ] }, { @@ -251,12 +592,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "480352b6", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# ваш код" + "titanic_data.loc[(titanic_data[\"who\"] == \"child\") & (titanic_data[\"alone\"])].shape[0]" ] }, { @@ -264,13 +616,34 @@ "id": "aac62cfd", "metadata": {}, "source": [ - "Какие выводы вы можете сделать о выживших пассажирах Титаника? " + "Какие выводы вы можете сделать о выживших пассажирах Титаника? \n", + "\n", + "**Выводы:**\n", + "\n", + "1. **Общая выживаемость низкая** — выжили лишь около **38.25%** пассажиров. Катастрофа унесла жизни большинства людей на борту.\n", + "\n", + "2. **Класс билета сильно влиял на шансы выжить.** Выживаемость по классам:\n", + " - Первый класс — **62.62%**\n", + " - Второй класс — **47.28%**\n", + " - Третий класс — всего **24.24%**\n", + " \n", + " Это говорит о том, что более состоятельные пассажиры имели гораздо лучший доступ к шлюпкам.\n", + "\n", + "3. **Богатые пассажиры (билет от $100) выживали значительно чаще** — **73.58%**, что почти вдвое выше среднего. Это подтверждает связь между социальным статусом и выживанием.\n", + "\n", + "4. **Большинство пассажиров отправились из Саутгемптона** (644 человека), однако в абсолютных числах оттуда же было больше всего и выживших (217). При этом доля выживших среди отправившихся из Шербура (Cherbourg) выше — там садилось много пассажиров первого класса.\n", + "\n", + "5. **Дети редко путешествовали одни** — всего **6 детей** были без сопровождения. Большинство детей путешествовали с семьёй, что согласуется с практикой того времени.\n", + "\n", + "6. **Известная фраза «женщины и дети первыми» подтверждается данными:** в категории `who` мужчины составляли большинство погибших, тогда как женщины и дети спасались чаще.\n", + "\n", + "В совокупности эти наблюдения показывают, что **на выживание влияли в первую очередь пол, возраст и социально-экономический статус** пассажира, а не случайность." ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "base", "language": "python", "name": "python3" }, @@ -284,7 +657,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.13.11" } }, "nbformat": 4,