"""empty message

Revision ID: 21ec9f49068d
Revises: f9ca502421ee
Create Date: 2018-01-05 16:28:42.017000

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql

# revision identifiers, used by Alembic.
revision = '21ec9f49068d'
down_revision = 'f9ca502421ee'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('posts',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('body', sa.Text(), nullable=True),
    sa.Column('timestamp', sa.DateTime(), nullable=True),
    sa.Column('auther_id', sa.Integer(), nullable=True),
    sa.Column('body_html', sa.Text(), nullable=True),
    sa.ForeignKeyConstraint(['auther_id'], ['users.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_posts_timestamp'), 'posts', ['timestamp'], unique=False)
    op.create_table('comments',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('body', sa.Text(), nullable=True),
    sa.Column('body_html', sa.Text(), nullable=True),
    sa.Column('timestamp', sa.DateTime(), nullable=True),
    sa.Column('author_id', sa.Integer(), nullable=True),
    sa.Column('post_id', sa.Integer(), nullable=True),
    sa.Column('disabled', sa.Boolean(), nullable=True),
    sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['post_id'], ['posts.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_comments_timestamp'), 'comments', ['timestamp'], unique=False)
    op.drop_table('post')
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('post',
    sa.Column('id', mysql.INTEGER(display_width=11), nullable=False),
    sa.Column('body', mysql.TEXT(), nullable=True),
    sa.Column('timestamp', mysql.DATETIME(), nullable=True),
    sa.Column('auther_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
    sa.Column('body_html', mysql.TEXT(), nullable=True),
    sa.ForeignKeyConstraint(['auther_id'], ['users.id'], name='post_ibfk_1'),
    sa.PrimaryKeyConstraint('id'),
    mysql_default_charset='utf8mb4',
    mysql_engine='InnoDB'
    )
    op.drop_index(op.f('ix_comments_timestamp'), table_name='comments')
    op.drop_table('comments')
    op.drop_index(op.f('ix_posts_timestamp'), table_name='posts')
    op.drop_table('posts')
    # ### end Alembic commands ###


import numpy as np
import matplotlib.pyplot as plt

def get_center(xmax, ymax, xmin, ymin, depth_path):
    """
    xmax: is the xmax of the detected region
    ymax: is the ymax of the detected region
    xmin: is hte xmin of the detected region
    ymin: is hte ymin of hte detected region
    depth_path is the location of the depth image and its demension should be w*h (eg. 256*256)
    """
    depthImage = plt.imread(depth_path)
    # because the depth image and rgb image are in the same coordination. The boudning box of the
    # rgb image should be in the same region of the depth image.
    depth_region = depthImage[xmin:xmax,ymin:ymax]
    print(depth_region)
    # find the mean of the depth region use as the representative depth
    d = depth_region.mean()

    # find the image coordination of the bounding box region.
    u = (xmax-xmin)/2
    v = (ymax-ymin)/2

    return d, u, v

d, u, v= get_center(140,160,105,100,'depth_0.png')
print(d)
print(u)
print(v)


import copy

import regex as re

with open("input-16.txt", "r") as file:
    lines = [line.strip() for line in file.readlines()]

# # Example input:
# lines = """Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
# Valve BB has flow rate=13; tunnels lead to valves CC, AA
# Valve CC has flow rate=2; tunnels lead to valves DD, BB
# Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
# Valve EE has flow rate=3; tunnels lead to valves FF, DD
# Valve FF has flow rate=0; tunnels lead to valves EE, GG
# Valve GG has flow rate=0; tunnels lead to valves FF, HH
# Valve HH has flow rate=22; tunnel leads to valve GG
# Valve II has flow rate=0; tunnels lead to valves AA, JJ
# Valve JJ has flow rate=21; tunnel leads to valve II
# """.splitlines()

# Example answer = 1651

class Valve:
    def __init__(self, name: str, flow_rate: str | int, tunnels: list[str]):
        self.name = name
        self.flow_rate = int(flow_rate)
        self.tunnels = tunnels
        self.open = False

def parse_line(line):
    match = re.search("Valve (\w+) has flow rate=(\d+); tunnel[s]? lead[s]? to valve[s]? (.+)", line)
    if match:
        name, rate, conn = match.groups()
        conn = conn.split(", ")
        return Valve(name, rate, conn)

def parse_input(lines):
    valves = {}
    for line in lines:
        valve = parse_line(line)
        if valve:
            valves[valve.name] = valve
    return valves

def calculate_distances(valves, start_point):
    dists = {}
    nexts = [(start_point, 0)]
    while nexts:
        (v, d) = nexts.pop(0)
        if dists.get(v) is not None:
            continue
        dists[v] = d
        for t in valves[v].tunnels:
            if dists.get(t) is not None or len(list(filter(lambda x: x[0] == t, nexts))):
                continue
            nexts.append((t, d + 1))
    return dists

def get_move_values(valves, pos, time_remaining):
    distances = calculate_distances(valves, pos)
    values = {v: valves[v].flow_rate * (time_remaining - distances[v] - 1)
              for v in valves
              if not valves[v].open and valves[v].flow_rate > 0}
    values = {k: v for (k, v) in values.items() if v > 0}
    return values

def find_max_release(valves, pos, time_remaining):
    if time_remaining <= 1:
        return 0
    distances = calculate_distances(valves, pos)
    values = get_move_values(valves, pos, time_remaining)
    max_release = 0
    for v in values:
        temp_valves = copy.deepcopy(valves)
        temp_valves[v].open = True
        max_release = max(max_release, values[v] + find_max_release(temp_valves, v, time_remaining - distances[v] - 1))
    return max_release

valves = parse_input(lines)

score = find_max_release(valves, "AA", 30)
print(score)


# Python packaging has layers upon layers of cruft (setuptools)
# on top of a very simple concept (zip files extracted into the
# site-packages directory). You can read [1,2,3] to get a sense
# of it.
#
# [1] https://blog.schuetze.link/2018/07/21/a-dive-into-packaging-native-python-extensions.html
# [2] https://packaging.python.org/specifications/core-metadata/
# [3] https://www.python.org/dev/peps/pep-0517/
#
# In short:
#
# 1. A source distribution (sdist) is a specially named .tar.gz
#    archive with a special file PKG-INFO; it can otherwise
#    contain anything.
#
#    Upon installation (by pip) this file will be extracted into
#    a temporary location, where it must be able to produce a
#    "built distribution".
#
# 2. A build distribution (bdist, aka a wheel) is a specially named
#    zip archive with an extension .whl and three special files:
#    * <package name>-<package version>.dist-info/WHEEL
#    * <package name>-<package version>.dist-info/METADATA
#    * <package name>-<package version>.dist-info/RECORD
#
#    Upon installation this whole archive will be extracted into
#    site-packages, and the RECORD file will be used to keep track
#    of which files belong to which package.
#
# Therefore, in this SConstruct file we must list every source
# for the sdist, and every binary needed in the bdist. Then,
# enscons will handle constructing the special files and the
# archives.

import enscons
import os
import packaging.tags
import subprocess
import toml

def get_universal_platform_tag():
    """Return the wheel tag for universal Python 3, but specific platform."""
    tag = next(packaging.tags.sys_tags())
    return f"py3-none-{tag.platform}"

def DirectoryFiles(dir):
    """Return a File() for each file in and under a directory."""
    files = []
    for root, dirnames, filenames in os.walk(dir):
        files += File(sorted(filenames), root)
    return files

pyproject = toml.load("pyproject.toml")

env = Environment(
    tools = ["default", "packaging", enscons.generate],
    PACKAGE_METADATA = pyproject["project"],
    WHEEL_TAG = get_universal_platform_tag(),
    ENV = os.environ,
    CONFIGUREDIR = os.path.join(os.environ.get("TMP", "/tmp"), ".sconf_temp"),
    CONFIGURELOG = "#/config.log"
)

contrib = SConscript("pySecDecContrib/SConscript", exports="env")
sdist_extra_source = File(Split("""
    COPYING
    CHANGELOG.md
    PKG-INFO
    README.md
    SConstruct
    pyproject.toml
"""))

if os.path.exists(".git"):
    git_id = subprocess.check_output(["git", "rev-parse", "HEAD"], encoding="utf8").strip()
    source = File(subprocess.check_output(["git", "ls-files", "pySecDec"], encoding="utf8").splitlines())
    generated_source = env.Textfile(target="pySecDec/metadata.py", source=[
        f'__version__ = "{pyproject["project"]["version"]}"',
        f'__authors__ = authors = "{pyproject["project"]["author"]}"',
        f'__commit__ = git_id = "{git_id}"'
    ])
    AlwaysBuild(generated_source)
else:
    # We are in a giless sdist. Lets hope that metadata.py was
    # included in it.
    source = DirectoryFiles("pySecDec")
    generated_source = []

platformlib = env.Whl("platlib", source + generated_source + contrib, root="")
bdist = env.WhlFile(source=platformlib)

# FindSourceFiles() will list every source file of every target
# defined so far.
sdist = env.SDist(source=FindSourceFiles() + generated_source)

env.Alias("sdist", sdist)
env.Alias("dist", sdist + bdist)
env.Alias("build", contrib + generated_source)

