Решите задачи, приведённые ниже. Код каждой задачи разместите в отдельном файле, название которого соответствует указанному в условии задачи. В качестве ответа на это задание загрузите архив (ZIP) с решениями. Архив должен содержать только файлы с решениями, файл test.py и вспомогательные файлы, необходимые для отдельных задач (не должно быть папок).

Напишите тесты для решённых задач с использованием фреймворка pytest, разместите их в файле test.py. Для каждой задачи достаточно рассмотреть принципиально различные ситуации (входные данные). Всего должно быть написано не менее 60 тестов. В отличие от предыдущего домашнего задания, в данном задании предполагается, что решение каждой задачи будет представлено в виде функции (если не сказано иного), которую вам необходимо импортировать в файле test.py для последующего тестирования (т. е. в тестах должны непосредственно вызываться тестируемые функции). При импорте функций не должно выполняться ничего лишнего. При необходимости выполнить какие-либо действия при запуске файла с программой (например, запросить пользовательский ввод, осуществить замер времени выполнения функции т. д.) не забудьте заключить их в условие `if __name__ == '__main__':`.

При обнаружении ошибок или неточностей в формулировках задач свяжитесь с преподавателем.

## 1) Вычисление факториала

Реализуйте рекурсивную (fact_rec) и итерационную (fact_it) функции вычисления факториала целого положительного числа n. Сравните скорость работы реализованных функций, результат сравнения приведите в комментарии в файле с программой. Для измерения времени выполнения можно использовать модули `time` или `timeit`. Функцией math.factorial пользоваться нельзя.

**Ограничения:**

- 1 ⩽ n ⩽ 10⁵

**Название файла:** fact.py

## 2) Данные сотрудника

Реализуйте функцию show_employee, принимающую в качестве аргументов имя сотрудника (name) и величину его заработной платы (salary) и возвращающую строку с данными в следующем формате: "Иванов Иван Иванович: 30000 ₽". В случае, если значение заработной платы не было передано при вызове функции, установить его равным 100000.

**Название файла:** show_employee.py

## 3) Сумма и разность

Реализуйте функцию sum_and_sub, которая принимает в качестве аргументов два действительных числа (a и b) и возвращает их сумму и разность.

**Название файла:** sum_and_sub.py

## 4) Обработка списка

В файле process_list.py приведён код функции обработки данных списка (arr).

Перепишите приведённую функцию с использованием list comprehension. Реализуйте функцию-генератор с аналогичной функциональностью (process_list_gen). Сравните скорость работы реализованных функций, результат сравнения приведите в комментарии в файле с программой.

**Ограничения:**

- 1 ⩽ len(arr) ⩽ 10³

**Название файла:** process_list.py

## 5) Функция сложения с произвольным количеством аргументов

Реализуйте функцию my_sum, принимающую произвольное количество аргументов (действительных чисел) и возвращающую их сумму.

**Название файла:** my_sum.py

## 6) Аргументы командной строки

Перепишите программу из 5-й задачи таким образом, чтобы числа для сложения передавались при вызове программы как аргументы командной строки, а результат выводился на стандартный поток вывода.

**Название файла:** my_sum_argv.py

**Пример вызова:** python3 my_sum_argv.py 1 2 3 4 5

**Пример вывода:**

```
15
```

## 7) Сортировка файлов

Напишите программу, которая в качестве аргумента командной строки принимает путь до некоторой директории и выводит список всех файлов в этой директории, упорядоченных в лексикографическом порядке и сгруппированных по расширению. Имена вложенных подкаталогов выводить не нужно. Например, если в директории находятся файлы a.py, a.txt, b.py, b.txt, c.py, c.txt, они должны быть выведены в следующем порядке:

```
a.py
b.py
c.py
a.txt
b.txt
c.txt
```

**Подсказка:** для того, чтобы отличить строку с именем файла от строки с именем подкаталога можно использовать функции os.path.isfile или os.path.isdir.

**Название файла:** files_sort.py

## 8) Рекурсивный поиск файла

Напишите программу, которая в качестве аргумента командной строки принимает имя файла и осуществляет рекурсивный поиск указанного файла относительно директории, в которой находится файл с программой. Если файл найден -- вывести первые 5 строк, в противном случае -- вывести сообщение "Файл {название_файла} не найден".

**Подсказка:** если вы пишете рекурсивную функцию, то после рекурсивного вызова нужно возвращаться в родительский каталог при помощи os.chdir(".."). Другой вариант решения задачи: воспользоваться функцией os.walk.

**Название файла:** file_search.py

## 9) Валидация адресов электронной почты

