import pytest

from pathlib import Path

from protontricks.flatpak import (get_inaccessible_paths,
                                  get_running_flatpak_version)

class TestGetRunningFlatpakVersion:
    def test_flatpak_not_active(self):
        """
        Test Flatpak version detection when Flatpak is not active
        """
        assert get_running_flatpak_version() is None

    def test_flatpak_active(self, monkeypatch, tmp_path):
        """
        Test Flatpak version detection when Flatpak is active
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        assert get_running_flatpak_version() == (1, 12, 1)

class TestGetInaccessiblePaths:
    def test_flatpak_disabled(self):
        """
        Test that an empty list is returned if Flatpak is not active
        """
        assert get_inaccessible_paths(["/fake", "/fake_2"]) == []

    def test_flatpak_active(self, monkeypatch, home_dir, tmp_path):
        """
        Test that inaccessible paths are correctly detected when
        Flatpak is active
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1\n"
            "\n"
            "[Context]\n"
            "filesystems=/mnt/SSD_A;/mnt/SSD_B;xdg-data/Steam;"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        inaccessible_paths = get_inaccessible_paths([
            "/mnt/SSD_A", "/mnt/SSD_C",
            str(home_dir / ".local/share/SteamOld"),
            str(home_dir / ".local/share/Steam")
        ])
        assert len(inaccessible_paths) == 2
        assert str(inaccessible_paths[0]) == "/mnt/SSD_C"
        assert str(inaccessible_paths[1]) == \
            str(Path("~/.local/share/SteamOld").expanduser())

    def test_flatpak_home(self, monkeypatch, tmp_path, home_dir):
        """
        Test that 'home' filesystem permission grants permission to the
        home directory
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1\n"
            "\n"
            "[Context]\n"
            "filesystems=home;"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        inaccessible_paths = get_inaccessible_paths([
            "/mnt/SSD_A", "/var/fake_path",
            str(home_dir / "fake_path"),
            str(home_dir / ".local/share/FakePath")
        ])

        assert len(inaccessible_paths) == 2
        assert str(inaccessible_paths[0]) == "/mnt/SSD_A"
        assert str(inaccessible_paths[1]) == "/var/fake_path"

    def test_flatpak_home_tilde(self, monkeypatch, tmp_path, home_dir):
        """
        Test that tilde slash is expanded if included in the list of
        file systems
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1\n"
            "\n"
            "[Context]\n"
            "filesystems=~/fake_path"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        inaccessible_paths = get_inaccessible_paths([
            str(home_dir / "fake_path"),
            str(home_dir / "fake_path_2")
        ])

        assert len(inaccessible_paths) == 1
        assert str(inaccessible_paths[0]) == str(home_dir / "fake_path_2")

    def test_flatpak_host(self, monkeypatch, tmp_path, home_dir):
        """
        Test that 'host' filesystem permission grants permission to the
        whole file system
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1\n"
            "\n"
            "[Context]\n"
            "filesystems=host;"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        inaccessible_paths = get_inaccessible_paths([
            "/mnt/SSD_A", "/var/fake_path",
            str(home_dir / "fake_path"),
        ])

        assert len(inaccessible_paths) == 0

    @pytest.mark.usefixtures("xdg_user_dir_bin")
    def test_flatpak_xdg_user_dir(self, monkeypatch, tmp_path, home_dir):
        """
        Test that XDG filesystem permissions such as 'xdg-pictures' and
        'xdg-download' are detected correctly
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1\n"
            "\n"
            "[Context]\n"
            "filesystems=xdg-pictures;"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        inaccessible_paths = get_inaccessible_paths([
            str(home_dir / "Pictures"),
            str(home_dir / "Download")
        ])

        assert len(inaccessible_paths) == 1
        assert str(inaccessible_paths[0]) == str(home_dir / "Download")

    def test_flatpak_unknown_permission(self, monkeypatch, tmp_path, caplog):
        """
        Test that unknown filesystem permissions are ignored
        """
        flatpak_info_path = tmp_path / "flatpak-info"

        flatpak_info_path.write_text(
            "[Application]\n"
            "name=fake.flatpak.Protontricks\n"
            "\n"
            "[Instance]\n"
            "flatpak-version=1.12.1\n"
            "\n"
            "[Context]\n"
            "filesystems=home;unknown-fs;"
        )
        monkeypatch.setattr(
            "protontricks.flatpak.FLATPAK_INFO_PATH", str(flatpak_info_path)
        )

        inaccessible_paths = get_inaccessible_paths([
            "/mnt/SSD",
        ])

        assert len(inaccessible_paths) == 1

        # Unknown filesystem permission is logged
        records = caplog.records

        assert len(records) == 1
        assert records[0].levelname == "WARNING"
        assert "Unknown Flatpak file system permission 'unknown-fs'" \
            in records[0].message


import click
import sqlite3
import datetime
from flask import current_app, g
from flask.cli import with_appcontext

class UserInfo(object):

    def __init__(self) -> None:
        self.user_id = None
        self.username = None
        self.role = None
        self.registered = None
        self.apitoken = None

    @classmethod
    def fromDb(cls, user_id):
        db = get_db()
        cursor = db.cursor()
        record = cursor.execute("SELECT * FROM user WHERE id = ?;", (user_id,)).fetchone()
        if record:
            user_info = cls()
            user_info.user_id = record["id"]
            user_info.username = record["username"]
            user_info.role = record["role"]
            user_info.registered = datetime.datetime.strptime(record["registered"], "%Y-%m-%d %H:%M:%S.%f")
            user_info.apitoken = record["apitoken"]
        else:
            user_info = None
        return user_info

    @property
    def registration_date(self):
        return self.registered.strftime("%Y-%m-%d")