env.Default(contrib, sdist, bdist)


import sqlite3

# Function to create the SQLite database and table
def create_database_and_table():
    conn = sqlite3.connect("stephen_king_adaptations.db")
    cursor = conn.cursor()

    cursor.execute('''CREATE TABLE IF NOT EXISTS stephen_king_adaptations_table (
                      movieID TEXT PRIMARY KEY,
                      movieName TEXT,
                      movieYear INTEGER,
                      imdbRating REAL)''')

    conn.commit()
    conn.close()

# Function to read the file and copy content to a list
def read_file_and_copy_to_list(filename):
    with open(filename, 'r') as file:
        return file.readlines()

# Function to insert data from the list into the database
def insert_data_into_database(data_list):
    conn = sqlite3.connect("stephen_king_adaptations.db")
    cursor = conn.cursor()

    for line in data_list:
        movie_data = line.strip().split(',')
        if len(movie_data) == 4:  # Ensure the line has four elements
            cursor.execute("INSERT INTO stephen_king_adaptations_table (movieID, movieName, movieYear, imdbRating) VALUES (?, ?, ?, ?)",
                           (movie_data[0], movie_data[1], int(movie_data[2]), float(movie_data[3])))

    conn.commit()
    conn.close()

# Function to search for movies in the database based on user input
def search_movies_in_database():
    conn = sqlite3.connect("stephen_king_adaptations.db")
    cursor = conn.cursor()

    while True:
        print("\nOptions:")
        print("1. Search by Movie Name")
        print("2. Search by Movie Year")
        print("3. Search by Movie Rating")
        print("4. STOP")
        choice = input("Enter your choice: ")

        if choice == '1':
            movie_name = input("Enter the name of the movie: ")
            cursor.execute("SELECT * FROM stephen_king_adaptations_table WHERE movieName=?", (movie_name,))
            result = cursor.fetchone()
            if result:
                print("Movie ID:", result[0])
                print("Movie Name:", result[1])
                print("Movie Year:", result[2])
                print("IMDB Rating:", result[3])
            else:
                print("No such movie exists in our database.")
        elif choice == '2':
            year = input("Enter the year: ")
            cursor.execute("SELECT * FROM stephen_king_adaptations_table WHERE movieYear=?", (year,))
            results = cursor.fetchall()
            if results:
                for result in results:
                    print("Movie ID:", result[0])
                    print("Movie Name:", result[1])
                    print("Movie Year:", result[2])
                    print("IMDB Rating:", result[3])
            else:
                print("No movies were found for that year in our database.")
        elif choice == '3':
            rating = float(input("Enter the minimum rating: "))
            cursor.execute("SELECT * FROM stephen_king_adaptations_table WHERE imdbRating >= ?", (rating,))
            results = cursor.fetchall()
            if results:
                for result in results:
                    print("Movie ID:", result[0])
                    print("Movie Name:", result[1])
                    print("Movie Year:", result[2])
                    print("IMDB Rating:", result[3])
            else:
                print("No movies at or above that rating were found in the database.")
        elif choice == '4':
            break
        else:
            print("Invalid choice. Please enter a valid option.")

    conn.close()

if __name__ == "__main__":
    create_database_and_table()
    file_data = read_file_and_copy_to_list("stephen_king_adaptations.txt")
    insert_data_into_database(file_data)
    search_movies_in_database()


import sys
import collections
from tkinter import Button, Frame, Tk, messagebox
from tkinter.filedialog import askopenfilename
from patterns import get_word_pattern
from word_patterns import dictionary_patterns
from copy import deepcopy
from math import prod

class Menu(Tk):
    def __init__(self, title, buttons):
        # Final variables
        self.path = None
        self.encoded = Cryptogram()

        # CREATE MENU ITEMS

        # CONFIGURE MENU
        Tk.__init__(self)
        # Bind keys to functions
        self.bind('<Return>', self.onclick)

        # Set window title
        self.title(title)
        self.frame = Frame(self)

        # Create buttons
        self.buttons = buttons  # Button click history
        self.rows = []  # Array to store button locations
        index = 2
        for button in self.buttons:
            self.button = Button(
                self, text=button[1], command=lambda response=button[0]: self.onclick(response))
            self.button.grid(row=index, column=0, padx=50,
                             pady=5, sticky="W E")
            self.rows.append(self.button)
            index = index + 1

            # Disable buttons until file is selected
            if index-3 == 0:
                pass
            else:
                self.button['state'] = 'disabled'

        # Return to menu or close program
        self.protocol("WM_DELETE_WINDOW", self.on_closing)

    # Enable submission on selection of file
    def enable(self):
        # File has been selected
        if self.path != None:
            for button in self.rows:
                button['state'] = 'normal'
        else:  # File not selected yet
            self.button['state'] = 'disabled'

    def onclick(self, response):
        # User selected button with mouse
        if type(response) == int:
            pass
        # User selected button with keyboard
        else:
            index = 1
            for location in self.rows:
                if self.focus_get() == location:
                    response = index
                else:
                    pass
                index = index + 1

        choice = 'func_' + str(response)
        method = getattr(self, choice)
        return method()

    # Enable menu option opening and closing
    @staticmethod
    def hide(frame):
        frame.withdraw()

    @staticmethod
    def show(frame):
        frame.update()
        frame.deiconify()

    # File prompt
    def func_1(self):
        # Hide menu
        self.hide(self)

        # Create prompt for file
        self.path = file_prompt()

        # Check if user closed out of file prompt
        if self.path == '':
            self.path = None  # Reset path
            self.rows[0].config(text=self.buttons[0][1])  # Reset button text
        else:
            # Change button text to selected path
            self.rows[0].config(text=self.path)

        self.enable()
        self.show(self)

    # Begin decryption
    def func_2(self):
        # Hide menu
        self.hide(self)

        # Set path to cryptogram file and open
        self.encoded.file = self.path
        self.encoded.parse()
        self.enable()
        self.show(self)
        # TODO: Show decrypted text in new window
        # TODO: Only run decryption once to prevent additional parsing unless file path changes

    def on_closing(self):
        ans = messagebox.askokcancel(
            'Verify exit', "Do you really want to quit the program?")
        if ans:
            self.quit()
            sys.exit(3)
        else:
            pass

def file_prompt():
    Tk().withdraw()
    filename = askopenfilename(filetypes=[("Text files", "*.txt")])
    return filename

# Find common potential decryption values
def common_keys(cypher_one, cypher_two):
    common_cypher = Cypher()
    for letter in cypher_one:
        if cypher_one[letter] == []:  # First cypher is empty
            # Copy over everything from second cypher
            for value in cypher_two[letter]:
                common_cypher.cypher[letter].append(value)
        elif cypher_two[letter] == []:  # Second cypher is empty
            # Copy over everything from first cypher
            for value in cypher_one[letter]:
                common_cypher.cypher[letter].append(value)
        else:
            for value in cypher_one[letter]:  # First cypher has a value
                # Second cypher has the same value
                if value in cypher_two[letter]:
                    common_cypher.cypher[letter].append(value)
    return common_cypher

class Cypher():
    def __init__(self):
        self.cypher = {
            "A": [],
            "B": [],
            "C": [],
            "D": [],
            "E": [],
            "F": [],
            "G": [],
            "H": [],
            "I": [],
            "J": [],
            "K": [],
            "L": [],
            "M": [],
            "N": [],
            "O": [],
            "P": [],
            "Q": [],
            "R": [],
            "S": [],
            "T": [],
            "U": [],
            "V": [],
            "W": [],
            "X": [],
            "Y": [],
            "Z": []}

    # Match possible decryption values to encrypted values
    def add_cypher_keys(self, length, encrypted_value, decrypted_source):
        for decrypted_value in decrypted_source:
            for letter in range(0, length):
                # Make sure decrypted value is not already accounted for
                if decrypted_value[letter] not in self.cypher[encrypted_value[letter]]:
                    self.cypher[encrypted_value[letter]].append(decrypted_value[letter])

class Alphabet():
    def __init__(self):
        # Statistical frequency of letters in the English language
        self.stat_frequency = [
            ["A", 0.084966],
            ["B", 0.020720],
            ["C", 0.045388],
            ["D", 0.033844],
            ["E", 0.111607],
            ["F", 0.018121],
            ["G", 0.024705],
            ["H", 0.030034],
            ["I", 0.075448],
            ["J", 0.001965],
            ["K", 0.011016],
            ["L", 0.054893],
            ["M", 0.030129],
            ["N", 0.066544],
            ["O", 0.071635],
            ["P", 0.031671],
            ["Q", 0.001962],
            ["R", 0.075809],
            ["S", 0.057351],
            ["T", 0.069509],
            ["U", 0.036308],
            ["V", 0.010074],
            ["W", 0.012899],
            ["X", 0.002902],
            ["Y", 0.017779],
            ["Z", 0.002722]]

        # Actual frequency of letters in text
        self.frequency = [
            ["A", None],
            ["B", None],
            ["C", None],
            ["D", None],
            ["E", None],
            ["F", None],
            ["G", None],
            ["H", None],
            ["I", None],
            ["J", None],
            ["K", None],
            ["L", None],
            ["M", None],
            ["N", None],
            ["O", None],
            ["P", None],
            ["Q", None],
            ["R", None],
            ["S", None],
            ["T", None],
            ["U", None],
            ["V", None],
            ["W", None],
            ["X", None],
            ["Y", None],
            ["Z", None]]

