from ctypes import Union
from pydantic import BaseModel
from fastapi import FastAPI
from chat import Chat
from typing import Union
import os
import json

try:
    os.environ["CUDA_VISIBLE_DEVICES"] = sys.argv[1]
except:
    pass

os.environ["RWKV_JIT_ON"] = "1"
os.environ["RWKV_CUDA_ON"] = "0"

chat_model = Chat("../model/rwkv.pth", "cuda fp16i8")
chat_model.load_model()

with open(f"./pure.json", 'r', encoding='utf-8') as f:
    char = json.loads(f.read())
    chat_model.load_init_prompt(char['user'], char['bot'], char['bot_persona'], char['example_dialogue'])
    f.close()

app = FastAPI()

class chatLog(BaseModel):
    log: str
    top_p: Union[float, None] = 0.7
    temperature: Union[float, None] = 1.5
    presence_penalty: Union[float, None] = 0.3
    frequency_penalty: Union[float, None] = 0.3

@app.get("/")
async def root():
    return "Hello, This is ChatRWKV!"

@app.get("/reset")
async def root():
    chat_model.reset_bot()
    return "OK"

@app.post("/chat/")
async def create_chat(chatLog: chatLog):
    print([chatLog.log, chatLog.top_p, chatLog.temperature, chatLog.presence_penalty, chatLog.frequency_penalty])
    output = chat_model.on_message(
        chatLog.log, chatLog.top_p, chatLog.temperature, chatLog.presence_penalty, chatLog.frequency_penalty)
    print(output)
    return output


import spotipy
from spotipy.oauth2 import SpotifyOAuth
import webbrowser

class SpotifyHandler:

    def __init__(self, client_id:str, client_secret:str) -> None:
        """Args:
            client_id (str): Spotify Developer Account client id
            client_secret (str): Spotify Developer Account client secret
        """
        self.CLIENT_ID = client_id
        self.CLIENT_SECRET = client_secret
        self.sp = None
        self.USER_ID = None
        self.setup_spotify_oauth()

    def setup_spotify_oauth(self):
        """Sets up spotify oath through spotipy connection, set user id
        """
        self.sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=self.CLIENT_ID,
                                                client_secret=self.CLIENT_SECRET,
                                                redirect_uri="http://example.com",
                                                scope="playlist-modify-private",
                                                show_dialog=True,
                                                cache_path=".cache")
        )
        self.USER_ID = self.sp.current_user()['id']

    def search_for_song_on_spotify(self, track_data:dict, year:str):
        """Iterate through song list, for each song use spotipy api search to fnd song uri:
        * 1). Search for uri using song and artist.
        * 2). if song and artist api search does not result in any uri, search on song and year.
        * 3). if song and year does not result in any uri data, search on song name only.
        * Add song uri to uri list

        Args:
            track_data (dict): Dict conaitain songs and artist key value pairs
            year (str): year of publication of billboard eg 2016

        Returns:
            list: List of spotify song uris
        """
        songs:list = track_data['songs']
        artists:list =track_data['artists']

        song_uris = []
        for song in songs:
            index = songs.index(song)
            artist = artists[index]
            # Try and get song title and artist title
            query =f'track: {song} artist: {artist}'
            results = self.sp.search(q=query,limit=1,type="track")
            try:
                uri =results["tracks"]["items"][0]["uri"]
                song_uris.append(uri)
            except IndexError:
                print(f"song:'{song}' and artist:'{artist}' doesn't exist. Searching by Year")
                # Try and get song title and year
                query =f'track: {song} year: {year}'
                results = self.sp.search(q=query,limit=1,type="track")
                try:
                    uri =results["tracks"]["items"][0]["uri"]
                    song_uris.append(uri)
                except IndexError:
                    print(f"song:'{song}' and year:{year}' doesn't exist. Searching by song title")
                    # Try and get song
                    query =f'track: {song}'
                    results = self.sp.search(q=query,limit=1,type="track")
                    try:
                        uri =results["tracks"]["items"][0]["uri"]
                        song_uris.append(uri)
                    except IndexError:
                        print(f"song:'{song}' doesn't exist")
        return song_uris

    def create_user_playlist(self, date:str, song_uris:list):
        """Create a new playlist for the provided song uris.
        Open the browser to display playlist once done.

        Args:
            date (str): Billboard Date as a string
            song_uris (list): List of song uris in order
        """
        playlist = self.sp.user_playlist_create(self.USER_ID, f'{date} Billboard 100', public=False, collaborative=False, description=f'Billboards Top 100 as at {date}.')
        self.sp.playlist_add_items(playlist['id'], items=song_uris)
        print('Playlist Created')
        external_url = playlist['external_urls']['spotify']
        webbrowser.open(external_url,2)


"""
Class containing crud logic for swipe session
"""

from sqlalchemy import and_, distinct, func, join, select, update
from sqlalchemy.orm import joinedload
from core.db import session
from core.db.enums import SwipeSessionEnum
from core.db.models import (
    GroupMember,
    Recipe,
    RecipeIngredient,
    RecipeTag,
    Swipe,
    SwipeSession,
    Group,
    User,
)
from core.repository.base import BaseRepo

class SwipeSessionRepository(BaseRepo):
    """Repository class for `SwipeSession` model.

    This class provides methods to interact with the `SwipeSession` model in the database.

    Args:
        BaseRepo (class): The base repository class from which this class inherits.

    Attributes:
        model (class): The `SwipeSession` model class.

    """
    def __init__(self):
        super().__init__(SwipeSession)

    def query_options(self, query):
        return query.options(
            joinedload(SwipeSession.swipes),
            joinedload(SwipeSession.swipe_match),
            joinedload(SwipeSession.swipe_match).joinedload(Recipe.image),
        )

    async def get(self):
        """Retrieve all swipe sessions with their swipes.

        Returns:
            list[SwipeSession]: List of `SwipeSession` instances with their `swipes`.
        """
        query = select(self.model)
        query = self.query_options(query)

        result = await session.execute(query)
        return result.unique().scalars().all()

    async def get_by_group(self, group_id) -> list[SwipeSession]:
        """Retrieve all swipe sessions for a group.

        Args:
            group_id (int): ID of the group.

        Returns:
            list[SwipeSession]: List of `SwipeSession` instances for the group with their `swipes`.
        """
        query = (
            select(self.model)
            .where(self.model.group_id == group_id)
        )
        query = self.query_options(query)
        result = await session.execute(query)
        return result.scalars().unique().all()

    async def update_by_group_to_paused(self, group_id: int) -> None:
        """Update swipe sessions for a group to `PAUSED` status.

        Args:
            group_id (int): ID of the group.
        """
        query = (
            update(self.model)
            .where(
                and_(
                    SwipeSession.group_id == group_id,
                    SwipeSession.status == SwipeSessionEnum.IN_PROGRESS,
                )
            )
            .values(status=SwipeSessionEnum.PAUSED)
        )
        await session.execute(query)

    async def get_matches(self, session_id: int) -> int:
        """Retrieve recipe IDs that have a match for a swipe session.

        Args:
            session_id (int): ID of the swipe session.

        Returns:
            int: List of recipe IDs that have a match for the swipe session.
        """
        group_size = (
            select(func.count(distinct(GroupMember.user_id))) # pylint: disable=not-callable
            .where(GroupMember.group_id == Group.id)
            .where(SwipeSession.id == session_id)
            .select_from(join(GroupMember, Group).join(SwipeSession))
            .scalar_subquery()
        )
        query = (
            select(Swipe.recipe_id)
            .join(SwipeSession, Swipe.swipe_session_id == SwipeSession.id)
            .join(Group, Group.id == SwipeSession.group_id)
            .join(GroupMember, GroupMember.group_id == Group.id)
            .where(
                and_(
                    SwipeSession.id == session_id,
                    Swipe.like is True,
                )
            )
            .group_by(Swipe.recipe_id)
            .having(
                func.count(distinct(Swipe.user_id)) == group_size # noqa pylint: disable=not-callable
            )
        )
        result = await session.execute(query)
        return result.scalars().all()


import unittest
from pydantic import ValidationError
from bnotify.task import _find_bad_types, Task

class TestTask(unittest.TestCase):

    def test_task_order_right(self):
        def f(): pass
        token = "TOKEN STR"
        delay = 300

        task = Task(
            f,
            token,
            delay
        )
        self.assertEqual(task.function, f)
        self.assertEqual(task.token, token)
        self.assertEqual(task.delay, delay)

    def test_task_order_wrong(self):
        self.assertRaises(ValidationError, lambda: Task(
            "TOKEN STR",
            lambda a: a,
            300
        ))

    def test_task_less_args(self):
        self.assertRaises(TypeError, lambda: Task(
            lambda a: a,
            "TOKEN STR"
        ))

class TestTaskList(unittest.TestCase):

    def test_find_bad_types_empty(self):
        iterable = [tuple((1, 2, 3)), Task(lambda: 0, "TOKEN", 10)]

        bad_types = _find_bad_types(iterable)

        self.assertEqual(bad_types, [])

    def test_find_bad_types_filled(self):
        wrong_items = ['str', 1, 1.1, [1, 2, 3], {1, 2, 3}]
        wrong_types = [type(x) for x in wrong_items]
        iterable = [tuple((1, 2, 3)), Task(lambda: 0, "TOKEN", 10)] + wrong_items

        bad_types = _find_bad_types(iterable)

        self.assertEqual(bad_types, wrong_types)


import os

import yaml

from .params import Paths

class DetailsWriter:
    """Writer class for details."""

    @staticmethod
    def save_local_cluster_details(cluster_details: dict) -> None:
        os.makedirs(name=f"{Paths.ABS_MARO_LOCAL}/cluster", exist_ok=True)
        with open(file=f"{Paths.ABS_MARO_LOCAL}/cluster/cluster_details.yml", mode="w") as fw:
            yaml.safe_dump(data=cluster_details, stream=fw)

    @staticmethod
    def save_local_master_details(master_details: dict) -> None:
        os.makedirs(name=f"{Paths.ABS_MARO_LOCAL}/cluster", exist_ok=True)
        with open(file=f"{Paths.ABS_MARO_LOCAL}/cluster/master_details.yml", mode="w") as fw:
            yaml.safe_dump(data=master_details, stream=fw)


import sqlite3 as sql
from venv import create

if __name__ == '__main__':
    print('Dont run this file directly')