def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row

    return g.db

def close_db(e=None):
    db = g.pop('db', None)

    if db is not None:
        db.close()

def init_db():
    db = get_db()
    with current_app.open_resource('create_table.sql') as f:
        db.executescript(f.read().decode('utf8'))

@click.command('init-db')
@with_appcontext
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)

def is_first_user():
    db = get_db()
    cursor = db.cursor()
    if len(cursor.execute("select * from user;").fetchall()) > 0:
        return False
    else:
        return True

def get_server_uuid():
    server_uuid = None
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("select server_uuid from server;").fetchone()
    if record:
        server_uuid = record["server_uuid"]
    return server_uuid

def get_server_version():
    server_version = None
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("select server_version from server;").fetchone()
    if record:
        server_version = record["server_version"]
    return server_version

def get_registration_token():
    registration_token = None
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("select registration_token from server;").fetchone()
    if record:
        registration_token = record["registration_token"]
    return registration_token

def get_operation_mode():
    operation_mode = None
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("select operation_mode from server;").fetchone()
    if record:
        operation_mode = record["operation_mode"]
    return operation_mode

def set_user_result_filters(user_id, filter_values):
    # init db usage
    db = get_db()
    cursor = db.cursor()
    # check existing entry
    current_filters = get_user_result_filters(user_id)
    if has_user_result_filters(user_id):
        # update values
        db.execute("UPDATE user_filters SET filter_direct_min_score = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_direct_min_score"])), user_id))
        db.execute("UPDATE user_filters SET filter_direct_nonlib_min_score = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_direct_nonlib_min_score"])), user_id))
        db.execute("UPDATE user_filters SET filter_frequency_min_score = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_frequency_min_score"])), user_id))
        db.execute("UPDATE user_filters SET filter_frequency_nonlib_min_score = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_frequency_nonlib_min_score"])), user_id))
        db.execute("UPDATE user_filters SET filter_unique_only = ? WHERE user_id = ?", (1 if filter_values["filter_unique_only"] else 0, user_id))
        db.execute("UPDATE user_filters SET filter_exclude_own_family = ? WHERE user_id = ?", (1 if filter_values["filter_exclude_own_family"] else 0, user_id))
        db.execute("UPDATE user_filters SET filter_function_min_score = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_function_min_score"])), user_id))
        db.execute("UPDATE user_filters SET filter_function_max_score = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_function_max_score"])), user_id))
        db.execute("UPDATE user_filters SET filter_max_num_families = ? WHERE user_id = ?", (min(100, max(0, filter_values["filter_max_num_families"])), user_id))
        db.execute("UPDATE user_filters SET filter_exclude_library = ? WHERE user_id = ?", (1 if filter_values["filter_exclude_library"] else 0, user_id))
        db.execute("UPDATE user_filters SET filter_exclude_pic = ? WHERE user_id = ?", (1 if filter_values["filter_exclude_pic"] else 0, user_id))
        db.commit()
    else:
        # insert inital values
        db.execute(
            "INSERT INTO user_filters (user_id, filter_direct_min_score, filter_direct_nonlib_min_score, filter_frequency_min_score, filter_frequency_nonlib_min_score, filter_unique_only, filter_exclude_own_family, filter_function_min_score, filter_function_max_score, filter_max_num_families, filter_exclude_library, filter_exclude_pic) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
            (user_id,
             min(100, max(0, filter_values["filter_direct_min_score"])),
             min(100, max(0, filter_values["filter_direct_nonlib_min_score"])),
             min(100, max(0, filter_values["filter_frequency_min_score"])),
             min(100, max(0, filter_values["filter_frequency_nonlib_min_score"])),
             1 if filter_values["filter_unique_only"] else 0,
             1 if filter_values["filter_exclude_own_family"] else 0,
             min(100, max(0, filter_values["filter_function_min_score"])),
             min(100, max(0, filter_values["filter_function_max_score"])),
             max(0, filter_values["filter_max_num_families"]),
             1 if filter_values["filter_exclude_library"] else 0,
             1 if filter_values["filter_exclude_pic"] else 0,
             ))
        db.commit()

def has_user_result_filters(user_id):
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("SELECT * FROM user_filters WHERE user_id = ?;", (user_id,)).fetchone()
    return True if record else False