class Cryptogram():
    def __init__(self):
        self.file = None
        self.encrypted = None
        self.words = []  # Access using [x][y], where x is the line number index and y is the word number index in that line
        self.letter_count = collections.Counter()  # Running count of letter appearances in encrypted text
        self.letters = Alphabet()
        self.word_patterns = {}
        self.cyphers = []
        self.final_cypher = Cypher()
        self.decrypted = None
        self.dict_length = 0

    def parse(self):
        # Parse encrypted file
        with open(self.file) as contents:
            self.encrypted = contents.readlines()

        # Strip whitespaces and standardize letters to same case
        for line in range(0, len(self.encrypted)):
            self.encrypted[line] = self.encrypted[line].strip().upper()

            # Split into words
            words = self.encrypted[line].split(' ')

            # Check words for alphabetic characters
            alphabetic = ''
            alphabetic_line = []
            for word in words:
                for letter in word:
                    if letter.isalpha():
                        alphabetic = ''.join((alphabetic, letter))
                alphabetic_line.append(alphabetic)
                alphabetic = ''
            self.words.append(alphabetic_line)
            self.count(self.encrypted[line])

        # Count the total number of alphabetic characters
        total = 0
        for letter in self.letter_count:
            if letter.isalpha():
                total = total + self.letter_count[letter]

        # Record the frequency of alphabetic characters
        for letter in self.letter_count:
            if letter.isalpha():
                for pair in self.letters.frequency:
                    if pair[0] == letter:
                        pair[1] = self.letter_count[letter]/total

        # Determine word patterns for words in encrypted text
        for line in self.words:
            for word in line:
                pattern = get_word_pattern(word)

                # Check for word pattern in pattern list
                if pattern in self.word_patterns:
                    # Add encrypted English word to matching pattern key
                    self.word_patterns[pattern].append(word)
                else:
                    # Create new pattern key and initialize value list with encrypted English word
                    self.word_patterns[pattern] = [word]

        # Compare word patterns in encrypted text to word patterns in English dictionary
        for pattern in self.word_patterns:
            # Map all potential decrypted values to corresponding encrypted values
            for encrypted_match in self.word_patterns[pattern]:
                cypher = Cypher()  # Create new cypher
                cypher.add_cypher_keys(len(pattern), encrypted_match, dictionary_patterns[pattern])
                self.cyphers.append(cypher)

        # Find common potential decrypted values in cyphers
        for count in range (0, len(self.cyphers)):
            self.final_cypher = common_keys(self.cyphers[count].cypher, self.final_cypher.cypher)

        # Simplify common decrypted values
        self.simplify_decryption()

        # Decrypt as much of message as possible
        self.decrypt()
        # TODO: Determine how many times to rerun decrypt function

    # Update letter count for file
    def count(self, word):
        self.letter_count.update(word)

        # TODO: Account for letter frequency

    # Remove any correctly decrypted letters from potential decryptions of other encrypted letters
    def simplify_decryption(self):
        # Get dictionary length
        self.dict_length = sum([len(val) for val in self.final_cypher.cypher.values()])

        solved = []
        rerun = []

        self.solved_letters(solved)

        for letter in self.final_cypher.cypher:
            if len(self.final_cypher.cypher[letter]) > 1:
                for value in solved:
                    # Remove already decrypted values from potential decrypted values
                    if value[1] in self.final_cypher.cypher[letter]:
                        self.final_cypher.cypher[letter].remove(value[1])

        # If removing already decrypted values leaves more letters with only one potential decrypted value, call function again
        self.solved_letters(rerun)
        if len(rerun) > len(solved):
            self.simplify_decryption()

    # Find letters with only one potential decryption for encrypted value, which must be correct
    def solved_letters(self, list):
        for letter in self.final_cypher.cypher:
            if len(self.final_cypher.cypher[letter]) == 1:
                list.append([letter, self.final_cypher.cypher[letter][0]])

    def decrypt(self):
        solved = []
        self.solved_letters(solved)
        self.decrypted = ''

        count = 0  # Track how many letters were replaced
        for line in range(0, len(self.encrypted)):
            for letter in range(0, len(self.encrypted[line])):
                flag = True
                for value in solved:
                    # If encrypted letter matches a solved letter, replace it with solution
                    if self.encrypted[line][letter] == value[0]:
                        self.decrypted = ''.join((self.decrypted, value[1]))
                        flag = False
                        count = count + 1
                        break
                if flag:
                    self.decrypted = ''.join(
                        (self.decrypted, self.encrypted[line][letter]))

        print("encrypted")
        print(self.encrypted)
        print("decrypted")
        print(self.decrypted)

        # THREE CASES:
        # Case one: self.decrypted is identical to self.encrypted[0], meaning no letters were able to be replaced
        # Case two: self.decrypted is not identical to self.encrypted[0], but count is not equal to len(self.encrypted[0]), meaning not all letters were replaced
        # Case three: self. decrypted is not identical to self.encrypted[0], and count is equal to len(self.encrypted[0]), meaning all letters were replaced
        if count != len(self.encrypted[0]):
            self.partially_solved()
            # self.find_key_words()
            self.rerun_check()

    # Takes partially solved words and searches the dictionary for matching patterns that specifically have the solved letters in those spots
    def partially_solved(self):
        for line in range(0, len(self.words)):
            for word in self.words[line]:
                index = 0
                correct_indices = []  # Holds solved letters and their position in the word
                for letter in word:
                    # Solved letter
                    # TODO: Incorporate solved_letters function
                    if len(self.final_cypher.cypher[letter]) == 1:
                        correct_indices.append((index, self.final_cypher.cypher[letter]))
                    index = index + 1

                # Only partially solved word
                if len(correct_indices) != len(word):
                    pattern = get_word_pattern(word)
                    wrong_matches = []
                    dictionary_matches = deepcopy(dictionary_patterns[pattern])

                    # Find matching dictionary patterns
                    for dictionary_match in dictionary_matches:
                        for correct_index in correct_indices:
                            if dictionary_match[correct_index[0]] != correct_index[1][0]:
                                wrong_matches.append(dictionary_match)
                                break

                    # Remove any dictionary matches that don't have the solved letters in the correct spots
                    for match in wrong_matches:
                        dictionary_matches.remove(match)

                    print(self.final_cypher.cypher)
                    cypher = Cypher()  # Create new cypher
                    cypher.add_cypher_keys(len(word), word, dictionary_matches)
                    self.final_cypher = common_keys(cypher.cypher, self.final_cypher.cypher)
                    self.simplify_decryption()
# TODO: Potentially remove all keys that are not solved, then search for dictionary matches with solved letters in correct spots and add corresponding new possibilities to unsolved letters

    # Determine if decryption should be rerun
    def rerun_check(self):
        # Dictionary length changed, decryption should be rerun
        if sum([len(val) for val in self.final_cypher.cypher.values()]) < self.dict_length:
            self.decrypt()
        else:
            self.letter_probability()

    # Identify potential key words and prefixes/suffixes
    def find_key_words(self):
        # Key three letter words: and, are, but, for, had, her, his, its, nor, she, the, was, yet
        # Key two letter words: am, an, as, at, be, by, do, go, he, if, in, is, it, me, my, no, of, on, or, so, to, up, us, we
        # Key one letter words: I, a
        # Key prefixes: bi-, co-, de-, dis-, ex-, in-, mis-, non-, post-, pre-, pro-, re-, sub-, un-
        # Key suffixes: -able, -acy, -al, -ate, -dom, -ed, -en, -er, -ful, -fy, -ing, -ion, -ish, -ist, -ive, -ize, -less, -ment, -ness, -or, -ship, -ty, -y
        # Key double letters, in order of frequency in the English language: ll, ss, ee, oo, tt, ff, pp, rr

        for line in range(0, len(self.words)):
            for word in self.words[line]:
                # One letter words
                if len(word) == 1:
                    # Not solved yet
                    if len(self.final_cypher.cypher[word]) != 1:
                        # If only one of the one letter words is present, it is automatically correct
                        if 'A' in self.final_cypher.cypher[word] and 'I' not in self.final_cypher.cypher[word]:
                            self.final_cypher.cypher[word] = 'A'
                        elif 'I' in self.final_cypher.cypher[word] and 'A' not in self.final_cypher.cypher[word]:
                            self.final_cypher.cypher[word] = 'I'
                        # Otherwise, add both one letter words as possibilities as needed
                        else:
                            if 'A' not in self.final_cypher.cypher[word]:
                                self.final_cypher.append('A')
                            if 'I' not in self.final_cypher.cypher[word]:
                                self.final_cypher.append('I')
                # Two letter words
                if len(word) == 2:
                    first_letter = word[0]
                    second_letter = word[1]
                    # Either letter not solved yet
                    if len(self.final_cypher.cypher[first_letter]) == 1:
                        if len(self.final_cypher.cypher[second_letter]) != 1:
                            pass

    def letter_probability(self):
        solved = []
        self.solved_letters(solved)
        decrypted = ''
        possible_decryptions = []

        incorrect_letters = Cypher()  # Access using [x], where x is the encrypted, unsolved letter. Returns indices containing x

        unsolved_letters = []  # List of unsolved letters and their possible decryptions
        for letter in self.final_cypher.cypher:
            if len(self.final_cypher.cypher[letter]) > 1:  # TODO: Should this be greater than 1 or != 1?
                unsolved_letters.append((letter, self.final_cypher.cypher[letter]))

        # TODO: Potentially incorporate into decryption function here
        wrong_letter = 0
        for line in range(0, len(self.encrypted)):  # TODO: Replace self.words with self.encrypted
            for letter in range(0, len(self.encrypted[line])):
                flag = True
                for value in solved:
                    # If encrypted letter matches a solved letter, replace it with solution
                    if self.encrypted[line][letter] == value[0]:
                        decrypted = ''.join((decrypted, value[1]))
                        flag = False
                        break
                if flag:
                    decrypted = ''.join((decrypted, self.encrypted[line][letter]))
                    if self.encrypted[line][letter].isalpha():
                        incorrect_letters.cypher[self.encrypted[line][letter]].append(wrong_letter)
                wrong_letter = wrong_letter + 1

        # Determine the number of possible decryptions using combinations of unsolved letters
        possibility_count = prod([len(val[1]) for val in unsolved_letters])
        for count in range(0, possibility_count):
            possible = list(deepcopy(decrypted))
            possible_decryptions.append(possible)

        # Determine how many decryptions each potential letter solution should be in
        for unsolved_letter in unsolved_letters:
            count = 0
            interval = int(len(possible_decryptions)/len(unsolved_letter[1]))
            for x in range(0, len(possible_decryptions), interval):
                for decryption in possible_decryptions[x:x+interval]:
                    for index in incorrect_letters.cypher[unsolved_letter[0]]:
                        decryption[index] = unsolved_letter[1][count]
                count = count + 1

        print("Possible decryptions:")
        for decryption in possible_decryptions:
            print(''.join(decryption))