else:
    def createDB(name):
        conn = sql.connect(name)
        conn.commit()
        conn.close()

    def createTable(bd, name):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        cursor.execute(f"""CREATE TABLE '{name}' (
            name text,
            price integer
        )""")
        conn.commit()
        conn.close()

    def createTable2(bd, name):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        cursor.execute(f"""CREATE TABLE '{name}' (
            datatime text,
            mensaje text
        )""")
        conn.commit()
        conn.close()

    def anyIns(bd, table, name, price):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        consulta = f"INSERT INTO '{table}' VALUES ('{name}', {price})"
        cursor.execute(consulta)
        conn.commit()
        conn.close()

    def itemIns(bd, table, name, price):
        try:
            conn = sql.connect(bd)
            cursor = conn.cursor()
            consulta = f"INSERT INTO '{table}' VALUES ('{name}', {price})"
            cursor.execute(consulta)
            conn.commit()
            conn.close()
        except Exception as e:
            sendException('itenmIns func:', str(e))

    def itemS_all(bd, table):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        consulta = f"SELECT * FROM '{table}'"
        cursor.execute(consulta)
        datos = cursor.fetchall()
        conn.commit()
        conn.close()
        return datos

    def itemLasttwo(bd, table):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        consulta = """SELECT * from {table} LIMIT 2 ORDER BY datetime DESC LIMIT 2"""
        mensajes_list = []
        for row in cursor.execute(consulta):
            mensajes_list.append(row[2])

        if mensajes_list[0] == mensajes_list[1]:
            return
        else:
            pass

        conn.commit()
        conn.close()

    def itemUpdt(bd, table, name, price):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        consulta = f"UPDATE '{table} SET price='{price}' WHERE name='{name}')"
        cursor.execute(consulta)
        conn.commit()
        conn.close()

    def makeDB():
        createDB('items.db')
        createTable('items.db', 'items')

    def anyIns2(bd, table, mensajes):
        conn = sql.connect(bd)
        cursor = conn.cursor()
        consulta = f"INSERT INTO '{table}' VALUES ('{mensajes}')"
        cursor.execute(consulta)
        conn.commit()
        conn.close()


import imaplib
import email
import json
import DBInteraction
import email.utils
import time
from aiogram import Bot, Dispatcher, executor, types
import asyncio

Settings = ""

# Зчитуємо файл з налаштуваннями
with open('Settings.json', 'r') as f:
    textParam = f.read()
    Settings = json.loads(textParam)
    f.close()

bot = Bot(token=Settings["BOT_TOKEN"], parse_mode=types.ParseMode.HTML)

async def send_message(channel_id: int, text: str):
    await bot.send_message(channel_id, text)

async def bot_alert():

    users = DBInteraction.db_get_all_users(Settings)
    letters = DBInteraction.db_get_all_not_processed_letters(Settings)

    for letter in letters:
        #text = "Тема: " + letter[5] + "\n\n" + letter[1]
        text = "Тема: " + letter[5]
        text = text.replace('<mailto:support@kpd-uz.com>','')

        for user in users:

            if user[3] == True and letter[6] == False:
                await send_message(user[1], text)
            elif letter[6] == True:
                await send_message(user[1], text)

        DBInteraction.db_letter_processed(letter[0],Settings)

def mail_analysis():

    # Встановлюємо з'єднання з сервером пошти та отримуємо вхідні
    mail = imaplib.IMAP4_SSL(Settings["EMAIL_SERVER"])
    mail.login(Settings["EMAIL"], Settings["EMAIL_PASSWORD"])
    mail.select('inbox')

    # Отримуємо список ідентифікаторів повідомлень
    typ, data = mail.search(None, 'ALL')

    # Перебираємо кожне повідомлення за його ідентифікатором
    for num in data[0].split():

        typ, data = mail.fetch(num, '(RFC822)')
        raw_email = data[0][1]

        # Конвертуємо повідомлення в об'єкт email
        email_message = email.message_from_bytes(raw_email)

        '''
        print('Subject:', email_message['Subject'])
        print('From:', email_message['From'])
        print('Body:', email_message.get_payload())
        '''

        tmsoft_letter = False

        from_list = email_message['From'].split()
        for list_elem in from_list:
            elemFind = list_elem.find("@tmsoft.com.ua")
            if elemFind > 0:
                tmsoft_letter = True
                break

        sent_dateTime = email.utils.parsedate_to_datetime(email_message['Date'])
        sent_date = sent_dateTime.date()
        sent_time = sent_dateTime.time()

        a = 0

        text_letter = ""
        letter = ""
        header_letter = ""

        header_letter, encoding = email.header.decode_header(
            email_message['Subject'])[0]
        header_letter = header_letter.decode('utf-8')

        try:
            text_letter = str(email_message.get_payload()[0]._payload).encode('utf8', 'surrogateescape').decode('utf8')
        except (Exception,) as error:
            a = 1

        letter = {'date': sent_date,'time': sent_time,'header': header_letter, 'text': text_letter, 'tmsoft_letter':tmsoft_letter}

        # Записуємо в базу лист
        res = DBInteraction.db_interactionin_letter(2, letter, Settings)

        if res == None:
            a = 2

    # Відключаємо з'єднання з сервером пошти
    mail.close()
    mail.logout()

async def main():
    while True:
        mail_analysis()
        await bot_alert()
        time.sleep(600)