def get_user_result_filters(user_id):
    filter_values = None
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("SELECT * FROM user_filters WHERE user_id = ?;", (user_id,)).fetchone()
    if record:
        filter_values = {
        "filter_direct_min_score": None if record["filter_direct_min_score"] == 0 else record["filter_direct_min_score"],
        "filter_direct_nonlib_min_score": None if record["filter_direct_nonlib_min_score"] == 0 else record["filter_direct_nonlib_min_score"],
        "filter_frequency_min_score": None if record["filter_frequency_min_score"] == 0 else record["filter_frequency_min_score"],
        "filter_frequency_nonlib_min_score": None if record["filter_frequency_nonlib_min_score"] == 0 else record["filter_frequency_nonlib_min_score"],
        "filter_unique_only": True if record["filter_unique_only"] else False,
        "filter_exclude_own_family": True if record["filter_exclude_own_family"] else False,
        # this is never stored as preference in the DB as its not as generic as the others
        "filter_family_name": None,
        "filter_function_min_score": None if record.get("filter_function_min_score", 0) == 0 else record["filter_function_min_score"],
        "filter_function_max_score": None if record.get("filter_function_max_score", 100) == 100 else record["filter_function_max_score"],
        # this is never stored as preference in the DB as its not as generic as the others
        "filter_function_offset": None,
        "filter_max_num_families": None if record.get("filter_max_num_families", 0) == 0 else record["filter_max_num_families"],
        # we don't store filter_max_num_samples separately but instead duplicate from family value
        "filter_min_num_samples": None if record.get("filter_min_num_samples", 0) == 0 else record["filter_min_num_samples"],
        "filter_max_num_samples": None if record.get("filter_max_num_samples", 0) == 0 else record["filter_max_num_samples"],
        "filter_exclude_library": True if record["filter_exclude_library"] else False,
        "filter_exclude_pic": True if record["filter_exclude_pic"] else False
        }
    # we appear to not have default filter values stored due to DB migration, so return empty filter dict instead
    elif user_id:
        filter_values = {
        "filter_direct_min_score": None,
        "filter_direct_nonlib_min_score": None,
        "filter_frequency_min_score": None,
        "filter_frequency_nonlib_min_score": None,
        "filter_unique_only": False,
        "filter_exclude_own_family": False,
        # this is never stored as preference in the DB as its not as generic as the others
        "filter_family_name": None,
        "filter_function_min_score": None,
        "filter_function_max_score": None,
        # this is never stored as preference in the DB as its not as generic as the others
        "filter_function_offset": None,
        "filter_max_num_families": None,
        "filter_max_num_samples": None,
        "filter_exclude_library": False,
        "filter_exclude_pic": False,
        }
    return filter_values

def get_user_by_apitoken(apitoken):
    user_id = None
    db = get_db()
    cursor = db.cursor()
    record = cursor.execute("SELECT * FROM user WHERE apitoken = ?;", (apitoken,)).fetchone()
    if record is not None:
        user_id = record["id"]
    return user_id


from pyecharts import options as opts
from pyecharts.charts import Bar, Grid, Line

city = ["2015", "2016", "2017", "2018","2010", "2020"]
data1 = [160,170,210,405,700,980]

bar = (
    Bar()
   .add_xaxis(city)
   .add_yaxis("Market Scale", data1, stack="stack1",z=1)

   # .reversal_axis()
   .set_series_opts(label_opts=opts.LabelOpts(position="top"),
                    markpoint_opts=opts.MarkPointOpts(
                    data=[
                     # opts.MarkPointItem(type_="max", name="最大值"),
                     # opts.MarkPointItem(type_="min", name="最小值"),
                         ]
                     )
                   )

    .extend_axis(
        yaxis=opts.AxisOpts(
            type_="value",
            name="Percentage",
            min_=0,
            max_=80,
            position="right",
            axisline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(color="black")
            ),
            axislabel_opts=opts.LabelOpts(formatter="{value}%"),

        )
    )

        .set_global_opts(
        yaxis_opts=opts.AxisOpts(
            name="Dollar(/Trillion)",
            min_=0,
            max_=1200,
            position="left",
            offset=0,
            axisline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(color="black")
            ),
            axislabel_opts=opts.LabelOpts(formatter="{value}"),
        ),

        title_opts=opts.TitleOpts(title="The scale of China's Artificial intelligence market from 2015 to 2020",
                                  pos_bottom='0%',pos_left='7%'),
        tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
    )
)

city1 = ["2015","2016", "2017", "2018","2010", "2020"]
data11 = [100,400,1000,1130,1000,595]

line = (
    Line()

    .add_xaxis(city1)
    .add_yaxis("Growth rate", data11,label_opts=opts.LabelOpts(is_show=False),z=10,color='black')

)

bar.overlap(line)

grid = Grid()
grid.add(bar, opts.GridOpts(pos_top="10%",pos_left="5%", pos_right="20%"), is_control_axis_index=True)
# grid.add(line, opts.GridOpts(pos_left="5%", pos_right="20%"), is_control_axis_index=True)
grid.render("The scale of China's Artificial intelligence market from 2015 to 2020.html")


import tkinter as tk
import matplotlib.pyplot as plt
import scipy.optimize as sc
import numpy as np

from tkinter import messagebox, ttk
from time import perf_counter
from math import sin, sqrt, floor, log10, copysign, fabs

errors = [
    "Нет ошибок",
    "Превышено максимальное число итераций",
    "Корень находится на границе отрезка",
    "Деление на 0",
    "Длина отрезка меньше значения точности"
]

funcStr = "sin(x)"

def f(x):
    return eval(funcStr, {'x':x, 'sin': sin})

def yList(x):
    res = []
    for xi in x:
        res.append(f(xi))
    return res

def numToStr(x, digit=7):
    if x == 0:
        return '0'
    if abs(x) >= 10**digit:
        return ('{:.'+str(digit-1)+'e}').format(x)
    if abs(x) >= 10**(digit-1):
        return str(int(x))
    if abs(x) < 0.1:
        return ('{:.'+str(digit-1)+'e}').format(x)
    if abs(x) < 1:
        return ('{:.'+str(digit)+'f}').format(x)
    return str(round(x, -floor(log10(abs(x)))-1 + digit))