#TODO: Account for partially solved separated words (self.words, not self.encrypted) and give user option to confirm based on word frequency

if __name__ == '__main__':
    menu = Menu(
        "Crypto-Solver!", ((1, "Choose an encrypted file."), (2, "Decrypt cryptogram.")))
    menu.mainloop()
    menu.destroy()


import json
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer

# Specify the path to your JSON file
cache_path = './cache.json'

def filter_items(search_term, items):
    search_tokens = list(set(search_term.lower().split()))
    filtered_items = []
    for item_id, item_data in items.items():
        item_tags = item_data["tags"]
        if any(any(token in tag for tag in item_tags) for token in search_tokens):
            filtered_items.append(item_data)
    return filtered_items

# def search_items(search_term, items):
#     stop_words = set(stopwords.words("english"))
#     tokens = [token for token in word_tokenize(search_term.lower()) if token not in stop_words]

#     best_match = None
#     best_weight = 0

#     for item_data in items:
#         tags = item_data["tags"]
#         weight = sum(1 for token in tokens if any(token in tag for tag in tags))
#         if weight > best_weight:
#             best_weight = weight
#             best_match = item_data

#     return best_match

def search_items(search_term, items):
    stop_words = set(stopwords.words("english"))
    tokens = [token for token in word_tokenize(search_term.lower()) if token not in stop_words]

    best_match = None
    best_weight = 0

    for item_data in items:
        tags = item_data["tags"]
        weight = 0

        # Check for exact matches in tags
        for token in tokens:
            if any(token == tag for tag in tags):
                weight += 2  # Assign higher weight for exact matches

        # Check for partial matches in tags
        for token in tokens:
            if any(token in tag for tag in tags):
                weight += 1  # Assign lower weight for partial matches

        if weight > best_weight:
            best_weight = weight
            best_match = item_data

    return best_match

def get_item_id_by_search(name):
    with open(cache_path) as json_file:
        data = json.load(json_file)
    items_full = data['__collections__']['items']
    search_term = name + ' ' + ''.join(name.split())
    lemmatizer = WordNetLemmatizer()
    search_tokens_singular = [lemmatizer.lemmatize(word.lower()) for word in search_term.lower().split()]
    search_term = search_term + " " + ' '.join(search_tokens_singular)
    filtered_items_list = filter_items(search_term,items_full)
    best_match = search_items(search_term,filtered_items_list)
    if not best_match:
        return "NA-"+name
    return best_match['itemId']

# res = search_item_by_name("butter milk")
# print(res)


import moderngl
import numpy as np
from PIL import Image
from pyrr import Matrix44
import io
import time
import os

USE_EGL=os.environ.get('USE_EGL', 'True') in ['True', 'true']
if(USE_EGL):
    # -------------------
    # CREATE CONTEXT HERE
    # -------------------
    ctx = moderngl.create_context(
        standalone=True,
        backend='egl',
        headless=True,

    )
else:
    ctx = moderngl.create_context(
        standalone=True,
        libgl='libGL.so.1',
        libx11='libX11.so.6',
        headless=True,
    )

prog = ctx.program(vertex_shader="""
    #version 330
    uniform mat4 model;
    in vec2 in_vert;
    in vec3 in_color;
    out vec3 color;
    void main() {
        gl_Position = model * vec4(in_vert, 0.0, 1.0);
        color = in_color;
    }
    """,
    fragment_shader="""
    #version 330
    in vec3 color;
    out vec4 fragColor;
    void main() {
        fragColor = vec4(color, 1.0);
    }
""")

vertices = np.array([
    -0.6, -0.6,
    1.0, 0.0, 0.0,
    0.6, -0.6,
    0.0, 1.0, 0.0,
    0.0, 0.6,
    0.0, 0.0, 1.0,
], dtype='f4')

def start(args, hkubeApi):
    try:
        input = args.get('input')[0]
        width=input.get('width',512)
        height=input.get('height',512)
        sleep=input.get('sleep',5)
    except Exception as e:
        width=512
        height=512
        sleep=5

    vbo = ctx.buffer(vertices)
    vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_color')
    fbo = ctx.framebuffer(color_attachments=[ctx.texture((width, height), 4)])

    fbo.use()
    ctx.clear()
    prog['model'].write(Matrix44.from_eulers((0.0, 0.1, 0.0), dtype='f4'))
    vao.render(moderngl.TRIANGLES)

    data = fbo.read(components=3)
    image = Image.frombytes('RGB', fbo.size, data)
    image = image.transpose(Image.FLIP_TOP_BOTTOM)
    in_mem_file = io.BytesIO()
    image.save(in_mem_file,format='JPEG')
    time.sleep(sleep)
    return {'buffer': in_mem_file.getvalue(), 'size': fbo.size, 'format': 'RGB'}

if __name__ == "__main__":
    with open("1.jpg","wb") as file:
        print('write')
        file.write(start({"input":[{'sleep':0}]}, None).get('buffer'))


from datetime import datetime
from environment.env import LOG_FILE_NAME

class LogController:
    """
    A controller class for handling operations related to logs.
    It provides methods to create a new log file, count lines in the log file,
    add a new log and read all logs.

    Attributes:
        LOG_FILE_NAME: Default log file name. Needs to be defined elsewhere in the code.
    """

    def __create_log_file(file_name: str = LOG_FILE_NAME):
        """
        Creates a new log file.

        Args:
            file_name (str): The name of the file to create. Defaults to LOG_FILE_NAME.

        Returns:
            None
        """
        with open(file_name, 'x') as file:
            file.write(f"1: Log File created at: {datetime.now()}")

    def __get_line_count(file_name: str = LOG_FILE_NAME):
        """
        Gets the line count of the log file. If the file doesn't exist, creates a new one.

        Args:
            file_name (str): The name of the file to get line count. Defaults to LOG_FILE_NAME.

        Returns:
            int: The number of lines in the log file.
        """
        try:
            with open(file_name, 'r') as file:
                lines = file.readlines()
            return len(lines) + 1
        except FileNotFoundError:
            LogController.__create_log_file(file_name)
            return LogController.__get_line_count(file_name)

    @staticmethod
    def new_log(msg: str, file_name: str = LOG_FILE_NAME):
        """
        Writes a new log entry to the log file.

        Args:
            msg (str): The message to be logged.
            file_name (str): The log file where the message will be logged. Defaults to LOG_FILE_NAME.

        Returns:
            None
        """
        line_number = LogController.__get_line_count(file_name)
        with open(file_name, 'a') as file:
            file.write(f"\n{line_number}: {msg} - at {datetime.now()}")

    @staticmethod
    def read_logs():
        """
        Reads all logs and prints the count of lines that contain the word "User".

        Returns:
            None
        """
        with open(LOG_FILE_NAME, 'r') as file:
            lines = file.readlines()
            print(len([l for l in lines if "User" in l]))


from pygments.lexer import RegexLexer, bygroups, include
from pygments.token import (Comment, Keyword, Name, Operator, Whitespace, Text)

__all__ = ['SCYLexer']