if __name__ == '__main__':
    asyncio.run(main())


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Wrap for custom logs
"""
# built-in
import collections
import datetime
import functools
import inspect
import json
import threading
import traceback
# pip3
import pytz

class JsonRepr:
    """
    Сan serialize itself
    """
    @classmethod
    def json(cls, obj, ready=dict()): #pylint: disable=W0102
        """
        Convert to json string with Circular reference control
        only for not native types
        """
        ido = id(obj)
        if ido in ready:
            result = ready[ido]
            ready.clear()
            ready[ido] = result
            return result

        ready[ido] = '__recursive__'
        result = str(obj)
        ready[ido] = result
        return result

    def __str__(self):
        return json.dumps(self.__dict__, default=self.json, ensure_ascii=False, sort_keys=True, indent=4)

class Log(JsonRepr):
    """
    Object of one log
    """
    def __init__(self, msg, log_level='log', logger=None, stack_lvl=1):
        self.msg = msg
        self.log_level = log_level
        self.logger = logger
        # list of name function from call stack
        self.stack = [lvl[3] for i, lvl in enumerate(inspect.stack()) if i > stack_lvl]
        self.exc = None
        self.start = datetime.datetime.now(pytz.utc)
        self.end = None

    def __enter__(self):
        return self

    def __exit__(self, _exctype, excinst, _exctb):
        self.end = datetime.datetime.now(pytz.utc)

        if excinst:
            self.exc = '{}\n{}'.format(excinst, traceback.format_exc())

        if isinstance(self.logger, Logger):
            self.logger.log(self, log_level='log')

class Logger(JsonRepr, threading.Thread):
    """
    Login Thread object for async log what and how you need
    """
    def __init__(self, log_func, log_init=None, log_levels=('log', 'warn', 'err'), log_level='log'):
        self.log_func = log_func
        self.log_init = log_init
        if log_init:
            self.log_init = log_init(self)
        self.log_levels = set()

        for lvl in log_levels:
            if not self.log_levels:
                if lvl == log_level:
                    self.log_levels.add(lvl)
                    continue
            self.log_levels.add(lvl)

        self.log_levels = log_levels
        self.active = True
        self.actions = collections.deque()
        self.act_rlock = threading.RLock()
        super().__init__()

    def run(self):
        while self.active:
            with self.act_rlock:
                act = self.actions.popleft() if self.actions else None
            if act:
                self.log_func(act)

    def join(self, timeout=None):
        """
        Waits until the last logs are processed and stops the thread object
        """
        self.active = False
        super().join(timeout)

    def log(self, msg, log_level='log'):
        """
        Add msg to log queue
        """
        if log_level not in self.log_levels:
            return

        if not isinstance(msg, Log):
            msg = Log(msg, log_level=log_level)
        else:
            msg.logger = None

        with self.act_rlock:
            self.actions.append(msg)

    def logging(self, func, label='', log_level='log', stack_lvl=1):
        """
        Wrapper for loging function call
        """
        @functools.wraps(func)
        def inner(*args, **kvargs):
            """
            Log wrapper
            """
            act = dict(input=(args, kvargs),
                       label=label,
                       output=None)
            with Log(act, log_level, self, stack_lvl+2):
                result = func(*args, **kvargs)
                act['output'] = result
        return inner


from collections import OrderedDict

class LRU_Cache(object):

    def __init__(self, capacity=None):
        # Initialize class variables
        self.cache_memory = OrderedDict()
        self.capacity = capacity

    def get(self, key):
        # Retrieve item from provided key. Return -1 if nonexistent.
        try:
            value = self.cache_memory.pop(key)
            self.cache_memory[key] = value
            return value
        except KeyError:
            return -1

    def set(self, key, value):
        # Set the value if the key is not present in the cache. If the cache is at capacity remove the oldest item.
        if self.capacity == 0 or self.capacity is None:
            print("Capacity of our cache is zero or null!")
            return
        if key in self.cache_memory:
            # poping to update the priority in dictionary
            self.cache_memory.pop(key)
            self.cache_memory[key] = value
            return
        # if the cache memory is not full, then just add it
        if len(self.cache_memory) < self.capacity:
            self.cache_memory[key] = value
        else:
            # if full, then remove the least recently used item
            self.cache_memory.popitem(last=False)
            self.cache_memory[key] = value

our_cache = LRU_Cache(5)

our_cache.set(1, 1)
our_cache.set(2, 2)
our_cache.set(3, 3)
our_cache.set(4, 4)

print(our_cache.get(1))       # returns 1
print(our_cache.get(2))      # returns 2
print(our_cache.get(9))    # returns -1 because 9 is not present in the cache

our_cache.set(5, 5)
our_cache.set(6, 6)

# returns -1 because the cache reached it's capacity and 3 was the least recently used entry
print(our_cache.get(3))

our_cache = LRU_Cache(4)
our_cache.set(1, 1)
our_cache.set(2, 2)
our_cache.set(3, 3)
our_cache.set(1, 4)
our_cache.set(4, 5)
our_cache.set(5, 6)
print(our_cache.get(1))  # should return 4
print(our_cache.get(2))  # should return -1 because it is least recently used

our_cache = LRU_Cache(0)
our_cache.set(1, 1)  # should print capacity of your cache is zero or null!
our_cache = LRU_Cache()
our_cache.set(1, 1)  # should print capacity of your cache is zero or null!


from django.urls import path

from . import views

urlpatterns = [

    path('2xx_success/', views.send_2xx, name='send_2xx'),
    path('4xx_not_found/', views.send_4xx, name='send_4xx'),
    path('5xx_exception/', views.send_5xx, name='send_5xx'),
]


from __future__ import absolute_import
from traits.api import Property, DelegatesTo, Instance, provides, CStr

# =============standard library imports ========================
# =============local library imports  ==========================
from pychron.config_loadable import ConfigLoadable
from pychron.config_mixin import ConfigMixin
from pychron.hardware.core.i_core_device import ICoreDevice
from pychron.has_communicator import HasCommunicator
from pychron.hardware.core.core_device import CoreDevice
from pychron.hardware.core.scanable_device import ScanableDevice

PACKAGES = dict(
    ProXRADC="pychron.hardware.ncd.adc",
    Eurotherm="pychron.hardware.eurotherm",
    NMGRLFurnaceFeeder="pychron.hardware.furnace.nmgrl.feeder",
    ThermoFurnaceFeeder="pychron.hardware.furnace.thermo.feeder",
    NMGRLFurnaceFunnel="pychron.hardware.furnace.nmgrl.funnel",
    NMGRLFurnaceEurotherm="pychron.hardware.furnace.nmgrl.eurotherm",
    MDriveMotor="pychron.hardware.mdrive",
    RPiGPIO="pychron.hardware.rpi_gpio",
)

@provides(ICoreDevice)
class AbstractDevice(ScanableDevice, ConfigLoadable, HasCommunicator):
    _cdevice = Instance(CoreDevice)
    communicator = DelegatesTo("_cdevice")

    dev_klass = Property(depends_on="_cdevice")
    graph = DelegatesTo("_cdevice")
    last_command = DelegatesTo("_cdevice")
    last_response = DelegatesTo("_cdevice")
    timestamp = DelegatesTo("_cdevice")
    current_scan_value = DelegatesTo("_cdevice")

    def load_additional_args(self, config):
        """ """
        cklass = self.config_get(config, "General", "type")

        factory = self.get_factory(PACKAGES[cklass], cklass)
        # self.debug('constructing cdevice: name={}, klass={}'.format(name, klass))
        self._cdevice = factory(
            name=cklass,
            application=self.application,
            configuration_dir_name=self.configuration_dir_name,
        )
        return True

    @property
    def com_device_name(self):
        return self._cdevice.__class__.__name__

    def get_factory(self, package, klass):
        try:
            module = __import__(package, fromlist=[klass])
            if hasattr(module, klass):
                factory = getattr(module, klass)
                return factory
        except ImportError as e:
            self.warning(e)

    def close(self):
        if self._cdevice:
            self._cdevice.close()

    def set(self, v):
        if self._cdevice:
            self._cdevice.set(v)

    def get(self, *args, **kw):
        if self._cdevice:
            return self._cdevice.get(*args, **kw)

    def post_initialize(self, *args, **kw):
        self.graph.set_y_title(self.graph_ytitle)

        # use our scan configuration not the cdevice's
        self.setup_scan()
        self.setup_alarms()
        self.setup_scheduler()

        if self.auto_start:
            self.start_scan()

    def initialize(self, *args, **kw):
        if self._cdevice:
            return self._cdevice.initialize(*args, **kw)

    def load(self, *args, **kw):
        if self._cdevice:
            if not self._check_cdevice():
                self.warning(
                    "Invalid device "
                    '"{}" for abstract device "{}"'.format(
                        self._cdevice.name, self.name
                    )
                )
                return

        config = self.get_configuration()
        if config:
            if self.load_additional_args(config):
                self._loaded = True
                self._cdevice.load()
                return True

    def open(self, *args, **kw):
        self.debug("open device")
        if self._cdevice:
            return self._cdevice.open(*args, **kw)

        return HasCommunicator.open(self, **kw)

    def __getattr__(self, attr):
        if hasattr(self._cdevice, attr):
            return getattr(self._cdevice, attr)

    def _get_dev_klass(self):
        return self._cdevice.__class__.__name__

    def _check_cdevice(self):
        return True

class AddressableAbstractDevice(AbstractDevice):
    address = CStr

    def load_additional_args(self, config):
        self.set_attribute(config, "address", "General", "address")
        return super(AddressableAbstractDevice, self).load_additional_args(config)

    def get(self, force=False, *args, **kw):
        if self._cdevice:
            return self._cdevice.read_channel(self.address, *args, **kw)

    def _check_cdevice(self):
        if self._cdevice:
            if hasattr(self._cdevice, "read_channel"):
                return True
        else:
            return True

# ============= EOF =====================================


import pickle
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def makecone():

    # Code for defining cone parameters and fitting parameters. The information from the text file is then
    # used in the main simulation code to define the cone.

    print("Here, you will define some of the setup parameters, including the shape of the receiver cone. \n")

    # Distance from the nozzle to the cone in Inches
    while True:
        try:
            conenozzdist = float(input("\nEnter the distance from the bottom of the nozzle to the top of the cone "
                                       "in inches: "))
            break
        except ValueError:
            print("ERROR: Your entry is invalid. Please try again. \n")

    while True:
        try:
            coneheight = float(input("\nEnter the cone height in inches, from the top of the ISO-100 cylinder"
                                     " to the top of the cone: "))
            break
        except ValueError:
            print("ERROR: Your entry is invalid. Please try again. \n")

    while True:
        try:
            iso160height = float(input("\nEnter the distance from the bottom of the nozzle to the top of the "
                                       "ISO-160 base in inches: "))
            break
        except ValueError:
            print("ERROR: Your entry is invalid. Please try again. \n")

    # Outer diameter of the top of the cone
    while True:
        try:
            conedia = float(input("\nEnter the outer diameter of the top of the cone in inches: "))
            break
        except ValueError:
            print("ERROR: Your entry is invalid. Please try again. \n")

    print("\nThe side of the cone is described by a polynomial (up to order 3) of the form: radius = a_i * y^i, "
          "\n where y and radius are in inches. The y-distance is defined as 0 at the top of the cone and"
          "\nincreases going down the cone. \n")

    hp = input("Confused? For a helpful image, enter H now. Otherwise, press ENTER to continue: ")

    if hp == "H" or hp == "h":
        plt.ion()
        img = mpimg.imread("./Jupyter_Pics/ConeDiagram.png")
        imgplot = plt.imshow(img)
        plt.show()
        plt.ioff()

        print("\n\n")
        print("The image demonstrates how the fitting should be done. As mentioned, \n"
              "you can use a polynomial up to order 3 here. \n\n")

    # set polyorder to some high value so that the user gets stuck in the while loop until they enter 3 or less.
    polyorder = 4
    while polyorder > 3 or polyorder < 1:
        try:
            polyorder = int(input("Please enter the order of the polynomial you'd like to use to "
                                  "describe the sides of the cone (max 3): "))
        except ValueError:
            print("ERROR: Your entry is invalid. Please try again. \n")

        if polyorder > 3:
            print("\nERROR: The maximum order of the polynomial is 3... ")
        if polyorder < 1:
            print("\nERROR: The minimum order of the polynomial is 1... ")

    # Convert conedia into a string and replace the . with a - to put it in the pickle file name
    conediastring = str(conedia)
    conediastring = conediastring.replace('.', '-')

    # Add all the input parameters to a new list called coneparms:
    coneparms = [conenozzdist, coneheight, iso160height, conedia, polyorder]

    # In the for loop, add 0's for the coefficients not being used and let the user
    # input coefficients for the ones that are.
    for i in range(4):
        expon = 3 - i

        if polyorder < expon:
            coneparms.append(0)
        else:
            while True:
                try:
                    coneparms.append(float(input("Enter the coefficient on the y^" + str(expon) +
                                                 " term (include the sign!): ")))
                    break
                except ValueError:
                    print("\n ERROR: The coefficient you entered is not valid.")

    geodir = "./Geometry_Files/"
    fname = "cust_cone_" + str(polyorder) + "_" + conediastring + "in.txt"

    file = open(geodir + fname, "w+")

    for i in coneparms:
        file.write(str(i) + "\n")

    print("A file named ", fname, " was saved to ", geodir)

    return fname

if __name__ == "__main__":

    makecone()


from ruxit.api.base_plugin import RemoteBasePlugin
from datetime import datetime
import logging
import socket

from dynatrace_api import DynatraceAPI

log = logging.getLogger(__name__)

class PortExtension(RemoteBasePlugin):
    def initialize(self, **kwargs):
        # The Dynatrace API client
        self.client = DynatraceAPI(self.config.get("api_url"), self.config.get("api_token"), log=log)
        self.executions = 0

    def query(self, **kwargs) -> None:

        log.setLevel(self.config.get("log_level"))

        name = self.config.get("test_name")
        target_ip = self.config.get("test_target_ip")
        target_ports = self.config.get("test_target_ports", "").split(",")
        location = self.config.get("test_location", "") if self.config.get("test_location") else "ActiveGate"
        frequency = int(self.config.get("frequency")) if self.config.get("frequency") else 1

        if self.executions % frequency == 0:
            for port in target_ports:
                if port:
                    success, response_time = test_port(target_ip, int(port))
                    test_name = f"{name} {target_ip}:{port}"
                    log.info(f"{target_ip}:{port} = {success}, {response_time}")

                    self.client.report_simple_test(
                        test_name, location, success, response_time, test_type="Port", interval=frequency * 60
                    )

                    if not success:
                        self.client.report_simple_event(
                            test_name,
                            f"Port check failed for {name}, target: {target_ip}:{port}",
                            location,
                            state="open",
                            test_type="Port",
                        )
                    else:
                        self.client.report_simple_event(
                            test_name,
                            f"Port check failed for {name}, target: {target_ip}:{port}",
                            location,
                            state="resolved",
                            test_type="Port",
                        )

        self.executions += 1

def test_port(ip: str, port: int) -> (bool, int):
    start = datetime.now()
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(2)
    result = sock.connect_ex((ip, port))
    sock.close()

    return result == 0, int((datetime.now() - start).total_seconds() * 1000)


import re
import time
from operator import attrgetter

from cached_property import cached_property
import digitalocean

# Represents the configuration options to create a server
class ServerConfig(object):
    pass

class ClusterManager(object):

    def best_node_match(self):
        raise NotImplementedError("")

    def launch_nodes(self):
        pass

    def create(self):
        raise NotImplementedError("")

    def list_nodes(self):
        raise NotImplementedError("")

    def shutdown_node(self):
        raise NotImplementedError("")

    def shutdown_all_nodes(self):
        raise NotImplementedError("")

    def list_regions(self):
        raise NotImplementedError("")

    def sync(self):
        """Sync the config state with cluster state"""
        raise NotImplementedError("")

class DigitalOceanNodeConfig(ServerConfig):

    def __init__(
        self, name, region, size, ssh_keys, image, backups=False, ipv6=False,
        private_networking=False
    ):
        self.name = name
        self.region = region
        self.size = size
        self.image = image
        self.ssh_keys = ssh_keys
        self.backups = backups
        self.ipv6 = ipv6
        self.private_networking = private_networking

    def as_kwargs_dict(self):
        kwargs = {}
        if self.name is not None:
            kwargs['name'] = self.name
        if self.region is not None:
            kwargs['region'] = self.region
        if self.size is not None:
            kwargs['size'] = self.size
        if self.image is not None:
            kwargs['image'] = self.image
        if self.ssh_keys is not None:
            kwargs['ssh_keys'] = self.ssh_keys
        if self.backups is not None:
            kwargs['backups'] = self.backups
        if self.ipv6 is not None:
            kwargs['ipv6'] = self.ipv6
        if self.private_networking is not None:
            kwargs['private_networking'] = self.private_networking

        return kwargs

class ClusterSyncException(Exception):
    pass

class DigitalOceanClusterManager(ClusterManager):

    def __init__(self, config):
        self.token = config['providers']['digitalocean']['token']
        self.manager = digitalocean.Manager(token=self.token)
        self.config = config

    @property
    def provider(self):
        return 'digitalocean'

    def parse_config(self):
        pass

    def default_region(self):
        return 'sfo1'

    def parse_node_size_config(self, cfg):
        return {
            'min_cores': cfg.get('min_cores', 1),
            'min_ram': cfg.get('min_ram', 256),
            'min_disk': cfg.get('min_disk', 10),
            'max_monthly_cost': cfg.get('max_monthly_cost', 20),
        }

    def size_for_slug(self, slug):
        return next(
            (
                size for size in self.sizes_list
                if size.slug == slug
            ),
            None,
        )

    def size_meets_requirements(self, size, min_cores, min_ram, min_disk, max_monthly_cost):
        return all((
            size.vcpus >= min_cores,
            size.memory >= min_ram,
            size.disk >= min_disk,
            size.price_monthly <= max_monthly_cost,
        ))

    def best_node_size_match(self, min_cores, min_ram, min_disk, max_monthly_cost):
        """
        :param min_cores: int Fewest cores allowable for machine (aka vcpu) default = 1
        :param min_ram: int Least memory allowed (in MB) default = 512
        :param min_disk: int Least disk allowed (in GB) default = 20
        :param max_monthly_cost: int Highest monthly price allowed default = 5

        :return: digitalocean.Size.Size
        """
        all_matching = [
            size for size in self.sizes_list
            if self.size_meets_requirements(size, min_cores, min_ram, min_disk, max_monthly_cost)
        ]

        return next(iter(sorted(all_matching, key=attrgetter("price_monthly"))), None)

    def rename_node(self, node, new_name):
        node.rename(new_name)

    def launch_node(self, node_configuration):
        """
        :param size: digitalocean.Size.Size
        """
        print("Launching node {}".format(node_configuration.name))
        droplet = digitalocean.Droplet(
            token=self.token,
            **node_configuration.as_kwargs_dict()
        )

        droplet.create()
        return droplet

    def destroy_node(self, node):
        if node.status == 'new':
            print("Can't destroy node {} yet, it's currently being created".format(node.name))

        print("Destroying node id:{} name:{}".format(node.id, node.name))
        node.destroy()

    def destroy_nodes(self, nodes):
        for node in nodes:
            self.destroy_node(node)

    def shutdown(self, node):
        node.shutdown()

    def node_in_cluster(self, cluster):
        cluster_name_scheme = '{}\d+'.format(cluster)
        return [
            n for n in self.nodes_list
            if re.match(cluster_name_scheme, n.name)
        ]

    def node_index(self, cluster_name, node_name):
        cluster_name_scheme = '{}(\d+)'.format(cluster_name)
        return int(re.findall(cluster_name_scheme, node_name)[0])

    @cached_property
    def nodes_list(self):
        return self._nodes_list()

    def _nodes_list(self):
        return self.manager.get_all_droplets()

    @cached_property
    def regions_list(self):
        return self.manager.get_all_regions()

    @cached_property
    def sizes_list(self):
        return self.manager.get_all_sizes()

    @cached_property
    def images_list(self):
        return self.manager.get_all_images()

    def list_clusters(self, cluster_name):
        pass

    def destroy_cluster(self, cluster_name):
        nodes_in_cluster = self.node_in_cluster(cluster_name)

        if not nodes_in_cluster:
            print("No nodes found in cluster {}, taking no action.".format(cluster_name))
            return

        self.destroy_nodes(nodes_in_cluster)

    def cluster_info(self, cluster_name):
        nodes_in_cluster = self.node_in_cluster(cluster_name)

        if not nodes_in_cluster:
            print("No nodes found in cluster {}".format(cluster_name))
            return

        return [
            {
                'name': node.name,
                'public_ips': node.ip_address,
                'private_ips': node.private_ip_address,
                'status': node.status
            }
            for node in nodes_in_cluster
        ]

    def cluster_status(self, cluster_name):
        nodes = self._nodes_list()

        def filter_nodes(status):
            def filter_node(node):
                return (
                    node.status == status and
                    re.match('{}\d+'.format(cluster_name), node.name)
                )

            return filter_node

        return {
            'active': list(filter(filter_nodes('active'), nodes)),
            'new': list(filter(filter_nodes('new'), nodes)),
            'off': list(filter(filter_nodes('off'), nodes)),
            'archive': list(filter(filter_nodes('archive'), nodes)),
        }

    def node_names(self, cluster_name):
        return [c['name'] for c in self.cluster_info(cluster_name)]

    def name_for_node(self, cluster_name, idx):
        return '{}{}'.format(cluster_name, idx)

    def ip_for_node(self, node_name):
        return next(
            (
                node.ip_address
                for node in self.nodes_list
                if node.name == node_name
            ),
            None
        )

    def stop_cluster(self, cluster_name, cluster_config):
        nodes_in_cluster = self.node_in_cluster(cluster_name)
        for node in nodes_in_cluster:
            self.shutdown(node)

    def start_cluster(self, cluster_name, cluster_config):
        """Refactor this. Gross"""
        print('Syncing cluster: {}'.format(cluster_name))
        nodes_in_cluster = self.node_in_cluster(cluster_name)
        node_size_config = self.parse_node_size_config(cluster_config)
        node_size = self.best_node_size_match(**node_size_config)
        desired_count = cluster_config['server_count']

        if any(node.status == 'new' for node in nodes_in_cluster):
            print(
                'Can\'t modify cluster {} right now, '
                'nodes currently spawning'.format(cluster_name)
            )

        if not node_size:
            raise ClusterSyncException(
                (
                    'No valid node was found matching cores: {min_cores}, '
                    'ram: {min_ram}mb, disk: {min_disk}gb, cost: ${max_monthly_cost}'
                ).format(**node_size_config)
            )

        def destroy_nonmatching_node(node):
            meets_size_requirements = self.size_meets_requirements(
                self.size_for_slug(node.size_slug), **node_size_config
            )
            if not meets_size_requirements:
                print(
                    'Destroying node {} - doesn\'t meet size requirement'.format(
                        node.name,
                    )
                )
                self.destroy_node(node)

            return meets_size_requirements

        # Remove nodes if they dont match size config & sort by name
        nodes_in_cluster = list(sorted(
            filter(destroy_nonmatching_node, nodes_in_cluster),
            key=attrgetter('name'),
        ))

        if len(nodes_in_cluster) > desired_count:
            print('Destroying extraneous nodes')
            for node in nodes_in_cluster[desired_count:]:
                self.destroy_node(node)
            nodes_in_cluster == nodes_in_cluster[:desired_count]

        # Rename nodes if nonsequential
        for idx, node in enumerate(nodes_in_cluster, start=1):
            name_for_node = self.name_for_node(cluster_name, idx)
            if name_for_node != node.name:
                self.rename_node(node, name_for_node)

        for idx in range(len(nodes_in_cluster), desired_count):
            node_configuration = DigitalOceanNodeConfig(
                name=self.name_for_node(cluster_name, idx + 1),
                region=cluster_config.get('region', self.default_region()),
                size=self.best_node_size_match(**node_size_config).slug,
                image=cluster_config.get('image', None),
                ssh_keys=cluster_config.get('ssh_keys', None),
                backups=cluster_config.get('backups', False),
                ipv6=cluster_config.get('ipv6', False),
                private_networking=cluster_config.get('private_networking', False),
            )
            self.launch_node(node_configuration)

        print('Cluster %s is operational!' % cluster_name)

    def start(self, cluster_name):
        cluster_config = self.config['clusters'].get(cluster_name)
        if not cluster_config:
            print('Config not found for cluster %s ' % cluster_name)
        elif cluster_config['provider'] != self.provider:
            print('The provider for %s is not %s' % (cluster_name, self.provider))
        else:
            self.start_cluster(cluster_name, cluster_config)

    def stop(self, cluster_name):
        cluster_config = self.config['clusters'].get(cluster_name)
        if not cluster_config:
            print('Config not found for cluster %s ' % cluster_name)
        elif cluster_config['provider'] != self.provider:
            print('The provider for %s is not %s' % (cluster_name, self.provider))
        else:
            self.stop_cluster(cluster_name, cluster_config)

    def wait_for_ready(self, cluster_name):
        cluster_status = self.cluster_status(cluster_name)
        waited = False
        while(len(cluster_status['new']) > 0):
            waited = True
            print('Waiting for nodes to come online: {}'.format(
                [n.name for n in cluster_status['new']]
            ))
            time.sleep(10)
            cluster_status = self.cluster_status(cluster_name)

        if waited:
            print('Giving the cluster 15 seconds to cool down')
            time.sleep(15)

        if len(cluster_status['off']) or len(cluster_status['archive']):
            nodes = [
                n.name
                for n in cluster_status['off'] + cluster_status['archive']
            ]
            print(
                'WARNING: Some nodes are NOT online, and commands cannot ',
                'be run on them'
            )
            print('Offline nodes: {}'.format(nodes))

PROVIDER_TO_CLUSTER_MANAGER = {
    'digitalocean': DigitalOceanClusterManager
}

def cluster_manager_for_provider(provider):
    if provider not in PROVIDER_TO_CLUSTER_MANAGER:
        raise ValueError('provider must be one of {}'.format(PROVIDER_TO_CLUSTER_MANAGER.keys()))
    return PROVIDER_TO_CLUSTER_MANAGER[provider]

def manager_for_cluster(config, cluster_name):
    return cluster_manager_for_provider(
        config['clusters'][cluster_name]['provider']
    )(config)


from django.urls import path
from intro import views
from accounts import views as accounts_views

urlpatterns = [
    path('', views.intro, name='intro'),
    path('humanity/', views.humanity, name='humanity'),
    path('society/', views.society, name='society'),
    path('science/', views.science, name='science'),
    path('business/', views.business, name='business'),
    path('engine/', views.engine, name='engine'),
    path('elec/', views.elec, name='elec'),
    path('farm/', views.farm, name='farm'),
    path('teacher/', views.teacher, name='teacher'),
    path('life/', views.life, name='life'),
    path('vet/', views.vet, name='vet'),
    path('medicine/', views.medicine, name='medicine'),
    path('doctor/', views.doctor, name='doctor'),
    path('mixed/', views.mixed, name='mixed'),
]


"""
Main Flask application which runs the website.
"""

from flask import Flask, render_template, request, redirect
import os

app = Flask(__name__)
app.debug = True

# TODO: Switch between "block" and "none" as necessary.
status = {
    "apply": "none",  # Enables/disables rush applications.
    "spotlight": "block"  # Enables/disables spotlights.
}
internal_website_url = "https://sites.google.com/cornell.edu/thetatau/home"

@app.route('/')
def main_page():
    """
    Main page of website.
    """
    with open("data/portfolio/employers.txt", "r") as employers, \
            open("data/portfolio/schools.txt", "r") as schools, \
            open("data/portfolio/teams.txt", "r") as teams, \
            open("data/brothers/core.tsv", "r") as eboard, \
            open("data/brothers/semester.tsv", "r") as semesterly, \
            open("data/brothers/brothers.tsv", "r") as brothers, \
            open("data/spotlight/carousel.tsv", "r") as carousel:
        return render_template("index.html",
                               employers=employers.readlines(),
                               schools=schools.readlines(),
                               teams=teams.readlines(),
                               eboard=eboard.readlines()[1:],
                               semester=semesterly.readlines()[1:],
                               brothers=brothers.readlines()[1:],
                               carousel=carousel.readlines()[1:],
                               status=status
                               )

@app.route('/spotlight')
def spotlight_page():
    """
    Spotlight page. Takes in a unique 8-char ID and maps it to
    the corresponding spotlight article if it exists.

    If the ID is not found, then redirects to 404 page.
    """
    id = request.args.get('id')

    with open("data/spotlight/spotlight.tsv", "r") as spotlight, \
            open("data/brothers/core.tsv", "r") as eboard:
        article = _find_article(id, spotlight.readlines())
        if article == None:
            return render_template("404.html",
                                   eboard=eboard.readlines()[1:],
                                   path="this",
                                   status=status)
        else:
            return render_template("spotlight.html",
                                   eboard=eboard.readlines()[1:],
                                   spotlight=article,
                                   status=status)

@app.route('/internal')
def reroute_to_internal():
    """
    Reroutes to internal website. People outside of Theta Tau will be redirected
    to a 404 error page.
    """
    return redirect(internal_website_url, code=302)

@app.route('/<path:path>')
def page_not_found(path):
    """
    Page for nonexistent pages on website.
    """
    with open("data/brothers/core.tsv", "r") as eboard:
        return render_template("404.html",
                               eboard=eboard.readlines()[1:],
                               path=path,
                               status=status)

def _find_article(id, file):
    """
    Given contents of spotlight.tsv file, will find the row
    which corresponds to the given ID.
    """
    for line in file:
        line_list = line.split('\t')
        if line_list[0] == id:
            return line_list
    return None

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port)


import nflows
import pytorch_lightning as pl
import torch
import torch.distributions as distr
import torch.nn.functional as F
from nflows import distributions, flows, transforms
from nflows.nn.nets import ResidualNet

from cdi.overrides.autoregressive import \
    MaskedPiecewiseRationalQuadraticAutoregressiveTransform
from cdi.overrides.coupling import PiecewiseRationalQuadraticCouplingTransform
from cdi.util.arg_utils import parse_bool

"""
Adapted from https://github.com/bayesiains/nsf/
"""

def create_linear_transform(hparams):
    if hparams.linear_transform_type == 'permutation':
        return transforms.RandomPermutation(features=hparams.dim)
    elif hparams.linear_transform_type == 'lu':
        return transforms.CompositeTransform([
            transforms.RandomPermutation(features=hparams.dim),
            transforms.LULinear(hparams.dim, identity_init=True)
        ])
    elif hparams.linear_transform_type == 'svd':
        return transforms.CompositeTransform([
            transforms.RandomPermutation(features=hparams.dim),
            transforms.SVDLinear(hparams.dim, num_householder=10, identity_init=True)
        ])
    else:
        raise ValueError

def create_base_transform(hparams, i):
    if hparams.base_transform_type == 'affine-coupling':
        return transforms.AffineCouplingTransform(
            mask=nflows.utils.create_alternating_binary_mask(hparams.dim, even=(i % 2 == 0)),
            transform_net_create_fn=lambda in_features, out_features: ResidualNet(
                in_features=in_features,
                out_features=out_features,
                hidden_features=hparams.hidden_features,
                context_features=None,
                num_blocks=hparams.num_transform_blocks,
                activation=F.relu,
                dropout_probability=hparams.dropout_probability,
                use_batch_norm=hparams.use_batch_norm
            )
        )
    elif hparams.base_transform_type == 'rq-coupling':
        return PiecewiseRationalQuadraticCouplingTransform(
            mask=nflows.utils.create_alternating_binary_mask(hparams.dim, even=(i % 2 == 0)),
            transform_net_create_fn=lambda in_features, out_features: ResidualNet(
                in_features=in_features,
                out_features=out_features,
                hidden_features=hparams.hidden_features,
                context_features=None,
                num_blocks=hparams.num_transform_blocks,
                activation=F.relu,
                dropout_probability=hparams.dropout_probability,
                use_batch_norm=hparams.use_batch_norm
            ),
            num_bins=hparams.num_bins,
            tails='linear',
            tail_bound=hparams.tail_bound,
            apply_unconditional_transform=hparams.apply_unconditional_transform,
            check_discriminant=(hasattr(hparams, 'check_discriminant') and hparams.check_discriminant) or (not hasattr(hparams, 'check_discriminant'))
        )
    elif hparams.base_transform_type == 'affine-autoregressive':
        return transforms.MaskedAffineAutoregressiveTransform(
            features=hparams.dim,
            hidden_features=hparams.hidden_features,
            context_features=None,
            num_blocks=hparams.num_transform_blocks,
            use_residual_blocks=True,
            random_mask=False,
            activation=F.relu,
            dropout_probability=hparams.dropout_probability,
            use_batch_norm=hparams.use_batch_norm
        )
    elif hparams.base_transform_type == 'rq-autoregressive':
        return MaskedPiecewiseRationalQuadraticAutoregressiveTransform(
            features=hparams.dim,
            hidden_features=hparams.hidden_features,
            context_features=None,
            num_bins=hparams.num_bins,
            tails='linear',
            tail_bound=hparams.tail_bound,
            num_blocks=hparams.num_transform_blocks,
            use_residual_blocks=True,
            random_mask=False,
            activation=F.relu,
            dropout_probability=hparams.dropout_probability,
            use_batch_norm=hparams.use_batch_norm,
            check_discriminant=(hasattr(hparams, 'check_discriminant') and hparams.check_discriminant) or (not hasattr(hparams, 'check_discriminant'))
        )
    else:
        raise ValueError

def create_transform(hparams):
    transform = transforms.CompositeTransform([
        transforms.CompositeTransform([
            create_linear_transform(hparams),
            create_base_transform(hparams, i)
        ]) for i in range(hparams.num_flow_steps)
    ] + [
        create_linear_transform(hparams)
    ])
    return transform

class StandardStudentT(nflows.distributions.Distribution):
    """A independent Student's-T with zero loc and unit scale, and one degree of freedom."""

    def __init__(self, shape):
        super().__init__()
        self._shape = torch.Size(shape)

        self.studentt = distr.StudentT(df=1, loc=0, scale=1)

    def _log_prob(self, inputs, context):
        # Note: the context is ignored.
        if inputs.shape[1:] != self._shape:
            raise ValueError(
                "Expected input of shape {}, got {}".format(
                    self._shape, inputs.shape[1:]
                )
            )
        log_probs = self.studentt.log_prob(inputs)
        return nflows.utils.torchutils.sum_except_batch(log_probs, num_batch_dims=1)

    def _sample(self, num_samples, context):
        if context is None:
            return self.studentt.sample(sample_shape=(num_samples, *self.shape_)).to(self.device)
        else:
            # The value of the context is ignored, only its size and device are taken into account.
            context_size = context.shape[0]
            return self.studentt.sample(sample_shape=(context_size, num_samples, *self._shape)).to(device=self.device)