class Graph:
    def __init__(self, master, roots, a, b):
        x = np.linspace(a, b, 1000)
        y = yList(x)
        plt.plot(x, y, label = "f(x) = sin(x)")
        plt.title("Graph of function")

        xRoot = []
        for rootInfo in roots:
            xRoot.append(rootInfo["own"]["root"])
        yRoot = yList(xRoot)
        plt.plot(xRoot, yRoot, 'mo', label = "roots")
        plt.legend(loc = 1)
        plt.show()

class Metods:
    @staticmethod
    def ridderOwn(f, a, b, tol = None, maxiter = 10):
        fl = f(a)
        fh = f(b)
        if fl*fh < 0:
            xl = a
            xh = b
            ans = 10**8
            for iterationNumber in range(1, maxiter + 1):
                xm = 0.5 * (xl + xh)
                fm = f(xm)
                s = sqrt(fm*fm - fl*fh)
                if s == 0:
                    return {"root": ans, "iterations": iterationNumber, "codeError": 3}
                xnew = xm + (xm-xl)*copysign(1, fl - fh)*fm/s
                if fabs(xnew-ans) <= tol:
                    return {"root": ans, "iterations": iterationNumber, "codeError": 0}
                ans = xnew
                fnew = f(ans)

                if fnew == 0.0:
                    return {"root": ans, "iterations": iterationNumber, "codeError": 5}
                if copysign(fm,fnew) != fm:
                    xl = xm
                    fl = fm
                    xh = ans
                    fh = fnew
                elif copysign(fl, fnew) != fl:
                    xh = ans
                    fh = fnew
                elif copysign(fh, fnew) != fh:
                    xl = ans
                    fl = fnew
                else:
                    print("Never get here")

                if fabs(xh - xl) <= tol:
                    return {"root": ans, "iterations": iterationNumber, "codeError": 4}
        else:
            if fl == 0:
                return {"root": a, "iterations": 0, "codeError": 2}
            if fh == 0:
                return {"root": b, "iterations": 0, "codeError": 2}
        print("Never get here")

    @staticmethod
    def ridderScipy(f, a, b, tol, maxiter):
        startTime = perf_counter()
        codeError = None
        try:
            scipyRes = sc.ridder(
                f,
                a,
                b,
                xtol = tol,
                maxiter = maxiter,
                full_output = True
                )[1]
        except ZeroDivisionError:
            codeError = 3
        except:
            codeError = 6

        if codeError == None:
            if scipyRes.root in (a, b):
                codeError = 2
            elif scipyRes.iterations > maxiter:
                codeError = 1
            else:
                codeError = 0
        return {
            "root": scipyRes.root,
            "iterations": scipyRes.iterations if scipyRes.root not in (a, b) else 0,
            "codeError": codeError
            }

class TableRoots:
    def __init__(self, master, roots):
        slave = tk.Toplevel(master)
        slave.title("Таблица корней")
        if len(roots) == 0:
            tk.Label(slave, text = "Корней нет").pick()
            return

        tree = ttk.Treeview(slave)
        tree["columns"] = ["№", "Метод", "Отрезок", "Значение X", "Значение f(x)",
         "Число итераций", "Время работы", "Код ошибки"]
        tree["show"] = "headings"

        tree.column("№", width = 30, minwidth = 30, stretch = tk.NO)
        tree.column("Метод", width = 80, minwidth = 80, stretch = tk.NO)
        tree.column("Отрезок", width = 80, minwidth = 80, stretch = tk.NO)
        tree.column("Значение X", width = 80, minwidth = 80, stretch = tk.NO)
        tree.column("Значение f(x)", width = 90, minwidth = 90, stretch = tk.NO)
        tree.column("Число итераций", width = 100, minwidth = 100, stretch = tk.NO)
        tree.column("Время работы", width = 100, minwidth = 100, stretch = tk.NO)
        tree.column("Код ошибки", width = 80, minwidth = 80, stretch = tk.NO)

        for i in tree["columns"]:
            tree.heading(i, text = i)

        index = iid = 0
        for rootNumber, row in enumerate(roots):
            rows = self.formRows(row, rootNumber + 1)
            tree.insert("", index, iid, value = rows["own"])
            index = iid = index + 1
            tree.insert("", index, iid, value = rows["scipy"])
            index = iid = index + 1
        tree.grid(column = 0, row = 0)

        textError = "Коды ошибок:\n"
        for i, error in enumerate(errors):
            textError += str(i) + " - " + error + '\n'
        tk.Label(slave, text = textError, justify = tk.LEFT).grid(row = 0, column = 1)

    def formRows(self, rootInfo, rootNumber):
        row1 = [
            rootNumber,
            "Ridder",
            rootInfo["own"]["interval"],
            numToStr(rootInfo["own"]["root"], 9),
            numToStr(f(rootInfo["own"]["root"]), 1),
            rootInfo["own"]["iterations"],
            "{:.6f} ms".format(rootInfo["own"]["time"]),
            rootInfo["own"]["codeError"]
            ]
        row2 = [
            rootNumber,
            "Ridder (scipy)",
            rootInfo["scipy"]["interval"],
            numToStr(rootInfo["scipy"]["root"], 9),
            numToStr(f(rootInfo["scipy"]["root"]), 1),
            rootInfo["scipy"]["iterations"],
            "{:.6f} ms".format(rootInfo["scipy"]["time"]),
            rootInfo["scipy"]["codeError"]]
        return {"own": row1, "scipy": row2}