Вам дается целое число N, за которым следуют N адресов электронной почты. Ваша задача -- вывести список, содержащий только корректные адреса электронной почты в лексикографическом порядке.

Корректные адреса электронной почты должны соответствовать следующим правилам:

- Он должен иметь формат username@websitename.extension.
- Имя пользователя может содержать только цифры, латинские буквы, тире и подчеркивания.
- Название веб-сайта может содержать только цифры и латинские буквы.
- Расширение может содержать только латинские буквы.
- Максимальная длина расширения составляет 3.

Допишите функцию fun, которая прилагается к заданию, чтобы она возвращала True для корректных адресов, а для остальных -- False.

**Формат ввода:**

Первая строка ввода -- целое число, количество адресов электронной почты. Далее следуют N строк, каждая из которых содержит адрес электронной почты.

**Ограничения:**

Не может быть введено пустых значений.

**Пример ввода:**

```
3
lara@mospolytech.ru
brian-23@mospolytech.ru
britts_54@mospolytech.ru
```

**Пример вывода:**

```
['brian-23@mospolytech.ru', 'britts_54@mospolytech.ru', 'lara@mospolytech.ru']
```

**Название файла:** email_validation.py

## 10) Числа Фибоначчи

Дополните код, приведённый в файле fibonacci.py. Вы должны сгенерировать список первых n чисел Фибоначчи, начиная с 0. Затем примените функцию map и лямбда-выражение для возведения в куб каждого из полученных чисел Фибоначчи и выведите список на стандартный поток вывода.

**Формат ввода:**

Одна строка: целое число n.

**Формат вывода:**

Список в одной строке, содержащий кубы первых n чисел Фибоначчи.

**Ограничения:**

- 1 ⩽ n ⩽ 15

**Пример ввода:**

```
5
```

**Пример вывода:**

```
[0, 1, 1, 8, 27]
```

**Название файла:** fibonacci.py

## 11) Средние оценки

Университет проводит экзамен для N студентов по X предметам. Реализуйте функцию compute_average_scores, которая подсчитывает средние баллы каждого студента. Функция принимает один параметр scores -- список из X кортежей, каждый из которых содержит по N чисел в диапазоне от 0 до 100. Каждый кортеж соответствует оценкам по определённому предмету. Элементы с одинаковым индексом в каждом из кортежей соответствуют одному и тому же студенту. Возвращает функция кортеж из X элементов -- средние баллы каждого студента.

**Формат ввода:**

Первая строка содержит значения N и X, разделенные пробелом. Следующие X строк содержат оценки, разделенные пробелом, полученные учащимися по определенному предмету.

**Ограничения:**

- 0 < N ⩽ 100
- 0 < X ⩽ 100

**Формат вывода:**

Выведите средние значения по всем учащимся в отдельных строках. Средние значения должны быть корректными с точностью до 1 десятичного знака.

**Пример ввода:**

```
5 3
89 90 78 93 80
90 91 85 88 86
91 92 83 89 90.5
```

**Пример вывода:**

```
90.0
91.0
82.0
90.0
85.5
```

**Название файла:** average_scores.py

## 12) Угол между плоскостями

Вам даны четыре точки (A, B, C, D) в трехмерной декартовой системе координат. Вам нужно вычислить угол между плоскостями, образованными точками A, B, C и B, C, D в градусах (не в радианах). Обозначим этот угол как ϕ, тогда cos(ϕ) = (X, Y)/|X||Y|, где X = AB × BC и Y = BC × CD. (X, Y) -- скалярное произведение X и Y, AB × BC -- векторное произведение AB и BC, где AB = B − A и BC = B − C. Оформите решение в виде функции plane_angle, которая принимает четыре аргумента: точки A, B, C, D (экземпляры класса Point). В файле plane_angle.py приведена заготовка класса Point -- допишите недостающие методы, которые вам потребуются для решения задачи. Функция plane_angle должна возвращать одно число -- искомый угол в градусах.

**Название файла:** plane_angle.py

## 13) Форматирование номера телефона

Вам даны номера мобильных телефонов. Отсортируйте их в порядке возрастания, затем выведите в стандартном формате: +7 (xxx) xxx-xx-xx. Указанные мобильные номера могут начинаться с 8, +7 или 0, после чего следует десятизначный номер. Также префикс может отсутствовать вовсе. Вам дана заготовка кода в файле phone_number.py -- заполните недостающие фрагменты.

**Формат ввода:**

Первая строка ввода содержит целое число N, количество номеров мобильных телефонов. Далее следуют N строк, содержащие номера мобильных телефонов.

**Формат вывода:**

Выведите N номеров мобильных телефонов отдельными строками в требуемом формате.