class NormFlow(pl.LightningModule):
    def __init__(self, args):
        super(NormFlow, self).__init__()
        self.hparams = args.flow

        if not hasattr(self.hparams, 'base_distribution') or self.hparams.base_distribution == 'gaussian':
            distribution = distributions.StandardNormal((self.hparams.dim,))
        elif self.hparams.base_distribution == 'studentt':
            distribution = StandardStudentT((self.hparams.dim,))
        else:
            raise ValueError(f'base_distribution={self.hparams.base_distribution} is invalid.')

        transform = create_transform(self.hparams)
        self.flow = flows.Flow(transform, distribution)

        self.cum_batch_size_called = 0

    @staticmethod
    def add_model_args(parser):
        parser.add_argument('--flow.dim', type=int, required=True,
                            help='Dimensionality of the flow.')
        parser.add_argument('--flow.base_transform_type', type=str, default='rq-autoregressive',
                            choices=['affine-coupling', 'rq-coupling',
                                     'affine-autoregressive', 'rq-autoregressive'],
                            help='Type of transform to use between linear layers.')
        parser.add_argument('--flow.linear_transform_type', type=str, default='lu',
                            choices=['permutation', 'lu', 'svd'],
                            help='Type of linear transform to use.')
        parser.add_argument('--flow.num_flow_steps', type=int, default=10,
                            help='Number of blocks to use in flow.')
        parser.add_argument('--flow.hidden_features', type=int, default=256,
                            help='Number of hidden features to use in coupling/autoregressive nets.')
        parser.add_argument('--flow.tail_bound', type=float, default=3,
                            help='Box is on [-bound, bound]^2')
        parser.add_argument('--flow.num_bins', type=int, default=8,
                            help='Number of bins to use for piecewise transforms.')
        parser.add_argument('--flow.num_transform_blocks', type=int, default=2,
                            help='Number of blocks to use in coupling/autoregressive nets.')
        parser.add_argument('--flow.use_batch_norm', type=parse_bool, default=False,
                            help='Whether to use batch norm in coupling/autoregressive nets.')
        parser.add_argument('--flow.dropout_probability', type=float, default=0.25,
                            help='Dropout probability for coupling/autoregressive nets.')
        parser.add_argument('--flow.apply_unconditional_transform', type=parse_bool, default=True,
                            help='Whether to unconditionally transform \'identity\' '
                                 'features in coupling layer.')

        parser.add_argument('--flow.base_distribution', type=str,
                            choices=['gaussian', 'studentt'],
                            default='gaussian',
                            help='Base distribution type.')

        parser.add_argument('--flow.check_discriminant', type=parse_bool,
                            default=True, required=False,
                            help=('Whether to check the descriminant in rational-quadratic flow inverses. '
                                  'Disabling migh provide more stable MCMC sampling for PLMCMC.'))

        return parser

    def forward(self, X, M):
        # Track training calls
        if self.training:
            self.cum_batch_size_called += X.shape[0]

        return self.flow.log_prob(X)

    def log_prob(self, X):
        return self.flow.log_prob(X)

    def transform_to_noise_and_logabsdetJ(self, inputs, context=None):
        """Transforms given data into noise. Useful for goodness-of-fit checking.
        And gives the log-abs-determinant of the Jacobian of the transformation.

        Args:
            inputs: A `Tensor` of shape [batch_size, ...], the data to be transformed.
            context: A `Tensor` of shape [batch_size, ...] or None, optional context associated
                with the data.

        Returns:
            A `Tensor` of shape [batch_size, ...], the noise.
            A `Tensor` of shape [batch_size, ...], the log-absolute-determinant of the Jacobian of the transformation.
        """
        return self.flow._transform(inputs, context=self.flow._embedding_net(context))

    def transform_from_noise_and_logabsdetJ(self, inputs, context=None):
        """Transforms given noise into data. Useful for goodness-of-fit checking.
        And gives the log-abs-determinant of the Jacobian of the transformation.

        Args:
            inputs: A `Tensor` of shape [batch_size, ...], the data to be transformed.
            context: A `Tensor` of shape [batch_size, ...] or None, optional context associated
                with the data.

        Returns:
            A `Tensor` of shape [batch_size, ...], the samples.
            A `Tensor` of shape [batch_size, ...], the log-absolute-determinant of the Jacobian of the transformation.
        """
        return self.flow._transform.inverse(inputs, context=self.flow._embedding_net(context))

    def reset_parameters(self):
        # TODO
        pass

    def on_epoch_start(self):
        self.cum_batch_size_called = 0