class SCYLexer(RegexLexer):
    name = 'Sequence of Covers'
    aliases = ['scy']
    filenames = ['*.scy']

    keyword_re = r'(cover|append|trace|add|disable|enable)'

    tokens = {
        'common': [
            (r'\s+', Whitespace),
            (r'#.*', Comment.Single),
            (r':', Operator),
            (r'(\S+)', Text),
        ],
        'root': [
            (keyword_re, Keyword, 'stmt'),
            include('common'),
        ],
        'stmt': [
            (r'(\S+)( )(.*)(:)?',
                bygroups(Name.Function, Whitespace, Text, Operator)),
            (r'(\S+)(:?)',
                bygroups(Text, Operator)),
            (r'(\n)', Whitespace, '#pop'),
            include('common'),
        ],
    }


#!/usr/bin/env python

"""
Usage: python symmetrize_for_uneven.py -i profile.in [-c weights.dat] [--anti] -o output

Purpose: Symmetrize given data (e.g., diffusivity, PMF) across x-value of zero.
         Takes into account weighted averages, if weights (counts of data at
         each grid point) are input.

Formula: For some profile D(x), weights N(x), and grid point xi:

             D_sym(xi) = [N(xi)*D(xi) + N(-xi)*D(-xi)]
                         -----------------------------
                                N(xi) + N(-xi)

By:         Victoria T. Lim
Version:    Oct 31 2018
Reference:  10.1021/ct400925s

Notes:
- DID NOT RETURN CORRECT MAGNITUDE OF VALUES ON EVEN LENGTH DATA ON
  MEMBRANE DENSITY PROFILES; VERIFY BEFORE USING
- this script was written to handle symmetrization when the RHS data
  was considerably longer than the LHS data
- colvar = collective variable, referring to x-value
- If your input data is the GRADIENT of the PMF, such as from adaptive biasing
  force, make sure to include the --anti flag. This changes the numerator of
  the above formula to [N(xi)*D(xi) - N(-xi)*D(-xi)], because you need to
  ANTIsymmetrize(grad(PMF)) in order to get symmetrized(PMF) after integration.

"""

import os, sys
import numpy as np
from scipy import integrate

def main(**kwargs):

    def initiate_indices(longest_side_length, z0, zero_present):
        """
        Define start and end range for looping through to (anti)symmetrize
        input data. The idea is to start from the profile's zero-value,
        and work out to the end of the profile. This function takes into
        account that the zero-value may not be at the beginning, middle,
        or end of the input data.

        Parameters
        ----------
        longest_side_length : int
            Number of items from zero-value to end of profile.
        z0 : int
            The index for the zero-value in the input data.
        zero_present : Bool
            Is the z0 value the actual center, or is it a skewed
            center? E.g. if input data has ..., -0.10, -0.05, 0.05, 0.10, ...
            True for actual zero value, False otherwise.

        Returns
        -------

        """
        sym_array = np.zeros([longest_side_length], np.float64)
        if zero_present:
            neg_i = z0-1
            pos_i = z0+1
            loopstart = 1
            loopend = longest_side_length
        else:
            neg_i = z0
            pos_i = z0+1
            loopstart = 0
            loopend = longest_side_length - 1
        return sym_array, neg_i, pos_i, loopstart, loopend

    # load data and check that they have the same colvar grid.
    file_grad  = np.loadtxt(args.infile)
    colvar_values = file_grad[:,0]
    length = len(colvar_values)
    grads =  file_grad[:,1]
    try:
        stds = file_grad[:,2]
        with_err = True
    except IndexError:
        with_err = False

    if args.cfile is not None:
        file_count = np.loadtxt(args.cfile)
        colvar_values1 = file_count[:,0]
        counts = file_count[:,1]
        if not np.array_equal(colvar_values,colvar_values1):
            sys.exit("ERROR: collective variable grid of .count and .grad files do not match")
    else:
        counts = np.ones(length)

    # find index where colvar changes sign (symmetrize around this point)
    # z0 is where colvar=0 if present, else will be the negative colvar closest to 0
    zero_present = False
    z0 = np.where(colvar_values <= 0)[0][-1]
    if colvar_values[z0] == 0:
        zero_present = True

    # get the length of data points on the longest side
    longest_side_length = max(z0, length-z0)
    longer = "right"
    if longest_side_length == z0:
        longer = "left"
    longer_by = longest_side_length-z0

    # initiate indices for symmetrization
    sym_grads, neg_i, pos_i, loopstart, loopend = initiate_indices(longest_side_length, z0, zero_present)
    if zero_present:
        sym_grads[0] = grads[z0]

    # loop from zero to max edge, symmetrizing
    for i in range(loopstart, loopend):

        # handle cases that can't be symmetrized: (1) colvar=0, (2) no data on one side
        # zero case can be combined with one of others but written out for clarity
        if neg_i < 0:
            sym_grads[i] = grads[pos_i]
        elif pos_i > len(colvar_values):
            sym_grads[i] = grads[neg_i]
        else:
            if args.anti:
                # based on eq. on p.558 of reference
                top_frac = counts[pos_i]*grads[pos_i] - counts[neg_i]*grads[neg_i]
            else:
                top_frac = counts[pos_i]*grads[pos_i] + counts[neg_i]*grads[neg_i]
            bot_frac = counts[pos_i] + counts[neg_i]
            sym_grads[i] = top_frac / bot_frac

        neg_i -= 1
        pos_i += 1

    # propagate error values if present
    ### TODO: this section is a bit redundant from the one above.
    if with_err:
        sym_stds, neg_i, pos_i, loopstart, loopend = initiate_indices(longest_side_length, z0, zero_present)
        if zero_present:
            sym_stds[0] = stds[z0]
        for i in range(loopstart, loopend):
            if neg_i < 0:
                sym_stds[i] = stds[pos_i]
            elif pos_i > len(colvar_values):
                sym_stds[i] = stds[neg_i]
            else:
                sym_stds[i] = np.sqrt(np.square(stds[pos_i]) + np.square(stds[neg_i]))
            neg_i -= 1
            pos_i += 1

    # extract part of colvar array from zero-value-cv out to longest edge
    if longer == "right":
        half_cvs = colvar_values[z0:] # 0 to 43.9
    else:
        half_cvs = colvar_values[:z0]  # untested

    # integrate the anti-symmetrized gradients
    if args.anti:

        # write out the counts-weighted and ANTI-symmetrized gradients.
        # gradients written out here bc the full gradient is never computed.
        # pmf is computed from what grad data is avail, then is symmetrized.
        gfile = os.path.splitext(args.outfile)[0]+'.grad'
        with open(gfile,'w') as f:
            f.write("# Input profile: {}\n# Input .count file: {}".format(args.infile, args.cfile))
            f.write("\n\n# Anti-symmetrized profile\n")
            np.savetxt(f, np.c_[half_cvs,sym_grads], delimiter='\t', fmt=['%.2f','%.6f'])

        if with_err:
            sys.exit("ERROR: error propagation not yet supported for gradient data")

        # trapezoid rule for integration
        int_pmf = integrate.cumtrapz(sym_grads, half_cvs)

        # take midpoint of all adjacent data points in half_cvs due to integration
        # https://tinyurl.com/ycahltpp
        half_cvs = (half_cvs[1:] + half_cvs[:-1]) / 2
        longest_side_length -= 1

    else:
        int_pmf = sym_grads

    # transform colvar unit from Angstroms to nanometers.
    cv_unit = "A"
#    cv_unit = "nm"
#    half_cvs = 0.1*half_cvs

    # generate the other half by mirroring
    full_cvs = np.zeros([2*longest_side_length], np.float64)
    full_pmf = np.zeros([2*longest_side_length], np.float64)
    full_std = np.zeros([2*longest_side_length], np.float64)
    full_length = len(full_cvs)

    for i in range(full_length):
        if not zero_present and i==longest_side_length-1:
            continue
        elif i < longest_side_length: # negative value colvars
            full_cvs[i] = -1*half_cvs[longest_side_length-1-i]
            full_pmf[i] = int_pmf[longest_side_length-1-i]
            if with_err:
                full_std[i] = sym_stds[longest_side_length-1-i]
        else:                         # positive value colvars
            full_cvs[i] = half_cvs[i-longest_side_length]
            full_pmf[i] = int_pmf[i-longest_side_length]
            if with_err:
                full_std[i] = sym_stds[i-longest_side_length]

    # if there is no zero-value colvars, remove the extraneous (-)0.0 value
    # TODO since [only tested for when RHS is longer]
    full_cvs = np.delete(full_cvs,longest_side_length-1)
    full_pmf = np.delete(full_pmf,longest_side_length-1)
    if with_err:
        full_std = np.delete(full_std,longest_side_length-1)
    full_length = len(full_cvs)

    # write out symmetrized profile
    with open(args.outfile,'w') as f:
        f.write("# Input profile: {}\n# Input .count file: {}".format(args.infile, args.cfile))
        f.write("\n# Number of data points: {}\n# Zero-based index of the zero-value colvar: {}".format(length, z0))
        f.write("\n# The {} hand side of the profile is longer by {} data points.".format(longer, longer_by))
        f.write("\n# Number of data points on longer edge: {}".format(longest_side_length))
        f.write("\n\n\n# Symmetrized profile (x units of {}, y units unchanged)\n".format(cv_unit))
        if not with_err:
            #f.write("\n\t{} {}".format(full_cvs[i], full_pmf[i]))
            np.savetxt(f, np.c_[full_cvs,full_pmf], delimiter='\t', fmt=['%.2f','%.15f'])
        else:
            #f.write("\n\t{} {} {}".format(full_cvs[i], full_pmf[i], full_std[i]))
            np.savetxt(f, np.c_[full_cvs,full_pmf,full_std], delimiter='\t', fmt=['%.2f','%.15f','%.15f'])