class Application:
    def __init__(self, master):
        self.master = master
        self.master.title("lab_14")

        #Вывод Label
        tk.Label(master, text = "Уточнение корней").grid(row = 0, column = 1)
        labelTexts = [
            "Функция",
            "Начало интервала",
            "Конец интервала",
            "Шаг разбиения",
            "Точность",
            "Максимальное число итераций"
            ]
        for i, labelText in enumerate(labelTexts):
            tk.Label(master, text = labelText).grid(row = i+1, column = 0)

        #Вывод Entry
        defaultValues = [
            "sin(x)",
            "0",
            "10",
            "1",
            "0.001",
            "10"
            ]

        self.entFunc = tk.Entry(master)
        self.entStart = tk.Entry(master)
        self.entEnd = tk.Entry(master)
        self.entStep = tk.Entry(master)
        self.entTol = tk.Entry(master)
        self.entMaxIter = tk.Entry(master)

        dictKeys = ("func", "start", "end", "step", "tol", "maxIter")
        dictValues = (
            self.entFunc,
            self.entStart,
            self.entEnd,
            self.entStep,
            self.entTol,
            self.entMaxIter
            )
        lbls = dict(zip(dictKeys, dictValues))

        for i, key in enumerate(dictKeys):
            v = tk.StringVar(master, value = defaultValues[i])
            lbls[key].config(textvariable =  v)
            lbls[key].grid(column = 1, row = i + 1)

        #Вывод кнопки
        tk.Button(text = "Найти корни", command = self.onClick).grid(column = 1, row = len(dictKeys) + 1)

    def onClick(self):
        try:
            global funcStr
            funcStr = self.entFunc.get()
            start = float(self.entStart.get())
            end = float(self.entEnd.get())
            step = float(self.entStep.get())
            tol  = float(self.entTol.get())
            maxIter = int(self.entMaxIter.get())
        except:
            messagebox.showerror(
                title = "Ошибка",
                message = "Введены некорректные данные"
                )
        else:
            roots = self.searchRoots(
                start,
                end,
                step,
                tol,
                maxIter
                )
            self.displayRoots(roots)
            self.showGraph(roots, start, end)

    def searchRoots(self, start, end, step, tol, maxIter):
        roots = []
        a, b = start, start + step
        if a < end and b > end:
            b = end
        rootNum = 0
        while a < end:
            if f(a)*f(b) <= 0:
                rootNum += 1

                timeStart = perf_counter()
                myRes = Metods.ridderOwn(
                    f,
                    a,
                    b,
                    tol = tol,
                    maxiter = maxIter
                    )
                myRes["time"] = 1000*(perf_counter() - timeStart)
                myRes["interval"] = "[{:s}; {:s}]".format(numToStr(a, 3), numToStr(b, 3))

                timeStart = perf_counter()
                scipyRes = Metods.ridderScipy(
                    f,
                    a,
                    b,
                    tol = tol,
                    maxiter = maxIter
                    )
                scipyRes["time"] = 1000*(perf_counter() - timeStart)
                scipyRes["interval"] = "[{:s}; {:s}]".format(numToStr(a, 3), numToStr(b, 3))

                roots.append({"own": myRes, "scipy": scipyRes})

            a, b = a + step, b + step
            if a < end and b > end:
                b = end
        return roots

    def displayRoots(self, roots):
        TableRoots(self.master, roots)

    def showGraph(self, roots, start, end):
        Graph(self.master, roots, start, end)

def main():
    root = tk.Tk()
    app = Application(root)
    root.mainloop()

if __name__ == "__main__":
    main()


from __future__ import absolute_import, unicode_literals
import time
from bs4 import BeautifulSoup
import random
from Crypto import Random
from Crypto.PublicKey import RSA
import base64
import requests
import time
from Crypto.Cipher import AES
import base64, os
import json
import string
from steganography.steganography import Steganography
import base58

def fake_message():
    lines = open('sentences.txt').read().splitlines()
    myline =random.choice(lines)
    print(myline)
    return myline

def chunky(seq, num):
    avg = len(seq) / float(num)
    out = []
    last = 0.0

    while last < len(seq):
        out.append(seq[int(last):int(last + avg)])
        last += avg

    return out

def post_message_to_relay(message):

    requests.post("http://34.217.16.124:5000/blast", data={"message-ujwjejwjaq" : message})

def get_message_from_relay():

    return requests.get("http://34.217.16.124:5000/blast", stream = True).content

def key_exchange_mode():

    while(1):
        time.sleep(5)
        _text = requests.get("http://34.217.16.124:5000/blast").text
        if json.loads(text)["key"] and json.loads(text)["key"] != key:
            return key

def generate_secret_key_for_AES_cipher():
    # AES key length must be either 16, 24, or 32 bytes long
    AES_key_length = 16 # use larger value in production
    # generate a random secret key with the decided key length
    # this secret key will be used to create AES cipher for encryption/decryption
    secret_key = os.urandom(AES_key_length)
    # encode this secret key for storing safely in database
    encoded_secret_key = base64.b64encode(secret_key)
    return encoded_secret_key