import sys
from PIL import Image
import requests

# command line argument is manifest.csv
filename = sys.argv[1]

with open(filename) as f:
    lines = f.readlines()

for url in lines:
    url = url.strip()
    im = Image.open(requests.get(url, stream=True).raw)
    filename = url.split("/")[-1]
    im.save('resized/' + filename, "JPEG", dpi=(256,256))


"""
PLY tokenizer for the Snow language.

@author Rune Kaagaard
"""

import re
import tokenize
from ply import lex
from standard import *
from whitespace import *
from number import *
from brackets import *
from strings import *
from indentation import get_line_offsets, make_token_stream
from error import raise_syntax_error, raise_indentation_error
import sys
# Debugging, should go away.
from sys import exit as e
from pprint import pprint as p

# Add extra internal tokens to the tokens from snow_tokens.py.
tokens = tuple(tokens) + ("NEWLINE", "NUMBER", "NAME", "WS",
    "STRING_START_TRIPLE", "STRING_START_SINGLE", "STRING_CONTINUE",
    "STRING_END", "STRING", "INDENT", "DEDENT", "ENDMARKER")

# The different states the lexer can operate in. Token names in non-initial
# states are written as "t_[STATE]_TOKENNAME]".
states = (
    ("COMMENT", "exclusive"),
    ('INDOUBLEQUOTEDSTRING', 'exclusive'),
    ('INSINGLEQUOTEDSTRING', 'exclusive'),
    ('INTRIPPLEDOUBLEQUOTEDSTRING', 'exclusive'),
    ('INTRIPPLESINGLEQUOTEDSTRING', 'exclusive'),
    ('SNOWINANYDOUBLEQUOTEDSTRING', 'inclusive'),
)