if __name__ == "__main__":

    import argparse
    parser = argparse.ArgumentParser()

    parser.add_argument("-i", "--infile", required=True,
                        help="Filename of data to be symmetrized.")

    parser.add_argument("-c", "--cfile",
                        help="Filename of ABF .count suffix with total number"
                             " of samples collected per grid point.")

    parser.add_argument("-a", "--anti", action="store_true", default=False,
                        help="Anti-symmetrize the input (gradient of pmf) then"
                             " integrate to get PMF.")

    parser.add_argument("-o", "--outfile", required=True,
                        help="Name of the output file.")

    args = parser.parse_args()
    opt = vars(args)
    if os.path.exists(args.outfile):
        sys.exit("ERROR: output file already exists")
    main(**opt)


from typing import List, Tuple, Any

# import keras
# import nltk
# import pandas as pd
import numpy as np
# import re
# import codecs
#
# from sklearn.decomposition import PCA, TruncatedSVD
# import matplotlib
# import matplotlib.patches as mpatches
# from keras.preprocessing.text import Tokenizer
# from keras.preprocessing.sequence import pad_sequences
# from keras.utils import to_categorical
# from nltk.tokenize import RegexpTokenizer

import matplotlib.pyplot as plt
# from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report

# import numpy as np
# import itertools
from sklearn.metrics import confusion_matrix

from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer

from sklearn.linear_model import LogisticRegression

from keras.layers import Dense, Input, Flatten, Dropout, concatenate
from keras.layers import Conv1D, MaxPooling1D, Embedding
# from keras.layers import LSTM, Bidirectional
from keras.models import Model

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.utils import to_categorical

from pandas import DataFrame

import gensim

from utils import read_file, plot_LSA, get_metrics, plot_confusion_matrix, get_most_important_features, plot_top_scores

input_file_name: str = "movie_review.csv"  # "movie_review.csv"#"socialmedia_relevant_cols.csv"
clean_file_name: str = "clean_" + input_file_name

target_column: str = "tag"  # "class_label"#"tag"

clean_questions: DataFrame = read_file(input_file_name, clean_file_name, target_column)
all_words: List[str] = [word for tokens in clean_questions["tokens"] for word in tokens]
sentence_lengths: List[int] = [len(tokens) for tokens in clean_questions["tokens"]]
VOCAB = sorted(list(set(all_words)))
print("%s words total, with a vocabulary size of %s" % (len(all_words), len(VOCAB)))
print("Max sentence length is %s" % max(sentence_lengths))

fig = plt.figure(figsize=(10, 10))
plt.xlabel('Sentence length')
plt.ylabel('Number of sentences')
plt.hist(sentence_lengths)
plt.show()

def cv(data: DataFrame) -> Tuple[Any, CountVectorizer]:
    _count_vectorizer = CountVectorizer()
    emb = _count_vectorizer.fit_transform(data)
    return emb, _count_vectorizer

list_corpus: List = clean_questions["text"].tolist()
list_labels: List = clean_questions[target_column].tolist()
X_train, X_test, y_train, y_test = train_test_split(list_corpus, list_labels, test_size=0.2, random_state=40)
X_train_counts, count_vectorizer = cv(X_train)

def process_train_result(train_counts, count_vecrorizer):
    X_test_counts = count_vectorizer.transform(X_test)

    fig = plt.figure(figsize=(16, 16))

    plot_LSA(X_train_counts, y_train)
    plt.show()

    clf = LogisticRegression(C=30.0, class_weight='balanced', solver='newton-cg', multi_class='multinomial', n_jobs=-1,
                             random_state=40)
    clf.fit(X_train_counts, y_train)
    y_predicted_counts = clf.predict(X_test_counts)

    accuracy, precision, recall, f1 = get_metrics(y_test, y_predicted_counts)
    print("accuracy = %.3f, precision = %.3f, recall = %.3f, f1 = %.3f" % (accuracy, precision, recall, f1))

    cm = confusion_matrix(y_test, y_predicted_counts)
    fig = plt.figure(figsize=(10, 10))
    plot = plot_confusion_matrix(cm, classes=['Irrelevant', 'Film', 'Unsure'], normalize=
    False, title='Confusion matrix')
    plt.show()
    return cm, clf

cm, clf = process_train_result(X_train_counts, count_vectorizer)
print(cm)

importance = get_most_important_features(count_vectorizer, clf, 10)

plot_top_scores(importance)

def tfidf(data):
    tfidf_vectorizer = TfidfVectorizer()
    train = tfidf_vectorizer.fit_transform(data)
    return train, tfidf_vectorizer

X_train_tfidf, tfidf_vectorizer = tfidf(X_train)
cm2, clf_tfidf = process_train_result(X_train_tfidf, tfidf_vectorizer)

# X_test_tfidf = tfidf_vectorizer.transform(X_test)
# fig = plt.figure(figsize=(16, 16))
# plot_LSA(X_train_tfidf, y_train)
# plt.show()
#
# clf_tfidf = LogisticRegression(C=30.0, class_weight='balanced', solver='newton-cg',
#                                multi_class='multinomial', n_jobs=-1, random_state=40)
# clf_tfidf.fit(X_train_tfidf, y_train)
# y_predicted_tfidf = clf_tfidf.predict(X_test_tfidf)
#
# accuracy_tfidf, precision_tfidf, recall_tfidf, f1_tfidf = get_metrics(y_test, y_predicted_tfidf)
# print("accuracy = %.3f, precision = %.3f, recall = %.3f, f1 = %.3f" % (
#     accuracy_tfidf, precision_tfidf, recall_tfidf, f1_tfidf))
#
# cm2 = confusion_matrix(y_test, y_predicted_tfidf)
# fig = plt.figure(figsize=(10, 10))
# plot = plot_confusion_matrix(cm2, classes=['Irrelevant', 'Film', 'Unsure'], normalize
# =False, title='Confusion matrix')
# plt.show()
print("TFIDF confusion matrix")
print(cm2)
print("BoW confusion matrix")
print(cm)

importance_tfidf = get_most_important_features(tfidf_vectorizer, clf_tfidf, 10)
plot_top_scores(importance_tfidf)
# top_scores = [a[0] for a in importance_tfidf[1]['tops']]
# top_words = [a[1] for a in importance_tfidf[1]['tops']]
# bottom_scores = [a[0] for a in importance_tfidf[1]['bottom']]
# bottom_words = [a[1] for a in importance_tfidf[1]['bottom']]
# plot_important_words(top_scores, top_words, bottom_scores, bottom_words, "Most important words for relevance")

word2vec_path = "GoogleNews-vectors-negative300.bin.gz"
word2vec = gensim.models.KeyedVectors.load_word2vec_format(word2vec_path, binary=True)

def get_average_word2vec(tokens_list, vector, generate_missing=False, k=300):
    if len(tokens_list) < 1:
        return np.zeros(k)
    if generate_missing:
        vectorized = [vector[word] if word in vector else np.random.rand(k) for word in tokens_list]
    else:
        vectorized = [vector[word] if word in vector else np.zeros(k) for word in tokens_list]

    length = len(vectorized)
    summed = np.sum(vectorized, axis=0)
    averaged = np.divide(summed, length)
    return averaged

def get_word2vec_embeddings(vectors, clean_questions, generate_missing=False):
    embeddings = clean_questions['tokens'].apply(
        lambda x: get_average_word2vec(x, vectors, generate_missing=generate_missing))
    return list(embeddings)

embeddings = get_word2vec_embeddings(word2vec, clean_questions)
X_train_word2vec, X_test_word2vec, y_train_word2vec, y_test_word2vec = train_test_split(embeddings, list_labels,
                                                                                test_size=0.2, random_state=40)

fig = plt.figure(figsize=(16, 16))
plot_LSA(embeddings, list_labels)
plt.show()

clf_w2v = LogisticRegression(C=30.0, class_weight='balanced', solver='newton-cg', multi_class='multinomial',
                             random_state=40)
clf_w2v.fit(X_train_word2vec, y_train_word2vec)
y_predicted_word2vec = clf_w2v.predict(X_test_word2vec)

clf_w2v = LogisticRegression(C=30.0, class_weight='balanced', solver='newton-cg', multi_class='multinomial',
                             random_state=40)
clf_w2v.fit(X_train_word2vec, y_train_word2vec)
y_predicted_word2vec = clf_w2v.predict(X_test_word2vec)