def encrypt_message(private_msg, encoded_secret_key, padding_character):
    # decode the encoded secret key
    secret_key = base64.b64decode(encoded_secret_key)
    # use the decoded secret key to create a AES cipher
    cipher = AES.new(secret_key)
    # pad the private_msg
    # because AES encryption requires the length of the msg to be a multiple of 16
    padded_private_msg = private_msg + (padding_character * ((16-len(private_msg)) % 16))
    # use the cipher to encrypt the padded message
    encrypted_msg = cipher.encrypt(padded_private_msg)
    # encode the encrypted msg for storing safely in the database
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
    # return encoded encrypted message
    return encoded_encrypted_msg

def decrypt_message(encoded_encrypted_msg, encoded_secret_key, padding_character):
    # decode the encoded encrypted message and encoded secret key
    secret_key = base64.b64decode(encoded_secret_key)
    encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    # use the decoded secret key to create a AES cipher
    cipher = AES.new(secret_key)
    # use the cipher to decrypt the encrypted message
    decrypted_msg = cipher.decrypt(encrypted_msg)
    # unpad the encrypted message
    unpadded_private_msg = decrypted_msg.rstrip(padding_character)
    # return a decrypted original private message
    return unpadded_private_msg

def hide_in_attribute(html_page_file, shards, tag_list):

    shard_num = 0
    tag_nums = []
    for shard in shards:
        shard = shards[0]
        html_doc = open(html_page_file).read()
        soup = BeautifulSoup(html_doc, 'html.parser')
        tags = soup.find_all(tag_list[shard_num])
        num_tags = len(tags) - 1
        x = random.randint(0, num_tags)
        y = random.randint(10,100)
        tag_name = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(y)])
        tags[x][tag_name] = shard + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        #print(soup.prettify().encode("utf-8").strip())
        fw = open(html_page_file, "w")
        #print(soup.prettify().encode("utf-8").strip())
        fw.write(soup.prettify().encode("utf-8").strip())
        tag_nums.append({x : tag_list[shard_num], "tag_name" : tag_name, "shard_num" : shard_num, "html_page_file" : html_page_file,})
        shard_num += 1
        fw.close()

    return json.dumps(tag_nums)

def hide_metadata(message):

    #encoded = base64.b64encode(message.encode())
    #encoded = base58.b58encode(encoded.encode())
    r = requests.post("http://34.217.16.124:5000/img/ewflwkewhnuhfnuajfjn", data={"message-ujwjejwjaq" : message})

def get_metadata():
    r = requests.get("http://34.217.16.124:5000/img/ewflwkewhnuhfnuajfjn")
    #message = base58.b58decode(r.text.encode())
    #message = base64.b64decode(r.text.encode())
    return r.text

def generate_fake_data(html_page_file, tag_list):

    for i in range(10):
        html_doc = open(html_page_file).read()
        soup = BeautifulSoup(html_doc, 'html.parser')
        #note no tag or shard num passed here, letting bs do some randomization for us
        tags = soup.find_all(tag_list)
        num_tags = len(tags) - 1
        x = random.randint(0, num_tags)
        y = random.randint(10,100)
        tag_name = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(y)])
        tags[x][tag_name] = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(y)]) + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
        fw = open(html_page_file, "w")
        fw.write(soup.prettify().encode("utf-8").strip())
        fw.close()

if __name__ == "__main__":

        private_msg = """
 Lorem ipsum dolor sit amet, malis recteque posidonium ea sit, te vis meliore verterem. Duis movet comprehensam eam ex, te mea possim luptatum gloriatur. Modus summo epicuri eu nec. Ex placerat complectitur eos.
"""
        padding_character = "{"

        secret_key = generate_secret_key_for_AES_cipher()
        encrypted_msg = encrypt_message(private_msg, secret_key, padding_character)
        decrypted_msg = decrypt_message(encrypted_msg, secret_key, padding_character)
        sharded_encrypted_message = chunky(encrypted_msg, 3)
        print(encrypted_msg)

        f = open("index.html").read()

        meta = hide_in_attribute("index.html", sharded_encrypted_message, ["a", "div", "p"])
        time.sleep(1)

        generate_fake_data("index.html", ["span", "div", "p"])
        time.sleep(1)
        post_message_to_relay(f)
        time.sleep(1)

        hide_metadata(encrypt_message(meta, secret_key, padding_character))
        time.sleep(1)
        print(decrypt_message(get_metadata(), secret_key, padding_character))


# -*- coding: utf-8 -*-
#
# This file is part of gpgliblib (https://github.com/mathiasertl/gpgliblib).
#
# gpgliblib is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# gpgliblib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with gpgliblib. If
# not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import
from __future__ import unicode_literals

from contextlib import contextmanager
from datetime import datetime
from threading import local

import gpgme
import gpgme.editutil
import six
from gpgme import ERR_EOF
from gpgme import ERR_GENERAL
from gpgme import ERR_SOURCE_GPGME
from gpgme import ERR_SOURCE_UNKNOWN
from gpgme import ERR_UNUSABLE_PUBKEY