class SnowLexer(object):
    """
    The Snow lexer class.

    Extends the default PLY lexer by adding rules for indentation and other
    whitespace stuff.
    """
    def __init__(self, lexer=None):
        if lexer is None:
            lexer = lex.lex().clone()
        self.lexer = lexer
        self._reset()
        self.lexer.filename = None
        self.token_stream = None

    def input(self, data, filename="<string>", add_endmarker=True):
        self._reset()
        self.lexer.input(data)
        self.lexer.filename = filename
        self.lexer.line_offsets = get_line_offsets(data)
        self.token_stream = make_token_stream(self.lexer, add_endmarker=True)

    def _reset(self):
        self.lexer.paren_count = 0
        self.lexer.is_raw = False
        self.lexer.string_been_concat = False

    def token(self):
        try:
            t = self.token_stream.next()
            return t
        except StopIteration:
            return None

    def __iter__(self):
        return self.token_stream


"""
DQN的tensorflow实现
This part of code is the DQN brain, which is a brain of the agent.
All decisions are made in here.
Using Tensorflow to build the neural network.
Tensorflow版本: 1.15
"""

import numpy as np
import pandas as pd
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

np.random.seed(1)
tf.set_random_seed(1)

# Deep Q Network off-policy
class DeepQNetwork:
    def __init__(
            self,
            n_actions,
            n_features,
            learning_rate=0.01,
            reward_decay=0.9,
            e_greedy=0.9,
            replace_target_iter=300,
            memory_size=500,
            batch_size=32,
            e_greedy_increment=None,
            output_graph=False,
    ):
        self.n_actions = n_actions
        self.n_features = n_features
        self.lr = learning_rate
        self.gamma = reward_decay
        self.epsilon_max = e_greedy
        self.replace_target_iter = replace_target_iter
        self.memory_size = memory_size
        self.batch_size = batch_size
        self.epsilon_increment = e_greedy_increment
        self.epsilon = 0 if e_greedy_increment is not None else self.epsilon_max

        # total learning step
        self.learn_step_counter = 0

        # initialize zero memory [s, a, r, s_]
        self.memory = np.zeros((self.memory_size, n_features * 2 + 2)) # 行数self.memory_size , 列数n_features * 2 + 2

        # consist of [target_net, evaluate_net]
        self._build_net()
        t_params = tf.get_collection('target_net_params')
        e_params = tf.get_collection('eval_net_params')
        self.replace_target_op = [tf.assign(t, e) for t, e in zip(t_params, e_params)]
        # 替换旧的网络参数w

        self.sess = tf.Session()

        if output_graph:
            # $ tensorboard --logdir=logs
            # tf.train.SummaryWriter soon be deprecated, use following
            tf.summary.FileWriter("logs/", self.sess.graph)

        self.sess.run(tf.global_variables_initializer())
        self.cost_his = []

    def _build_net(self):
        # ------------------ build evaluate_net ------------------
        self.s = tf.placeholder(tf.float32, [None, self.n_features], name='s')  # input
        self.q_target = tf.placeholder(tf.float32, [None, self.n_actions], name='Q_target')  # for calculating loss
        with tf.variable_scope('eval_net'):
            # c_names(collections_names) are the collections to store variables
            c_names, n_l1, w_initializer, b_initializer = \
                ['eval_net_params', tf.GraphKeys.GLOBAL_VARIABLES], 10, \
                tf.random_normal_initializer(0., 0.3), tf.constant_initializer(0.1)  # config of layers

            # first layer. collections is used later when assign to target net
            with tf.variable_scope('l1'):
                w1 = tf.get_variable('w1', [self.n_features, n_l1], initializer=w_initializer, collections=c_names)
                b1 = tf.get_variable('b1', [1, n_l1], initializer=b_initializer, collections=c_names)
                l1 = tf.nn.relu(tf.matmul(self.s, w1) + b1)

            # second layer. collections is used later when assign to target net
            with tf.variable_scope('l2'):
                w2 = tf.get_variable('w2', [n_l1, self.n_actions], initializer=w_initializer, collections=c_names)
                b2 = tf.get_variable('b2', [1, self.n_actions], initializer=b_initializer, collections=c_names)
                self.q_eval = tf.matmul(l1, w2) + b2

        with tf.variable_scope('loss'):
            self.loss = tf.reduce_mean(tf.squared_difference(self.q_target, self.q_eval))
        with tf.variable_scope('train'):
            self._train_op = tf.train.RMSPropOptimizer(self.lr).minimize(self.loss)     # 没有用Adam

        # ------------------ build target_net ------------------
        self.s_ = tf.placeholder(tf.float32, [None, self.n_features], name='s_')    # input
        with tf.variable_scope('target_net'):
            # c_names(collections_names) are the collections to store variables
            c_names = ['target_net_params', tf.GraphKeys.GLOBAL_VARIABLES]

            # first layer. collections is used later when assign to target net
            with tf.variable_scope('l1'):
                w1 = tf.get_variable('w1', [self.n_features, n_l1], initializer=w_initializer, collections=c_names)
                b1 = tf.get_variable('b1', [1, n_l1], initializer=b_initializer, collections=c_names)
                l1 = tf.nn.relu(tf.matmul(self.s_, w1) + b1)

            # second layer. collections is used later when assign to target net
            with tf.variable_scope('l2'):
                w2 = tf.get_variable('w2', [n_l1, self.n_actions], initializer=w_initializer, collections=c_names)
                b2 = tf.get_variable('b2', [1, self.n_actions], initializer=b_initializer, collections=c_names)
                self.q_next = tf.matmul(l1, w2) + b2

    def store_transition(self,s,a,r,s_):
        if not hasattr(self,'memory_counter'):
            self.memory_counter = 0

        transition = np.hstack((s,[a,r],s_)) # 横向压缩状态转移
        # 更新记忆库
        index = self.memory_counter % self.memory_size
        self.memory[index , : ] = transition
        # 每次更新一行
        self.memory_counter += 1

    def choose_action(self , observation):
        # 加一个维度变成矩阵才能输入tensorflow
        observation = observation[np.newaxis , : ]

        if np.random.uniform() < self.epsilon: # uniform默认在0到1之间随机取值
            # 贪心,向前探索一步,算出每个action的q
            actions_value = self.sess.run(self.q_eval , feed_dict={self.s : observation})
            # 备注:self.sess.run(self.out , feed_dict) feed_dict喂入网络,self.out输出
            # 等于这里是输入s,输出一行q(s,a),a∈A
            action = np.argmax(actions_value)
        else:
            action = np.random.randint(0 , self.n_actions) # 前闭后开
        return action

    def learn(self):
        # 检查是否需要替换掉原来老的w(用于q_target计算),新的w用于计算q_predict
        if self.learn_step_counter % self.replace_target_iter == 0:
            self.sess.run(self.replace_target_op) # op=old parameters
            print('target_parameters_replaced')

        ################ 随机抽样一批记忆库记忆 #############
        if self.memory_counter > self.memory_size: # 记忆库满了
            # 等于是0-499抽样32个数
            sample_index = np.random.choice(self.memory_size , size = self.batch_size)
        else: # 记忆库还没装满,有多少抽多少，由于是200步开始学习,记忆库有500个数据,所以是存在这种可能的
            sample_index = np.random.choice(self.memory_counter , size = self.batch_size)
        batch_memory = self.memory[sample_index , : ]

        q_next,q_eval = self.sess.run(
            [self.q_next , self.q_eval],
            feed_dict={
                self.s_ : batch_memory[ : , -self.n_features : ],   # 取了s_的特征值送入老w的神经网络,算q_next,再算q_target = r + max(q_next)
                self.s : batch_memory[ : ,  : self.n_features],     # 取了s的特征值送入新w的神经网络,算q_predict
            }
        )
        # 输出的q_next,q_eval都是32行n_actions列
        q_target = q_eval.copy() # 32行(batch_size),n_actions列,只改变被选动作的q
        batch_index = np.arange(self.batch_size , dtype = np.int32)
        eval_act_index = batch_memory[ : , self.n_features].astype(int)  # 抽出动作来
        reward = batch_memory[ : , self.n_features + 1]
        # 瘦高条子的矩阵运算(reward , np.max(q_next , axis = 1))
        q_target[batch_index , eval_act_index] = reward + self.gamma * np.max(q_next , axis = 1) # 行最大值
        '''
        举个例子：
        如果我一批抽样2个例子3个动作
        q_eval =
        [[1,2,3],
         [4,5,6]]
        先copy过来:q_target = q_eval =
        [[1,2,3],
         [4,5,6]]
        我只改变他抽样中执行的动作对应的q,例如:
        sample 0 中执行动作0,max q(s_) = -1 (为简化例子,reward为0,gamma=1)
        sample 1 中执行动作2,max q(s_) = -2
        那么q_target就变成了:
        [[-1,2,3],
         [4,5,-2]]
        delta = q_target - q_predict =
        [[-2,0,0],
         [0,0,-8]]
        delta传回去之后只改变选择的动作对应的q
        让人不禁感叹,这就是矩阵运算啊!!!!!!
        '''
        # 训练eval,BP网络
        _,self.cost = self.sess.run(
            [self._train_op , self.loss],
            feed_dict={self.s : batch_memory[ : , : self.n_features],
                       self.q_target : q_target}
        )
        # 输入s,q_target, s ==> q_eval , delta = q_target - q_predict 反向传播改变神经网络w
        self.cost_his.append(self.cost)
        # 增大epsilon,让策略收敛
        self.epsilon = self.epsilon + self.epsilon_increment if self.epsilon < self.epsilon_max else self.epsilon_max
        self.learn_step_counter += 1

    # 画图部分
    def plot_cost(self):
        import matplotlib.pyplot as plt
        plt.plot(np.arange(len(self.cost_his)) , self.cost_his)
        plt.ylabel('Cost')
        plt.xlabel('training steps')
        plt.show()