cm_w2v = confusion_matrix(y_test_word2vec, y_predicted_word2vec)
fig = plt.figure(figsize=(10, 10))
plot = plot_confusion_matrix(cm, classes=['Irrelevant', 'Film', 'Unsure'], normalize=False,
                             title='Confusion matrix')
plt.show()
print("Word2Vec confusion matrix")
print(cm_w2v)
print("TFIDF confusion matrix")
print(cm2)
print("BoW confusion matrix")
print(cm)

EMBEDDING_DIM = 300
MAX_SEQUENCE_LENGTH = 35
VOCAB_SIZE = len(VOCAB)
VALIDATION_SPLIT = .2
tokenizer = Tokenizer(num_words=VOCAB_SIZE)
tokenizer.fit_on_texts(clean_questions["text"].tolist())
sequences = tokenizer.texts_to_sequences(clean_questions["text"].tolist())
word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))
cnn_data = pad_sequences(sequences, maxlen=MAX_SEQUENCE_LENGTH)
labels = to_categorical(np.asarray(clean_questions[target_column]))
indices = np.arange(cnn_data.shape[0])
np.random.shuffle(indices)
cnn_data = cnn_data[indices]
labels = labels[indices]
num_validation_samples = int(VALIDATION_SPLIT * cnn_data.shape[0])
embedding_weights = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
for word, index in word_index.items():
    embedding_weights[index, :] = word2vec[word] if word in word2vec else np.random.rand(EMBEDDING_DIM)
print(embedding_weights.shape)

def ConvNet(embeddings, max_sequence_length, num_words, embedding_dim, labels_index, trainable=False, extra_conv=True):
    embedding_layer = Embedding(num_words, embedding_dim, weights=[embeddings], input_length=max_sequence_length,
                                trainable=trainable)
    sequence_input = Input(shape=(max_sequence_length,), dtype='int32')
    embedded_sequences = embedding_layer(sequence_input)
    # Yoon Kim model (https://arxiv.org/abs/1408.5882)
    convs = []
    filter_sizes = [3, 4, 5]
    for filter_size in filter_sizes:
        l_conv = Conv1D(filters=128, kernel_size=filter_size, activation='relu')(embedded_sequences)
        l_pool = MaxPooling1D(pool_size=3)(l_conv)
        convs.append(l_pool)

    l_merge = concatenate(convs, axis=1)
    # add a 1D convnet with global maxpooling, instead of Yoon Kim model
    conv = Conv1D(filters=128, kernel_size=3, activation='relu')(embedded_sequences)
    pool = MaxPooling1D(pool_size=3)(conv)
    if extra_conv == True:
        x = Dropout(0.5)(l_merge)
    else:
        # Original Yoon Kim model
        x = Dropout(0.5)(pool)
    x = Flatten()(x)
    x = Dense(128, activation='relu')(x)
    # x = Dropout(0.5)(x)
    preds = Dense(labels_index, activation='softmax')(x)
    model = Model(sequence_input, preds)
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
    return model

x_train = cnn_data[:-num_validation_samples]
y_train = labels[:-num_validation_samples]
x_val = cnn_data[-num_validation_samples:]
y_val = labels[-num_validation_samples:]
model = ConvNet(embedding_weights, MAX_SEQUENCE_LENGTH, len(word_index) + 1, EMBEDDING_DIM,
                len(list(clean_questions[target_column].unique())), False)

model.fit(x_train, y_train, validation_data=(x_val, y_val), epochs=3, batch_size=128)
print("end")


import discord
from discord.ext import commands

class Mod(commands.Cog):
    def __init__(self, client):
        self.client = client

    @commands.command()
    @commands.has_any_role("admin", "mod", "Owner")
    async def ban(self, ctx, member : discord.Member, *, reason = None):
        if ctx.message.author == member:
            await ctx.send(f"{ctx.message.author.mention}, you wanna ban yourself\nlmao")
        else:
            await member.ban(reason=reason)
            await ctx.send(f"Banned {member.mention}\nReason={reason}")

    @commands.command()
    @commands.has_any_role("admin","mod","Owner")
    async def unban(self, ctx, *, member):
        banned_user = await ctx.guild.bans()
        member_name, member_discriminator = member.split("#")

        for banned_entry in banned_user:
            user = banned_entry.user
            if (user.name, user.discriminator) == (member_name, member_discriminator):
                await ctx.guild.unban(user)
                await ctx.send(f"Unbanned {user.mention}")
                return
        await ctx.send(f"{member} was not found")

    @commands.command(aliases=["purge"])
    @commands.has_any_role("admin","mod","Owner")
    async def clear(self, ctx, amount=2):
        await ctx.channel.purge(limit=amount)

    @commands.command()
    @commands.has_any_role("admin","mod","Owner")
    async def kick(self, ctx, member : discord.Member, *, reason=None):
        if ctx.message.author == member:
            await ctx.send(f"{ctx.message.author.mention}, Why you wanna kick yourself?")

        else:
            await member.kick(reason=reason)
            await ctx.send(f"Kicked {member.mention}\nReason={reason}")

    @commands.command(aliases=['m'])
    @commands.has_any_role("admin","mod","Owner")
    async def mute(self, ctx, member : discord.Member):
        muted_role = discord.utils.get(ctx.guild.roles, name='Muted')
        if ctx.message.author == member:
            await ctx.send(f"{ctx.message.author.mention}, is this a joke?\nBecause it ain't funny")
        if muted_role in member.roles:
            await ctx.send(f"{member.mention} is already muted")
        else:
            await member.add_roles(muted_role)
            await ctx.send(f"{member.mention} has been muted by {ctx.author.mention}")

    @commands.command()
    @commands.has_any_role("admin","mod","Owner")
    async def unmute(self, ctx, member : discord.Member):
        # muted_role = ctx.guild.get_role(785417099131486239)
        muted_role = discord.utils.get(ctx.guild.roles, name='Muted')
        if ctx.message.author == member:
            await ctx.send(f"{member.mention}, you can't unmute yourself")
        if muted_role not in member.roles:
            await ctx.send(f"{member.mention} is not muted\n you need to mute him first")
        else:
            await member.remove_roles(muted_role)
            await ctx.send(f"{member.mention} has been unmuted by {ctx.author.mention}")

    @commands.command(aliases=["giverole"])
    @commands.has_any_role("admin", "mod", "Owner")
    async def addrole(self, ctx, role : discord.Role, *, member: discord.Member):
        if role not in member.roles:
            await member.add_roles(role)
            await ctx.send("Successfully added role")
        else:
            await ctx.send("Mentioned member already has that role")
    @commands.command()
    @commands.has_any_role('admin', 'mod', 'Owner')
    async def removerole(self, ctx, role : discord.Role, member : discord.Member):
        if role not in member.roles:
            await ctx.send(f"{member.mention} does not have that role\n try adding it with .addrole command")
        else:
            await member.remove_roles(role)
            await ctx.send(f"Successfully updated roles of {member.mention}")

def setup(client):
    client.add_cog(Mod(client))


import os
import torch.optim as optim
from engine import *