from .base import GpgBackendBase
from .base import GpgBadSignature
from .base import GpgDecryptionFailed
from .base import GpgKey
from .base import GpgKeyNotFoundError
from .base import GpgSecretKeyPresent
from .base import GpgUntrustedKeyError
from .base import MODE_ARMOR
from .base import UnknownGpgliblibError
from .base import VALIDITY_FULL
from .base import VALIDITY_MARGINAL
from .base import VALIDITY_NEVER
from .base import VALIDITY_ULTIMATE
from .base import VALIDITY_UNKNOWN
from .utils import get_version

class GpgMeBackend(GpgBackendBase):
    """A gpgliblib backend using `pygpgme <https://pypi.python.org/pypi/pygpgme/>`_.

    ``pygpgme`` cannot get the GnuPG version used by itself, and setting key trust works different
    depending on what version is used. This backend thus requires the ``gnupg_version`` parameter
    to be passed if you want to set the trust of keys.
    """

    def __init__(self, **kwargs):
        super(GpgMeBackend, self).__init__(**kwargs)
        self._local = local()

    @property
    def context(self):
        if hasattr(self._local, 'context') is False:
            context = gpgme.Context()
            context.armor = True

            if self._path or self._home:
                context.set_engine_info(gpgme.PROTOCOL_OpenPGP, self._path, self._home)
            self._local.context = context

        return self._local.context

    @contextmanager
    def _attrs(self, signers=None, armor=None):
        context = self.context

        if signers is not None:
            old_signers = context.signers
            context.signers = signers
        if armor is not None:
            old_armor = context.armor
            context.armor = armor

        try:
            yield context
        finally:
            if signers is not None:
                context.signers = old_signers
            if armor is not None:
                context.armor = old_armor

    @property
    def gnupg_version(self):
        if self._gnupg_version is None:
            self._gnupg_version = get_version()

        return self._gnupg_version

    def get_key(self, fingerprint):
        return GpgMeKey(self, fingerprint)

    def _get_gpgme_key(self, obj):
        if isinstance(obj, six.string_types):
            return GpgMeKey(self, obj)._key
        return obj._key

    def _encrypt_flags(self, always_trust=True, **kwargs):
        flags = 0
        if always_trust is True:
            flags |= gpgme.ENCRYPT_ALWAYS_TRUST
        return flags

    def _encrypt(self, data, recipients, always_trust):
        recipients = [self._get_gpgme_key(r) for r in recipients]

        output_bytes = six.BytesIO()
        flags = self._encrypt_flags(always_trust=always_trust)
        try:
            if self.context.signers:
                self.context.encrypt_sign(recipients, flags, six.BytesIO(data), output_bytes)
            else:
                self.context.encrypt(recipients, flags, six.BytesIO(data), output_bytes)
        except gpgme.GpgmeError as e:
            if e.source == ERR_SOURCE_UNKNOWN and e.code == ERR_GENERAL:  # pragma: gpg1
                raise GpgUntrustedKeyError("Key not trusted.")

            if e.source == ERR_SOURCE_GPGME and e.code == ERR_UNUSABLE_PUBKEY:  # pragma: gpg2
                raise GpgUntrustedKeyError("Key not trusted.")

            raise UnknownGpgliblibError(e.strerror)  # pragma: no cover

        output_bytes.seek(0)
        return output_bytes.getvalue()

    def sign(self, data, signer):
        output_bytes = six.BytesIO()

        with self._attrs(signers=[self._get_gpgme_key(signer)]) as context:
            context.sign(six.BytesIO(data), output_bytes, gpgme.SIG_MODE_DETACH)

        output_bytes.seek(0)
        return output_bytes.getvalue()

    def encrypt(self, data, recipients, **kwargs):
        always_trust = kwargs.get('always_trust', self._default_trust)
        return self._encrypt(data, recipients, always_trust)

    def sign_encrypt(self, data, recipients, signer, **kwargs):
        always_trust = kwargs.get('always_trust', self._default_trust)
        with self._attrs(signers=[self._get_gpgme_key(signer)]):
            return self._encrypt(data, recipients, always_trust)

    def verify(self, data, signature):
        signatures = self.context.verify(six.BytesIO(signature), six.BytesIO(data), None)

        errors = list(filter(lambda s: s.status is not None, signatures))
        if not errors:
            return signatures[0].fpr
        raise GpgBadSignature("Bad signature", errors=errors)

    def decrypt(self, data):
        output = six.BytesIO()
        try:
            self.context.decrypt(six.BytesIO(data), output)
        except gpgme.GpgmeError as e:
            if e.source == gpgme.ERR_SOURCE_GPGME and e.code == gpgme.ERR_DECRYPT_FAILED:
                raise GpgDecryptionFailed(e.strerror)
            raise UnknownGpgliblibError(e.strerror)  # pragma: no cover

        return output.getvalue()

    def decrypt_verify(self, data):
        output = six.BytesIO()
        signatures = self.context.decrypt_verify(six.BytesIO(data), output)

        errors = list(filter(lambda s: s.status is not None, signatures))
        if not errors:
            if signatures:
                return output.getvalue(), signatures[0].fpr
            else:
                return output.getvalue(), None

    def import_key(self, data):
        if six.PY3 and isinstance(data, str):  # pragma: py3
            data = data.encode('utf-8')
        elif six.PY2 is True and isinstance(data, unicode):  # pragma: py2
            data = data.encode('utf-8')

        result = self.context.import_(six.BytesIO(data))
        return [GpgMeKey(self, r[0]) for r in result.imports]

    def import_private_key(self, data):
        if six.PY3 and isinstance(data, str):
            data = data.encode('utf-8')

        result = self.context.import_(six.BytesIO(data))
        return list(set([GpgMeKey(self, r[0]) for r in result.imports]))

    def list_keys(self, query=None, secret_keys=False):
        return [GpgMeKey(self, key=k) for k in self.context.keylist(query, secret_keys)]