from pyspark.sql import SparkSession
from pyspark.sql.functions import from_json, to_json, col, unbase64, base64, split, expr
from pyspark.sql.types import StructField, StructType, StringType, BooleanType, ArrayType, DateType, FloatType

# TO-DO: create a kafka message schema StructType including the following JSON elements:
# {"accountNumber":"703934969","amount":415.94,"dateAndTime":"Sep 29, 2020, 10:06:23 AM"}

# References:
# https://spark.apache.org/docs/latest/sql-ref-datatypes.html
# https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.types.StructType.html
# https://spark.apache.org/docs/3.1.1/api/python/reference/api/pyspark.sql.types.StructField.html

schema = StructType([StructField("accountNumber", StringType()),
                     StructField("amount", FloatType()),
                     StructField("dateAndTime", StringType())
                    ])

# TO-DO: create a spark session, with an appropriately named application name
# TO-DO: set the log level to WARN

spark_session = SparkSession.builder.appName("atm_visits").getOrCreate()
spark_session.sparkContext.setLogLevel("WARN")

#TO-DO: read the atm-visits kafka topic as a source into a streaming dataframe with the bootstrap server localhost:9092, configuring the stream to read the earliest messages possible
#TO-DO: using a select expression on the streaming dataframe, cast the key and the value columns from kafka as strings, and then select them

raw_stream_df = spark_session\
   .readStream\
   .format("kafka")\
   .option("kafka.bootstrap.servers","localhost:9092")\
   .option("subscribe","bank-deposits")\
   .option("startingOffsets","earliest")\
   .load()

stream_df = raw_stream_df.selectExpr("CAST(key AS STRING) key", "CAST(value AS STRING) value")

#TO-DO: using the kafka message StructType, deserialize the JSON from the streaming dataframe

stream_df = stream_df.withColumn("value", from_json("value", schema))

# TO-DO: create a temporary streaming view called "BankDeposits"
# it can later be queried with spark.sql

# note: added .select(col("value.*")) to create individual columns based on the value column.

stream_df.select(col('value.*')).createOrReplaceTempView("BankDeposits")

#TO-DO: using spark.sql, select * from BankDeposits

new_stream_df = spark_session.sql("select * from BankDeposits")

# TO-DO: write the stream to the console, and configure it to run indefinitely, the console output will look something like this:
# +-------------+------+--------------------+
# |accountNumber|amount|         dateAndTime|
# +-------------+------+--------------------+
# |    103397629| 800.8|Oct 6, 2020 1:27:...|
# +-------------+------+--------------------+

new_stream_df.writeStream \
    .outputMode("append") \
    .format("console") \
    .start() \
    .awaitTermination()

# to run this application
# /home/workspace/spark/bin/spark-submit --packages org.apache.spark:spark-sql-kafka-0-10_2.11:2.4.6 /home/workspace/bank-deposits.py | tee /home/workspace/spark/logs/bank-deposits.log

# to check input topic
# kafka-console-consumer --topic bank-deposits --bootstrap-server localhost:9092 --from-beginning


from datetime import datetime, timedelta

from jose import jwt

from server.utils.setting import Setting

setting = Setting()

def create_access_token(data: dict, expires_delta: timedelta = timedelta(minutes=setting.ACCESS_TOKEN_EXPIRE_MINUTES)):
    to_encode = data.copy()
    expire = datetime.utcnow() + expires_delta
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, setting.SECRET_KEY, algorithm=setting.HASH_ALGORITHM)
    return encoded_jwt


import insightconnect_plugin_runtime
from .schema import CreateIocThreatInput, CreateIocThreatOutput, Input, Output, Component

# Custom imports below
from komand_sentinelone.util.helper import clean

class CreateIocThreat(insightconnect_plugin_runtime.Action):
    def __init__(self):
        super(self.__class__, self).__init__(
            name="create_ioc_threat",
            description=Component.DESCRIPTION,
            input=CreateIocThreatInput(),
            output=CreateIocThreatOutput(),
        )

    def run(self, params={}):
        return {
            Output.AFFECTED: self.connection.client.create_ioc_threat(
                clean(
                    {
                        "data": [
                            {
                                "hash": params.get(Input.HASH),
                                "groupId": params.get(Input.GROUPID),
                                "path": params.get(Input.PATH),
                                "agentId": params.get(Input.AGENTID),
                                "note": params.get(Input.NOTE),
                            }
                        ]
                    }
                )
            )
            .get("data", {})
            .get("affected", 0)
        }


import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy as sp

# def density_plot(data, measure, ext):
#     plot = sns.distplot(data[measure])
#     plot.figure.savefig(f"{OUT_DIR}{ext}.png")
#     plt.clf()

def violin_plot(data, ext):
    plot = sns.violinplot(data = data, x = "Measure", y  = "Value")
    plt.tight_layout()
    plot.figure.savefig(f"cent_dist_{ext}.pdf")
    plt.clf()

#set variables
OUT_DIR = ""
cent_file = "endpoints"
cols = ["degree", "betweenness", "closeness", "eigenvector"]
# load file
cent_data = pd.read_csv(cent_file, sep = "\t", header= None)
cent_data.columns = ['Node', 'Res'] + cols
cent_data = cent_data[cols]
#convert to long format
cent_data = pd.melt(cent_data, value_vars = cols, var_name = 'Measure', value_name = "Value")
# plot
violin_plot(cent_data, "random")

#plot original file
original_file = "../centrality/wt.txt"
original_data = pd.read_csv(original_file, sep = "\t")
original_data = original_data[cols]
original_long = pd.melt(original_data, value_vars = cols, var_name = 'Measure', value_name = "Value")
violin_plot(original_long, "original")