**Пример ввода:**

```
3
07895462130
89875641230
9195969878
```

**Пример вывода:**

```
+7 (789) 546-21-30
+7 (919) 596-98-78
+7 (987) 564-12-30
```

**Название файла:** phone_number.py

## 14) Сортировка списка

Вам предоставляется некоторая информация о людях. Для каждого человека в вашем распоряжении есть имя, фамилия, возраст и пол. Выведите их имена в определенном формате (см. ниже), отсортируйте список по возрасту в порядке возрастания, т.е. имя самого младшего человека должно быть напечатано первым. Для двух человек одного возраста выведите их в порядке ввода. Вам дана заготовка кода в файле people_sort.py -- заполните недостающие фрагменты.

**Формат ввода:**

Первая строка содержит целое число N, количество человек. Следующие N строк содержат разделенные пробелом значения имени, фамилии, возраста и пола соответственно.

**Формат вывода:**

Выведите имена в отдельных строках в приведённом ниже формате в порядке возрастания возраста.

Пример формата вывода: для "Henry Davids" вывод должен быть "Mr. Henry Davids", для "Mary George" вывод должен быть "Ms. Mary George".

**Ограничения:**

- 1 ⩽ N ⩽ 10

**Пример ввода:**

```
3
Mike Thomson 20 M
Robert Bustle 32 M
Andria Bustle 30 F
```

**Пример вывода:**

```
Mr. Mike Thomson
Ms. Andria Bustle
Mr. Robert Bustle
```

**Название файла:** people_sort.py

## 15) Комплексные числа

Для этого задания вам даны два комплексных числа, и вы должны вывести результат их сложения, вычитания, умножения, деления и операции вычисления модуля. Действительная и мнимая части должны быть представлены с точностью до двух знаков после запятой. Вам дана заготовка кода в файле complex_numbers.py -- заполните недостающие фрагменты.

**Формат ввода:**

Одна строка ввода: действительная и мнимая части числа, разделенные пробелом.

**Формат вывода:**

Для двух комплексных чисел C и D выходные данные должны быть в следующей последовательности в отдельных строках:

- C + D,
- C - D,
- C * D,
- C/D,
- mod(C),
- mod(D).

Для комплексных чисел с ненулевой действительной (A) и комплексной (B) частью выходные данные должны быть в следующем формате: A + Bi. Знак "+" должен быть заменён на "-", если B < 0.

Для комплексных чисел с нулевой комплексной частью (B), т.е. действительных чисел, выходные данные должны быть в формате: A + 0.00i.

Для комплексных чисел, где действительная часть (A) равна нулю, а комплексная часть (B) ненулевая, выходные данные должны быть: 0.00 + Bi.

**Пример ввода:**

```
2 1
5 6
```

**Пример вывода:**

```
7.00+7.00i
-3.00-5.00i
4.00+17.00i
0.26-0.11i
2.24+0.00i
7.81+0.00i
```

**Название файла:** complex_numbers.py

## 16) Монте-Карло

Реализуйте вычисление площади окружности методом Монте-Карло. Оформите решение в виде функции circle_square_mk, которая принимает два параметра: r -- радиус окружности и n -- количество экспериментов в методе Монте-Карло, и возвращает действительное число -- полученную площадь. При тестировании функции сравните полученные результаты с площадью, вычисленной по формуле. Оцените получаемую погрешность расчёта в зависимости от количества экспериментов (n), результат укажите в комментарии.

**Название файла:** circle_square_mk.py

## 17) Декоратор для логирования

Напишите декоратор function_logger, который принимает в качестве аргумента путь к файлу. Если данный декоратор добавить к функции, то в указанный файл будет логироваться информация вида:

- Название функции,
- Время вызова функции (в формате yyyy-mm-dd HH:MM:SS),
- Входящие аргументы (при наличии: в виде кортежа -- позиционные, в виде словаря -- ключевые),
- Возвращаемое значение (если есть, если нет то логировать '-')
- Время завершения работы функции (в формате yyyy-mm-dd HH:MM:SS)
- Время работы функции (в секундах).

При отсутствии указанного файла он должен быть создан, в противном случае логи должны добавляться в конец файла.

**Пример использования:**

```python
@function_logger('test.log')
def greeting_format(name):
    return f'Hello, {name}!'

greeting_format('John')
```

**Пример содержимого файла test.log:**

```
greeting_format
2024-02-19 16:00:12.670738
('John',)
Hello, John!
2024-02-19 16:00:12.670741
0:00:00.000003
```

**Название файла:** log_decorator.py

**Замечание:** При написании тестов вам может быть полезна библиотека FreezeGun.