class GpgMeKey(GpgKey):
    _loaded_key = None
    _loaded_secret_key = None

    def __init__(self, backend, fingerprint=None, key=None):
        if not fingerprint and not key:
            raise ValueError("Must pass either fingerprint or key.")
        elif not fingerprint:
            fingerprint = key.subkeys[0].fpr

        super(GpgMeKey, self).__init__(backend, fingerprint)
        self._loaded_key = key

    @property
    def _key(self):
        if self._loaded_key is None:
            try:
                self._loaded_key = self.backend.context.get_key(self.fingerprint)
            except gpgme.GpgmeError as e:
                if e.source == ERR_SOURCE_GPGME and e.code == ERR_EOF:
                    raise GpgKeyNotFoundError(self.fingerprint)

                raise UnknownGpgliblibError(e.strerror)  # pragma: no cover
        return self._loaded_key

    @property
    def _secret_key(self):
        if self._loaded_secret_key is None:
            try:
                self._loaded_secret_key = self.backend.context.get_key(self.fingerprint, True)
            except gpgme.GpgmeError:
                self._loaded_secret_key = False
        return self._loaded_secret_key

    def refresh(self):
        self._loaded_key = None

    @property
    def name(self):
        return self._key.uids[0].name

    @property
    def comment(self):
        comment = self._key.uids[0].comment
        if comment:
            return comment

    @property
    def email(self):
        return self._key.uids[0].email

    @property
    def has_secret_key(self):
        return self._secret_key is not False

    @property
    def trust(self):
        if self._key.owner_trust == gpgme.VALIDITY_UNKNOWN:
            return VALIDITY_UNKNOWN
        elif self._key.owner_trust == gpgme.VALIDITY_NEVER:
            return VALIDITY_NEVER
        elif self._key.owner_trust == gpgme.VALIDITY_MARGINAL:
            return VALIDITY_MARGINAL
        elif self._key.owner_trust == gpgme.VALIDITY_FULL:
            return VALIDITY_FULL
        elif self._key.owner_trust == gpgme.VALIDITY_ULTIMATE:
            return VALIDITY_ULTIMATE
        else:  # pragma: no cover
            return VALIDITY_UNKNOWN

    @trust.setter
    def trust(self, value):
        if value == VALIDITY_NEVER:
            value = gpgme.VALIDITY_NEVER
        elif value == VALIDITY_MARGINAL:
            value = gpgme.VALIDITY_MARGINAL
        elif value == VALIDITY_FULL:
            value = gpgme.VALIDITY_FULL
        elif value == VALIDITY_ULTIMATE:
            value = gpgme.VALIDITY_ULTIMATE
        else:
            raise ValueError("Unknown value passed.")

        if self.backend.gnupg_version >= (2, ):  # pragma: gpg2

            @gpgme.editutil.key_editor
            def _edit_trust_gnupg2(ctx, key, trust):
                """Copy of gpgme.editutil.edit_trust fixed for gpg2."""
                if trust not in (gpgme.VALIDITY_UNDEFINED,
                                 gpgme.VALIDITY_NEVER,
                                 gpgme.VALIDITY_MARGINAL,
                                 gpgme.VALIDITY_FULL,
                                 gpgme.VALIDITY_ULTIMATE):
                    raise ValueError('Bad trust value %d' % trust)

                status, args = yield None

                # we need to yield an additional None in gpg2.
                status, args = yield None

                assert args == 'keyedit.prompt'
                status, args = yield 'trust\n'

                assert args == 'edit_ownertrust.value'
                status, args = yield '%d\n' % trust

                if args == 'edit_ownertrust.set_ultimate.okay':
                    status, args = yield 'Y\n'

                assert args == 'keyedit.prompt'
                status, args = yield 'quit\n'

                assert args == 'keyedit.save.okay'
                status, args = yield 'Y\n'

            _edit_trust_gnupg2(self.backend.context, self._key, value)
        else:
            gpgme.editutil.edit_trust(self.backend.context, self._key, value)
        self.refresh()

    @property
    def expires(self):
        expires = lambda i: datetime.fromtimestamp(i) if i else None
        subkeys = {sk.fpr: expires(sk.expires) for sk in self._key.subkeys}
        return subkeys[self.fingerprint]

    @property
    def revoked(self):
        return self._key.revoked

    def export(self, mode=MODE_ARMOR, output=None):
        if output is None:
            buf = six.BytesIO()
        else:
            buf = output

        if mode == MODE_ARMOR:
            args = {'armor': True}
        else:
            args = {'armor': False}

        with self.backend._attrs(**args) as context:
            context.export(self.fingerprint, buf)

        if output is None:
            value = buf.getvalue()
            if mode == MODE_ARMOR:
                return value.decode('utf-8')
            return value

    def delete(self, secret_key=False):
        if self.has_secret_key and not secret_key:
            raise GpgSecretKeyPresent('Secret key is present.')

        # NOTE: unlike the unofficial docs, this function does not take any keyword arguments
        self.backend.context.delete(self._key, secret_key)