#combined
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10, 6), sharey=True)
ax1.set_title('Random network')
sns.violinplot(ax = ax1, data = cent_data, x = "Measure", y  = "Value")
ax2.set_title('Original network')
sns.violinplot(ax = ax2, data = original_long, x = "Measure", y  = "Value")
plt.tight_layout
plt.savefig("combined.pdf")

# for measure in cent_data.columns:
#     print(measure)
#     density_plot(cent_data, measure, "_one_edge_2")

# cypa_file = "../centrality/wt.txt"
# cypa_data = pd.read_csv(cypa_file, sep = "\t",)[cols]
# n = cypa_data.shape[0] + cent_data.shape[0]
# print("\n")
# for measure in cols:
#     print(measure)
#     stat, pval = sp.stats.kruskal(cypa_data[measure], cent_data[measure])
#     eta = (stat - 1)/(n - 2)
#     print("Statistic:", stat)
#     print("eta:", eta)
#     print("Pval:", pval)


from random import random
import random
import utils
from Scores import scores

# I am using the difficulty level which was set when the user selected the game, using the value
# which is 1 to 5 with the power of 10
def get_secret_number(difficulty):
    return random.randint(1, 10**difficulty)

# here we let the user guess the number - to be honest slim chance of success
def get_guess_from_user(difficulty):
    while True:
       print("Please select a number between 1 and" , 10**difficulty, ":")
       num = input()
       if utils.is_num_in_range(num ,1 , 10**difficulty):
           break

    return int(num)

# comparing the user guessed value with the random number
def compare_numbers(a , b):
    if a == b:
        return True
    return False

# The actual game
def guess_game(difficulty=1):
    while True:
        secret_number = get_secret_number(difficulty)
        player_guess=get_guess_from_user(difficulty)

        if compare_numbers(secret_number, player_guess):
            print("KUDOS")
            scores.add_scores(difficulty)
        else:
            print("You're a loser baby....")
        if utils.do_you_want_to_quit():
            break


"""
Simon Paquette
300044038
CSI 4108
Assignment 4

Before executing the python file, need to install the Sympy library (https://www.sympy.org/en/index.html):
    pip install sympy
    or
    conda install sympy
"""

import hmac
import math
import random
from hashlib import sha1, sha512

import sympy

#!##################################################################
#! Q1 - HMAC-SHA-512
#!##################################################################

def to_bytes(int_val: int) -> bytes:
    """
    Convert an integer to a bytes

    Args:
        int_val (int): an integer

    Returns:
        bytes: the integer in a bytes format
    """
    byteorder = "big"
    bytes_len = math.ceil(int_val.bit_length() / 8)
    bytes_val = int_val.to_bytes(bytes_len, byteorder)
    return bytes_val

def to_int(bytes_val: bytes) -> int:
    """
    Convert a bytes to an integer

    Args:
        bytes_val (bytes): a bytes

    Returns:
        int: the bytes in int format
    """
    byteorder = "big"
    int_val = int.from_bytes(bytes_val, byteorder)
    return int_val

def xor(bytes1: bytes, bytes2: bytes) -> bytes:
    """
    Bitwise XOR apply to a pair of bytes
    This built-in bitwise (^) operator is only useable for integer

    Args:
        bytes1 (bytes): bytes value 1
        bytes2 (bytes): bytes value 2

    Returns:
        bytes: XOR bytes value
    """
    int1, int2 = to_int(bytes1), to_int(bytes2)
    int_output = int1 ^ int2
    bytes_output = to_bytes(int_output)
    return bytes_output

def hmac_sha_512(k: bytes, m: bytes) -> str:
    """
    Implementation of HMAC using SHA-512

    Args:
        k (bytes): secret key
        m (bytes): message input

    Returns:
        str: the hexdigest of the message
    """
    # Define system parameters
    H = sha512
    block_size = 128
    ipad = b"\x36" * block_size
    opad = b"\x5C" * block_size

    # key padding
    k = k.ljust(block_size, b"\0")

    # Algo
    ipad_xor = xor(k, ipad)
    concat_msg = ipad_xor + m
    inner_hash = H(concat_msg)
    opad_xor = xor(k, opad)
    concat_all = opad_xor + inner_hash.digest()
    outer_hash = H(concat_all)

    return outer_hash.hexdigest()

# Define param
m = b"I am using this input string to test my own implementation of HMAC-SHA-512."
k = b"A new totally random, secure and secret key!"

# Implementation
lib_hmac = hmac.new(k, m, sha512).hexdigest()
my_hmac = hmac_sha_512(k, m)

# Show Result
print()
print("----------")
print("QUESTION 1")
print("----------")
print()
print("Message:", m)
print("Key:    ", k)
print()
print("Result from hmac library:")
print(lib_hmac)
print()
print("Result from my own implementation:")
print(my_hmac)
print()

#!##################################################################
#! Q2 - DSA
#!##################################################################

class DSA:
    prime_p_bit_length = 1024
    prime_q_bit_length = 160
    hash_fun = sha1

    def __init__(
        self,
        p: int = None,
        q: int = None,
        h: int = None,
        g: int = None,
        x: int = None,
        y: int = None,
    ):

        if p is None or q is None:
            self.set_primes()
        else:
            self.p = p
            self.q = q

        self.h = h

        if g is None:
            self.set_generator()

        if x is None:
            self.set_private_key()

        if y is None:
            self.set_public_key()

    def set_primes(self):

        # get a list of 160-bits prime to be checked for valid parameters
        q_list = sympy.primerange(
            2 ** (DSA.prime_q_bit_length - 1), 2**DSA.prime_q_bit_length
        )

        for q_tmp in q_list:

            # get a random p value of 1024 bits from a random q
            # HERE: get a value where q divide p-1
            # easier to do this way, instead of trying to find a q that divide p-1
            p_tmp = q_tmp * 2 ** (DSA.prime_p_bit_length - DSA.prime_q_bit_length) + 1

            # test that p is prime
            if not sympy.isprime(p_tmp):
                continue

            # HERE: test that q^2 does not divide p-1
            if (p_tmp - 1) % (q_tmp**2) == 0:
                continue

            # if the code run this, that means the previous condition were passed
            # and that we correctly identify prime p and prime q
            p = p_tmp
            q = q_tmp
            break

        self.p = p
        self.q = q

    def set_generator(self):

        if self.h is None:
            self.h = random.randint(2, self.p - 1)

        # set generator of the form: g = h^((p-1)/q) mod q
        g = -1
        while g <= 1:
            g = pow(base=self.h, exp=(self.p - 1) // self.q, mod=self.p)
        self.g = g

    def set_private_key(self):

        self.x = random.randint(2, self.q - 1)

    def set_public_key(self):

        # set public key as y = g^x mod p
        self.y = pow(base=self.g, exp=self.x, mod=self.p)

    def sign(self, message: int, k: int = None) -> tuple[int, int]:

        if k is not None:
            assert k > 1 and k < self.q - 1
        else:
            k = random.randint(2, self.q - 1)

        # calculate r = (g^k mod p) mod q
        r = pow(base=self.g, exp=k, mod=self.p) % self.q

        # calculate s = ([H(m) +xr] k^-1) mod q
        inner_hash = DSA.hash_fun(to_bytes(message)).digest()
        xr = self.x * r
        k_inv = pow(k, -1, self.q)
        concat = to_int(inner_hash) + (xr)
        mult = concat * k_inv
        s = mult % self.q

        # generate signature
        return (r, s)

    def verify(self, signature: tuple[int, int], message: int) -> bool:

        r, s = signature

        # algo
        s_inv = pow(s, -1, self.q)
        hash_m = to_int(DSA.hash_fun(to_bytes(message)).digest())
        u1 = (hash_m * s_inv) % self.q
        u2 = (r * s_inv) % self.q

        # validation step
        g_u1 = pow(self.g, u1, self.p)
        y_u2 = pow(self.y, u2, self.p)
        validation = ((g_u1 * y_u2) % self.p) % self.q

        return validation == r

# Define param
m = 522346828557612
k = 12345

# Implementation
dsa = DSA()
signature = dsa.sign(m, k)
verification = dsa.verify(signature, m)

# Show Result
print()
print("----------")
print("QUESTION 2")
print("----------")
print()
print("Bit lenght prime p =", dsa.p.bit_length())
print("Bit lenght prime q =", dsa.q.bit_length())
print()
print("Signature 1:", signature)
print("Valid signature:", verification)
print()

#!##################################################################
#! Q3 - DSA SECURITY
#!##################################################################

def find_k(m1: int, m2: int, s1: int, s2: int, q: int) -> int:
    """
    From 2 signatures that use the same unknown secret number k,
    extract the secret number k

    Args:
        m1 (int): first message
        m2 (int): seconde message
        s1 (int): s part of the first message's signature
        s2 (int): s part of the second message's signature
        q (int): public prime q

    Returns:
        int: found secret number k
    """
    hash_m1 = to_int(DSA.hash_fun(to_bytes(m1)).digest())
    hash_m2 = to_int(DSA.hash_fun(to_bytes(m2)).digest())
    found_k = ((hash_m1 - hash_m2) * (pow(s1 - s2, -1, q))) % q
    return found_k

def find_x(k: int, m: int, signature: tuple[int, int], q: int) -> int:
    """
    With a know secret number k, extract the private key

    Args:
        k (int): secret dsa number
        m (int): message
        signature (tuple[int, int]): message's signature (r,s)
        q (int): public prime q

    Returns:
        int: found private key x
    """
    r, s = signature
    hash_m = to_int(DSA.hash_fun(to_bytes(m)).digest())
    found_x = ((k * s - hash_m) * (pow(r, -1, q))) % q
    return found_x

# Define param
m2 = 8161474912883
signature_2 = dsa.sign(m2, k)

# Implementation
found_k = find_k(m, m2, signature[1], signature_2[1], dsa.q)
found_x = find_x(found_k, m2, signature_2, dsa.q)

# Show Result
print()
print("----------")
print("QUESTION 3")
print("----------")
print()
print("Signature 2:", signature_2)
print()
print("Is found k equal to the k used?", found_k == k)
print("Is found x equal to the x used?", found_x == dsa.x)
print()

#!##################################################################
#! Q4 - HASH FUNCTION & BLOCK CIPHER
#!##################################################################

# Answer in the pdf