def main():
    directory = os.path.dirname("../mdkpmodels/localrl/")
    if not os.path.exists(directory):
        os.makedirs(directory)

    global_name = "RL-it200-dim20-w200-c0.00"
    # c = 0 --> Uncorrelated
    # c = 1 --> Correlated
    file_name = "LRL-it%d-dim%d-w%d-c%.2f" % (
        args.num_items, args.item_dim, args.w,  args.c
    )

    env = Env(
        args.item_dim, args.num_items, 1,
        args.w, args.c
        )

    if args.use_cuda:
        policy = Att_Policy(args.item_dim, args.embed_dim, args.use_cuda).to("cuda:0")
        bl = Att_Policy(args.item_dim, args.embed_dim, args.use_cuda).to("cuda:0")
    else:
        policy = Att_Policy(args.item_dim, args.embed_dim, args.use_cuda)
        bl = Att_Policy(args.item_dim, args.embed_dim, args.use_cuda)

    policy.load_finetune_checkpoint(
        "../mdkpmodels/globalrl/"+global_name+".torchmodel"
    )

    if args.load:
        with open("../mdkpmodels/localrl/" + file_name + ".torchmodel", "rb") as f:
            tmp = torch.load(f)
        policy.load_state_dict(tmp.state_dict())

    for name, param in policy.named_parameters():
        if not name.startswith("item"):
            param.requires_grad = False
    bl.load_state_dict(policy.state_dict())
    policy.train()

    optimizer = optim.Adam(policy.parameters(), lr=5e-4)
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.95)
    averaged_value = 0
    averaged_returns = 0.0
    averaged_loss = 0.0

    start = time.time()
    for epoch in range(args.epochs):
        optimizer.zero_grad()
        loss, value, reward = rollout(env, policy, guide=True)
        optimizer.step()
        scheduler.step()

        if epoch == 0:
            averaged_loss = loss
            averaged_value = value
            averaged_returns = reward

        else:
            averaged_returns = 0.95 * averaged_returns + 0.05 * reward
            averaged_value = 0.95 * averaged_value + 0.05 * value
            averaged_loss = 0.95 * averaged_loss + 0.05 * loss

        end = time.time()
        elapsed = (end - start)
        print("----------")
        print("EPOCH : {} / {}".format(epoch, args.epochs))
        print("CURRENT VALUE : {}".format(value))
        print("LOSS : {:.3f}, VALUE : {}, REWARD : {:.3f}".\
              format(averaged_loss, averaged_value, averaged_returns))
        # print(elapsed)
        minute = int(elapsed // 60)
        second = int(elapsed - 60 * minute)
        print("경과시간 : {}m{:}s".format(minute, second))

        torch.save(policy, "../mdkpmodels/localrl/" + file_name + ".torchmodel")

if __name__ == "__main__":
    main()


import requests
import sys
import json

def main():
    if len(sys.argv) != 2:
        sys.exit()

    number = 50

    response = requests.get(f"https://itunes.apple.com/search?entity=song&limit={number}&term={sys.argv[1]}")

#    print(json.dumps(response.json(), sort_keys=True, indent=4))

    o = response.json()

    for result in o["results"]:
        print(result["artistName"],result["trackName"], sep=" --- ")
main()


from pak.datasets.Dataset import Dataset
import numpy as np
import zipfile
import tarfile
import urllib.request
import shutil
from os import makedirs, listdir
from os.path import join, isfile, isdir, exists, splitext
from scipy.ndimage import imread
from scipy.misc import imresize
from scipy.io import loadmat
from skimage.transform import resize
from pak import utils
from pak.util import mpii_human_pose as mpii_hp
import h5py
from enum import Enum

class Hand_config(Enum):
    Default = 1
    Square = 2
    AABB = 3

class Hand(Dataset):
    """
    hand dataset (http://www.robots.ox.ac.uk/~vgg/data/hands/)
    """

    def __init__(self, root, verbose=True):
        """ create a hand dataset
        """
        Dataset.__init__(self, "hand_dataset", root, verbose)
        url = 'http://www.robots.ox.ac.uk/~vgg/data/hands/downloads/hand_dataset.tar.gz'
        #self.root_export = join(root, "lspe")
        self.download_and_unzip(url,
            zipfile_name='hand_dataset.tar.gz')
        self.root_export = join(root, "hand_dataset")

    def get_test(self, config=Hand_config.Default):
        return self.get_raw(join('test_dataset', 'test_data'), config)

    def get_train(self, config=Hand_config.Default):
        return self.get_raw(join('training_dataset', 'training_data'),\
            config)

    def get_val(self, config=Hand_config.Default):
        return self.get_raw(join('validation_dataset', 'validation_data'),\
            config)

    def get_raw(self, subfolder, config):
        """ test vs train vs validation

            make_square: if True a 4th point is added to enclose the
                hand
        """
        make_square = config is Hand_config.Square or config is Hand_config.AABB
        aabb = config is Hand_config.AABB

        path = join(self.root_export, subfolder)

        # # annotations
        path_anno = join(path, 'annotations')
        path_imgs = join(path, 'images')

        slates = sorted([splitext(f)[0] for f in listdir(path_imgs) if \
            isfile(join(path_anno, splitext(f)[0] + '.mat')) and \
            isfile(join(path_imgs, splitext(f)[0] + '.jpg'))])

        X = []
        Y = []

        for f in slates:
            img_file = join(path_imgs, f + '.jpg')
            ann_file = join(path_anno, f + '.mat')

            x = imread(img_file)
            X.append(x)

            # --- y ---
            M = loadmat(ann_file)['boxes'][0]
            nbr_boxes = len(M)
            Frame = []
            for i in range(nbr_boxes):
                single_hand = M[i][0][0]
                Hand = []
                for j in range(3):
                    e = single_hand[j][0]
                    Hand.append(np.array((e[1], e[0])))  # first X, then Y ...
                if make_square:
                    v1 = Hand[0]
                    v2 = Hand[1]
                    v3 = Hand[2]

                    # we want to determine {4} given (1),(2),(3)
                    # (1)--(w)--(2)
                    #  .         |
                    #  .        (h)
                    #  .         |
                    # {4}. . . .(3)
                    direction = v3 - v2
                    v4 = v1 + direction
                    Hand.append(v4)

                    if aabb:
                        x1,y1 = v1
                        x2,y2 = v2
                        x3,y3 = v3
                        x4,y4 = v4

                        Hand = [
                            np.array([max([x1,x2,x3,x4]), max([y1,y2,y3,y4])]),
                            np.array([min([x1,x2,x3,x4]), min([y1,y2,y3,y4])])
                        ]

                Frame.append(Hand)
            Y.append(Frame)

        return np.array(X), Y


import math

import torch

class BoxCoder:
    def __init__(self, weights, bbox_xform_clip=math.log(1000. / 16)):
        self.weights = weights
        self.bbox_xform_clip = bbox_xform_clip

    def encode(self, reference_box, proposal):
        """
        Encode a set of proposals with respect to some
        reference boxes

        Arguments:
            reference_boxes (Tensor[N, 4]): reference boxes
            proposals (Tensor[N, 4]): boxes to be encoded
        """

        width = proposal[:, 2] - proposal[:, 0]
        height = proposal[:, 3] - proposal[:, 1]
        ctr_x = proposal[:, 0] + 0.5 * width
        ctr_y = proposal[:, 1] + 0.5 * height

        gt_width = reference_box[:, 2] - reference_box[:, 0]
        gt_height = reference_box[:, 3] - reference_box[:, 1]
        gt_ctr_x = reference_box[:, 0] + 0.5 * gt_width
        gt_ctr_y = reference_box[:, 1] + 0.5 * gt_height

        dx = self.weights[0] * (gt_ctr_x - ctr_x) / width
        dy = self.weights[1] * (gt_ctr_y - ctr_y) / height
        dw = self.weights[2] * torch.log(gt_width / width)
        dh = self.weights[3] * torch.log(gt_height / height)

        delta = torch.stack((dx, dy, dw, dh), dim=1)
        return delta

    def decode(self, delta, box):
        """
        From a set of original boxes and encoded relative box offsets,
        get the decoded boxes.

        Arguments:
            delta (Tensor[N, 4]): encoded boxes.
            boxes (Tensor[N, 4]): reference boxes.
        """

        dx = delta[:, 0] / self.weights[0]
        dy = delta[:, 1] / self.weights[1]
        dw = delta[:, 2] / self.weights[2]
        dh = delta[:, 3] / self.weights[3]

        dw = torch.clamp(dw, max=self.bbox_xform_clip)
        dh = torch.clamp(dh, max=self.bbox_xform_clip)

        width = box[:, 2] - box[:, 0]
        height = box[:, 3] - box[:, 1]
        ctr_x = box[:, 0] + 0.5 * width
        ctr_y = box[:, 1] + 0.5 * height

        pred_ctr_x = dx * width + ctr_x
        pred_ctr_y = dy * height + ctr_y
        pred_w = torch.exp(dw) * width
        pred_h = torch.exp(dh) * height

        xmin = pred_ctr_x - 0.5 * pred_w
        ymin = pred_ctr_y - 0.5 * pred_h
        xmax = pred_ctr_x + 0.5 * pred_w
        ymax = pred_ctr_y + 0.5 * pred_h

        target = torch.stack((xmin, ymin, xmax, ymax), dim=1)
        return target

def box_iou(box_a, box_b):
    """
    Arguments:
        boxe_a (Tensor[N, 4])
        boxe_b (Tensor[M, 4])

    Returns:
        iou (Tensor[N, M]): the NxM matrix containing the pairwise
            IoU values for every element in box_a and box_b
    """

    lt = torch.max(box_a[:, None, :2], box_b[:, :2])
    rb = torch.min(box_a[:, None, 2:], box_b[:, 2:])

    wh = (rb - lt).clamp(min=0)
    inter = wh[:, :, 0] * wh[:, :, 1]
    area_a = torch.prod(box_a[:, 2:] - box_a[:, :2], 1)
    area_b = torch.prod(box_b[:, 2:] - box_b[:, :2], 1)

    return inter / (area_a[:, None] + area_b - inter)

def process_box(box, score, image_shape, min_size):
    """
    Clip boxes in the image size and remove boxes which are too small.
    """

    box[:, [0, 2]] = box[:, [0, 2]].clamp(0, image_shape[1])
    box[:, [1, 3]] = box[:, [1, 3]].clamp(0, image_shape[0])

    w, h = box[:, 2] - box[:, 0], box[:, 3] - box[:, 1]
    keep = torch.where((w >= min_size) & (h >= min_size))[0]
    box, score = box[keep], score[keep]
    return box, score

def nms(box, score, threshold):
    """
    Arguments:
        box (Tensor[N, 4])
        score (Tensor[N]): scores of the boxes.
        threshold (float): iou threshold.

    Returns:
        keep (Tensor): indices of boxes filtered by NMS.
    """

    return torch.ops.torchvision.nms(box, score, threshold)

# just for test. It is too slow. Don't use it during train
def slow_nms(box, nms_thresh):
    idx = torch.arange(box.size(0))

    keep = []
    while idx.size(0) > 0:
        keep.append(idx[0].item())
        head_box = box[idx[0], None, :]
        remain = torch.where(box_iou(head_box, box[idx]) <= nms_thresh)[1]
        idx = idx[remain]

    return keep