# Написать два алгоритма нахождения i-го по счёту простого числа.
# Без использования Решета Эратосфена;
# Использовать алгоритм решето Эратосфена


import timeit
import cProfile
import sys
import platform
from memory_profiler import profile
from memory_profiler import memory_usage

# нахождение простых числе через перебор делителей


@profile
def natur(f):
    s = []
    for i in range(2, f + 1):
        for j in range(2, i):
            if i % j == 0:
                break
        else:
            s.append(i)
    return s


# нахождение через решето
@profile
def erat(f):
    s = []
    e = [int(i) for i in range(2, f + 1)]
    num = 2  # установка первого простого числа
    n = 2
    for i in range(1, len(e)):  # запуск перебора чисел
        while n <= f:  # запуск цикла перебора чисел кратных простому
            for j in range(i+1, len(e)):  # проверка на кратность
                if e[j] == n:
                    e[j] = 0  # "прокол" числа, кратного взятому простому числу
                    break  # остановка прогона, после нахождения
                elif e[j] > n:
                    break  # остановка прогона, после прохождения установленного числа для проверки кратности
            n += num  # установка следующего кратного числа
        if e[i] > num:  # поиск оставшегося в решете числа, после цикла удаления кратных чисел по предудущему простому числу
            num = e[i]
            n = num
    for i in e:  # создание списка простых чисел в заданном диапозоне
        if i > 0:
            s.append(i)
    return s


# нахождение через решето 2
@profile
def erat2(f):
    s = [int(i) for i in range(f + 1)]
    s[1] = 0
    ss = []
    for i in s:
        if i > 1:
            for j in range(i + i, f, i):
                s[j] = 0

    for i in range(f + 1):
        if s[i]:
            ss.append(i)

    return ss


@profile
def main():
    f = int(input("введите искомое натуральное число: "))
    print(natur(f))
    print(timeit.timeit("natur(f)", setup="from __main__ import natur", number=1, globals={"f": f}))
    print(erat(f))
    print(timeit.timeit("erat(f)", setup="from __main__ import erat", number=1, globals={"f": f}))
    print(erat2(f))
    print(timeit.timeit("erat2(f)", setup="from __main__ import erat2", number=1, globals={"f": f}))
    print("использованно памяти", memory_usage())

    print("\n\nДомашнее задание 6")

    print("Версия Python - ", sys.version)
    print("Разрядность ОС - ", platform.architecture())
    print("\nСтатистика по переменным\n")
    ts = 0
    v = 0
    for i in dir():
        w = str(type(eval(i)))
        if not i.startswith('__') and w != "<class 'module'>":
            print(f"адрес {id(i)}, {i}, размер {sys.getsizeof(eval(i))}, {type(eval(i))}, кол-во ссылок "
                  f"{sys.getrefcount(i)}")
            ts += sys.getsizeof(eval(i))
            v += 1
    print(f"Общий объем памяти для {v} переменных {ts}")


main()

cProfile.run("main()")

print("\nСтатистика по переменным\n")
v = 0
ts = 0
for i in dir():
    w = str(type(eval(i)))
    if not i.startswith('__') and w != "<class 'module'>":
        print(
            f"адрес {id(i)}, {i}, размер {sys.getsizeof(eval(i))}, {type(eval(i))}, кол-во ссылок {sys.getrefcount(i)}")
        ts += sys.getsizeof(eval(i))
        v += 1

print(f"Общий объем памяти для {v} переменных {ts}")

# Выводы:
# 1. Согласно данным метода @profile все модули потребовали 34.8 MiB (по первому прогону программы)
# 2. В блоке main 3 переменных потребовали 84 MiB (по первому прогону программы)
# 3. Согласно данным метода @profile все модули потребовали 25.5 MiB  (по второму прогону программы)
# 4. Все 3 программы в ДЗ (6_1, 6_2, 6_3) потребовали примерно один объем оперативки

# Оставшиеся вопросы:
# 1. Почему программа запускается два раза? есть какая-то связь с исполнением cProfile.run("main()") -  но объяснения я
# не нашел
# 2. Почему по данным метода @profile все функции потребовали 34.8 MiB в первый запус и во второй 25.5 MiB  - это
# действительно так?? быстродействие кода и используемые типы переменных не связано напрямую - вроде логично или нет
# 3. Почему если переменные dir не показывает за пределами функции все переменные? и global тоже и работа с locals тоже
# не позволила в цикле вытащить все перменные по программе и через sys.getsizeof посчитать сколько требуют все
# переменные оперативной памяти
# 4. На что влияет количество ссылок на переменную? (особенно в разрезе темы объем памяти занимаемый программой)
# 5. Как связан объем требуемой памяти и быстродействие?
# 6. По итогам этого занятия понял следующее - все переменные в питоне это объекты, которые могут быть достаточно т
# яжеловестными - "на все случае жизни",  типы используемых данных необходимо тщательно подбирать чтобы не было
# избыточности/"избыточной тяжести", задача была научиться смотреть вес переменных, посчитать требуемый объем ОП всей
# программы достаточно сложно
# не уверен что научился корректно определять объем используемой ОП
# буду рад любой обратной связи и ответам на вопросы

# Версия
# Python - 3.7
# .2(default, Jan
# 13
# 2019, 12: 50:15)
# [Clang 10.0.0(clang - 1000.11.45.5)]
# Разрядность
# ОС - ('64bit', '')
