import os
import json
import datetime
from openpyxl import Workbook
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter

def adjust_column_widths(sheet):
    for column in sheet.columns:
        max_length = max(len(str(cell.value)) for cell in column)
        column_letter = get_column_letter(column[0].column)
        adjusted_width = (max_length + 2) * 1.2
        sheet.column_dimensions[column_letter].width = adjusted_width

def format_datetime(datetime_str):
    datetime_obj = datetime.datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S")
    formatted_datetime = datetime_obj.strftime("%d.%m.%y-%H:%M")
    return formatted_datetime

def replace_shop_name(shop_name):
    replace_file_path = os.path.join("sys", "shopnamereplace.json")

    # Load data from shopnamereplace.json for shop name replacements
    with open(replace_file_path, "r", encoding="utf-8") as replace_file:
        replace_data = json.load(replace_file)

    # Replace shop name if there is a matching entry in shopnamereplace.json
    for entry in replace_data:
        if entry.get("original_name") == shop_name:
            return entry.get("short_name")

    return shop_name

def create_export_file(data):
    # Path to the output directory
    output_dir = "output/"

    # Load excluded shop names
    excluded_file_path = os.path.join("sys", "excluded.json")
    excluded_shops = []
    if os.path.exists(excluded_file_path):
        with open(excluded_file_path, "r", encoding="utf-8") as excluded_file:
            excluded_shops = json.load(excluded_file)

    # Create a new Excel workbook for export
    export_workbook = Workbook()

    # Remove the default "Sheet" that is automatically created
    default_sheet = export_workbook["Sheet"]
    export_workbook.remove(default_sheet)

    # Check if there is any data available
    if not data:
        # Create a single sheet named "No data" if there is no data
        export_sheet = export_workbook.active
        export_sheet.title = "No data"
    else:
        # Group the data by month and day
        data_by_month = {}
        for item in data:
            receipt = item.get("ticket", {}).get("document", {}).get("receipt", {})
            datetime_str = receipt.get("dateTime")
            shop_name = replace_shop_name(receipt.get("user"))
            if datetime_str and shop_name not in excluded_shops:
                datetime_obj = datetime.datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S")
                month = datetime_obj.strftime("%Y-%m")
                day = datetime_obj.date()
                if month not in data_by_month:
                    data_by_month[month] = {}
                if day not in data_by_month[month]:
                    data_by_month[month][day] = []
                data_by_month[month][day].append(item)

        # Create separate sheets for each month and add data for each day
        for month, month_data in data_by_month.items():
            month_sheet = export_workbook.create_sheet(title=month)

            for day, day_data in month_data.items():
                export_sheet = month_sheet

                # Add a separator row if it's not the first day
                if day != min(month_data.keys()):
                    export_sheet.append([])

                # Add a header row
                export_sheet.append(["Date/Time", "Shop", "Product", "Price", "Quantity", "Sum"])
                export_sheet.freeze_panes = "A2"

                # Apply bold font to the header row
                export_bold_font = Font(bold=True)
                for cell in export_sheet[1]:
                    cell.font = export_bold_font

                # Export data to the export sheet
                sum_total = 0
                for item in day_data:
                    receipt = item.get("ticket", {}).get("document", {}).get("receipt", {})
                    shop_name = replace_shop_name(receipt.get("user"))
                    for product in receipt.get("items", []):
                        row = [
                            format_datetime(receipt.get("dateTime")), shop_name, product.get("name"),
                            product.get("price") / 100, product.get("quantity"), product.get("sum") / 100
                        ]
                        export_sheet.append(row)
                        sum_total += product.get("sum") / 100

                # Add a row with the total sum for the day
                export_sheet.append([])  # Add an empty row
                total_row = ["Total", "", "", "", "", sum_total]
                export_sheet.append(total_row)

                # Apply bold font to the total row
                for row in export_sheet.iter_rows(max_row=export_sheet.max_row, max_col=export_sheet.max_column):
                    for cell in row:
                        if cell.row == export_sheet.max_row:
                            cell.font = export_bold_font

            # Adjust column widths for the month sheet
            adjust_column_widths(month_sheet)

    # Save the export workbook as export.xlsx
    export_file = os.path.join(output_dir, "export.xlsx")
    export_workbook.save(export_file)

    print(f"Export file {export_file} created successfully.")

# Path to the input directory
input_dir = "input/"

# Get a list of JSON files in the directory
json_files = [file for file in os.listdir(input_dir) if file.endswith(".json")]

data = []  # Store the data from all JSON files

for json_file in json_files:
    # Load data from the JSON file
    with open(os.path.join(input_dir, json_file), "r", encoding="utf-8") as file:
        data.extend(json.load(file))

# Create the export file with separate sheets for each month and add separators between days
create_export_file(data)


#!/usr/bin/env python3
# -*- coding:utf-8 -*-

"""
The Official CKIP CoreNLP Toolkit.
"""

__author_name__ = 'Mu Yang'
__author_email__ = 'emfomy@gmail.com'
__copyright__ = '2018-2023 CKIP Lab'

__title__ = 'CKIPNLP'
__version__ = '1.0.3'
__description__ = 'CKIP CoreNLP'
__license__ = 'GPL-3.0'

__url__ = 'https://ckipnlp.readthedocs.io'
__download_url__ = __url__+'/tarball/'+__version__


# Importing flask module in the project is mandatory
# An object of Flask class is our WSGI application.
from flask import Flask, request, url_for, redirect, render_template

app = Flask(__name__)

@app.route('/')
@app.route('/home')
def home():
    return render_template("home.html")

# @app.route('/<file_address>')
# def print_address(file_address):
#     return f"
{file_address}
"

@app.route('/<fc>', methods=['POST', 'GET'])

def get_bidupdate(fc):
    render_template('bid_update.html')
    if request.method == "POST":
        bidupdate_file = request.form["bd_ad"]
        return f'
Forecast file: {fc}
' \
               f'Bid-Update file: {bidupdate_file}
'
    else:
        return render_template("bid_update.html")

    # return f"
you entered this address for forecast data: {file_address}
"

@app.route('/forecast', methods=['POST', 'GET'])
def forecast_get():
    if request.method == "POST":
        forecast_file = request.form["fc_ad"]
        return redirect(url_for('get_bidupdate', fc=forecast_file))
    else:
        return render_template("forecast.html")

# main driver function
if __name__ == '__main__':
    # run() method of Flask class runs the application
    # on the local development server.
    app.run(debug=True)


#https://micropyramid.com/blog/programming-with-python-descriptors/
class Person(object):
  def __init__(self):
    self._name = ';'
  @property
  def name(self):
    print("Getting: %s" % self._name)
    return self._name
  @name.setter
  def name(self, value):
    print("Setting: %s" % value)
    self._name = value.title()
  @name.deleter
  def name(self):
    print("Deleting: %s" % self._name)
    del self._name

obj = Person
print(obj.name)
obj.name = "Sheetal"
print(obj.name)
del(obj.name)
#print(obj.name)


import typing as t
import numpy as np
import math

"""## 2.1 Feature-based Measures

### 2.1.1 Maximum Fisher’s Discriminant Ratio (F1)
"""

def numerator (X: np.ndarray, cls_index: np.ndarray, cls_n_ex: np.ndarray, f: int) -> float:
    summation = 0
    for j in range(len(cls_n_ex)):
        for k in range(len(cls_n_ex)):
            summation += cls_n_ex[j]/sum(cls_n_ex) * cls_n_ex[k]/sum(cls_n_ex) * np.power((np.mean(X[cls_index[j], f])- np.mean(X[cls_index[k], f])), 2)
    return summation
# according to aquation(2)

def denominator (X: np.ndarray, cls_index: np.ndarray, cls_n_ex: np.ndarray, f: int) -> float:
    summation = 0
    for j in range(len(cls_n_ex)):
        summation += cls_n_ex[j]/sum(cls_n_ex) * np.power(np.std(X[cls_index[j], f]), 2)
    return summation
# according to aquation(2)

def compute_rfi (X: np.ndarray, cls_index, cls_n_ex) -> float:
  ls = []
  for i in range(np.shape(X)[1]):
    if denominator(X, cls_index, cls_n_ex, i)!= 0:
      ls.append(numerator (X, cls_index, cls_n_ex, i)/denominator(X, cls_index, cls_n_ex, i))
  return ls

def ft_F1(X: np.ndarray, cls_index: np.ndarray, cls_n_ex: np.ndarray) -> float:
    return 1/(1 + np.max(compute_rfi (X, cls_index, cls_n_ex)))

def numerator (X: np.ndarray, cls_index, cls_n_ex, i) -> float:
    return np.sum([cls_n_ex[j]*np.power((np.mean(X[cls_index[j], i])-np.mean(X[:, i], axis=0)),2) for j in range (len(cls_index))])
# according to aquation(3)

def denominator (X: np.ndarray, cls_index, cls_n_ex, i) -> float:
    return np.sum([np.sum(np.power(X[cls_index[j], i]-np.mean(X[cls_index[j], i], axis=0), 2)) for j in range(0, len(cls_n_ex))])
# according to aquation(3)

def compute_rfi (X: np.ndarray, cls_index, cls_n_ex) -> float:
  ls = []
  for i in range(np.shape(X)[1]):
    if denominator(X, cls_index, cls_n_ex, i)!= 0:
      ls.append(numerator (X, cls_index, cls_n_ex, i)/denominator(X, cls_index, cls_n_ex, i))
  return ls

def ft_F1(X: np.ndarray, cls_index: np.ndarray, cls_n_ex: np.ndarray) -> float:
    return 1/(1 + np.max(compute_rfi (X, cls_index, cls_n_ex)))

"""### 2.1.2 The Directional-vector MaximumFisher's Discreminant Ratio (F1v)"""

def dVector(X: np.ndarray, y_class1: np.ndarray, y_class2: np.ndarray) -> float:
    X_class1 = X[y_class1]; u_class1 = np.mean(X_class1, axis= 0)
    X_class2 = X[y_class2]; u_class2 = np.mean(X_class2, axis= 0)
    W = ((np.shape(X_class1)[0]/ (np.shape(X_class1)[0] + np.shape(X_class2)[0]))* np.cov(X_class1.T)) \
     + (np.shape(X_class2)[0]/(np.shape(X_class1)[0] + (np.shape(X_class2)[0])) * np.cov(X_class2.T))
    try:
      d = np.dot(np.linalg.inv(W), (u_class1 - u_class2))
    except:
      return 0
    B = np.dot((u_class1 - u_class2)[np.newaxis].T,(u_class1 - u_class2).reshape(1,(u_class1 - u_class2).shape[0]))
    dv = np.dot(np.dot(d.T, B), d)/ np.dot(np.dot(d.T, W), d)
    return dv

def ft_F1v(X: np.ndarray, ovo_comb: np.ndarray, cls_index: np.ndarray) ->float:
    df_list = []

    for idx1, idx2 in ovo_comb:
        y_class1 = cls_index[idx1]
        y_class2 = cls_index[idx2]
        dF = dVector(X, y_class1, y_class2)
        if dF == 0:
          continue
        df_list.append(dF)
    aux = np.mean(df_list)
    f1v = 1/(1+aux)
    if math.isnan(f1v) == True:
      f1v = 0.0
    return f1v

"""### 2.1.3 Volume of Overlapping Region (F2)¶"""

def _minmax(X: np.ndarray, class1: np.ndarray, class2: np.ndarray) -> np.ndarray:
    """ This function computes the minimum of the maximum values per class
    for all features.
    """
    max_cls = np.zeros((2, X.shape[1]))

    try:
      max_cls[0, :] = np.max(X[class1], axis=0)
    except:
      print()
    try:
      max_cls[1, :] = np.max(X[class2], axis=0)
    except:
      print()
    aux = np.min(max_cls, axis=0)

    return aux

def _minmin(X: np.ndarray, class1: np.ndarray, class2: np.ndarray) -> np.ndarray:
    """ This function computes the minimum of the minimum values per class
    for all features.
    """
    min_cls = np.zeros((2, X.shape[1]))
    min_cls[0, :] = np.min(X[class1], axis=0)
    min_cls[1, :] = np.min(X[class2], axis=0)
    aux = np.min(min_cls, axis=0)

    return aux

def _maxmin(X: np.ndarray, class1: np.ndarray, class2: np.ndarray) -> np.ndarray:
    """ This function computes the maximum of the minimum values per class
    for all features.
    """
    min_cls = np.zeros((2, X.shape[1]))

    min_cls = np.zeros((2, X.shape[1]))
    try:
      min_cls[0, :] = np.min(X[class1], axis=0)
    except:
      print()
    try:
      min_cls[1, :] = np.min(X[class2], axis=0)
    except:
      print()
    aux = np.max(min_cls, axis=0)

    return aux

def _maxmax(X: np.ndarray, class1: np.ndarray, class2: np.ndarray) -> np.ndarray:
    """ This function computes the maximum of the maximum values per class
    for all features.
    """
    max_cls = np.zeros((2, X.shape[1]))
    max_cls[0, :] = np.max(X[class1], axis=0)
    max_cls[1, :] = np.max(X[class2], axis=0)
    aux = np.max(max_cls, axis=0)
    return aux

def ft_F2(X: np.ndarray, ovo_comb: np.ndarray, cls_index: np.ndarray) -> float:
    f2_list = []

    for idx1, idx2 in ovo_comb:
        y_class1 = cls_index[idx1]
        y_class2 = cls_index[idx2]
        zero_ = np.zeros(np.shape(X)[1])
        overlap_ = np.maximum(zero_, _minmax(X, y_class1, y_class2)-_maxmin(X, y_class1, y_class2))
        range_ = _maxmax(X, y_class1, y_class2)-_minmin(X, y_class1, y_class2)
        ratio = overlap_/range_
        for i in range(ratio.shape[0]):
          if math.isnan(ratio[i]) == True:
            ratio[i] = 1
        f2_list.append(np.prod(ratio))

    f2 = np.mean(f2_list)
    if f2 == float("NaN"):
      f2 = 0
    return np.mean(f2_list)

"""### 2.1.4 Maximum Individual Feature Efficiency (F3)"""

def _compute_f3(X_: np.ndarray, minmax_: np.ndarray, maxmin_: np.ndarray) -> np.ndarray:
    """ This function computes the F3 complexity measure given minmax and maxmin."""

    overlapped_region_by_feature = np.logical_and(X_ >= maxmin_, X_ <= minmax_)

    n_fi = np.sum(overlapped_region_by_feature, axis=0)
    idx_min = np.argmin(n_fi)

    return idx_min, n_fi, overlapped_region_by_feature

def ft_F3(X: np.ndarray, ovo_comb: np.ndarray, cls_index: np.ndarray, cls_n_ex: np.ndarray) -> np.ndarray:

    f3 = []
    for idx1, idx2 in ovo_comb:
        idx_min, n_fi, _ = _compute_f3(X, _minmax(X, cls_index[idx1], cls_index[idx2]),
        _maxmin(X, cls_index[idx1], cls_index[idx2]))
    f3.append(n_fi[idx_min] / (cls_n_ex[idx1] + cls_n_ex[idx2]))

    return np.mean(f3)/len(cls_index)

"""### 2.1.5 Colective Feature Efficiency (F4)"""

def ft_F4(X: np.ndarray, ovo_comb: np.ndarray, cls_index: np.ndarray, cls_n_ex: np.ndarray) -> np.ndarray:

    f4 = []
    for idx1, idx2 in ovo_comb:
        aux = 0

        y_class1 = cls_index[idx1]
        y_class2 = cls_index[idx2]
        sub_set = np.logical_or(y_class1, y_class2)
        y_class1 = y_class1[sub_set]
        y_class2 = y_class2[sub_set]
        X_ = X[sub_set, :]
        # X_ = X[np.logical_or(y_class1, y_class2),:]

        while X_.shape[1] > 0 and X_.shape[0] > 0:
            # True if the example is in the overlapping region
            idx_min, _, overlapped_region_by_feature = _compute_f3(X_,_minmax(X_, y_class1, y_class2),_maxmin(X_, y_class1, y_class2))

            # boolean that if True, this example is in the overlapping region
            overlapped_region = overlapped_region_by_feature[:, idx_min]

            # removing the non overlapped features
            X_ = X_[overlapped_region, :]
            y_class1 = y_class1[overlapped_region]
            y_class2 = y_class2[overlapped_region]

            if X_.shape[0] > 0:
                aux = X_.shape[0]
            else:
                aux = 0
            # removing the most efficient feature
            X_ = np.delete(X_, idx_min, axis=1)

        f4.append(aux/(cls_n_ex[idx1] + cls_n_ex[idx2]))

    return np.mean(f4)


from __future__ import print_function

import sys
import pickle

import numpy as np
import lasagne
import theano
import theano.tensor as T

import hyperopt
from hyperopt import fmin, tpe, hp, Trials, rand
from hyperopt.mongoexp import MongoTrials

sys.path.append("./utils")
import otto_train as otto_tr
import otto_load_data as otto_dl

def objective(space, num_epochs=100):
    """
    Defines the objective function used by hyperopt. The objective function
    is the best validation score of a trained neural network.
    """

    #Some weirdness with the scope of hyperopt.
    import pickle
    import sys

    import numpy as np

    import lasagne
    import theano
    import theano.tensor as T

    sys.path.append("./utils")
    import otto_train as otto_tr
    import otto_load_data as otto_dl
    import model_building as mb

    print(space)

    #Train code
    dataset=otto_dl.load_data(transform='sqrt',
                              whiten=True,
                              test_size=0.05)

    output_layer = mb.build_model(space,
                               num_features=dataset['input_dim'],
                               output_dim=dataset['output_dim'])

    valid_loss, train_loss = otto_tr.run_training_hyperopt(
                                 output_layer=output_layer,
                                 dataset=dataset,
                                 batch_size=334,
                                 num_epochs=num_epochs,
                                 learning_rate=space['learning_rate'])

    #Monitoring
    best_valid_loss = valid_loss.min()
    #Haha this looks fun
    best_valid_epoch = np.argmin(valid_loss)+1 #correct of 0-index
    corresponding_train_loss = train_loss[best_valid_epoch-1]
    print("Best valid loss: ")
    print(best_valid_loss)
    print("Corresponding train loss: ")
    print(corresponding_train_loss)
    print("At epoch: ")
    print(best_valid_epoch)

    return {
            'loss': str(best_valid_loss),
            'attachments':
            {'valid_loss': pickle.dumps(valid_loss),
             'train_loss': pickle.dumps(train_loss),
             },
           }

def main():
    """
    Performs hyperparameter optimization with hyperopt. It consists of three
    steps. First define a trials object connected to a mongo database where all
    the results will be stored. Secondly define a stochastic search space from
    which hyperopt will sample hyperparameter configurations. Thirdly define
    the define the objective function and run the minimization function.
    """

    trials = MongoTrials('mongo://localhost:1234/otto-sqrt-pca-95-5/jobs',
            exp_key='15-11-03')

    #Search space
    space={

    'dense_part':  {'num_units' : hp.quniform('DL1', 512, 2048, 512),
                    'more_layers' :
                        {'num_units' : hp.quniform('DL2', 512, 2048, 512),
                         'more_layers' :
                          hp.choice('MD2', [0,
                            {'num_units' : hp.quniform('DL3', 512, 2048, 512),
                             'more_layers' : 0,} #DL3
                                        ])},#DL2
                                                },#DL1

    'leakiness' : hp.choice('leak', [0, 0.01, 0.15] ),

    'weight_init' : hp.choice('weight',['orto','uni']),
    'input_dropout' : hp.quniform('p_in', 0.1, 0.4, 0.1),

    'learning_rate': hp.choice('lr',[0.001,0.01,0.025,0.05,0.1]),
    }

    #Optimize
    best = fmin(objective, space=space, algo=tpe.suggest, max_evals=100,
               trials=trials)

    print(trials.losses())
    print(best)

if __name__ == '__main__':
    main()


import re

from sqlalchemy import Column, String, Float, PrimaryKeyConstraint, ForeignKeyConstraint
from sqlalchemy.orm import relationship

from fr.tagc.rainet.core.util.sql.Base import Base

from fr.tagc.rainet.core.util.sql.SQLManager import SQLManager
from fr.tagc.rainet.core.util.exception.RainetException import RainetException
from fr.tagc.rainet.core.data import DataConstants
from fr.tagc.rainet.core.util.pattern.PatternUtil import PatternUtil
from fr.tagc.rainet.core.util.sql.SQLUtil import SQLUtil
from fr.tagc.rainet.core.util.log.Logger import Logger
from fr.tagc.rainet.core.util.exception.NotRequiredInstantiationException import NotRequiredInstantiationException
from fr.tagc.rainet.core.data.ProteinCrossReference import ProteinCrossReference

# #
# This class describes a Protein to Protein interaction
#
class ProteinInteraction( Base ):
    __tablename__ = 'ProteinInteraction'

    # The first interactor
    interactorA_id = Column( String )
    interactorA = relationship( 'Protein', foreign_keys = interactorA_id )
    # The first interactor
    interactorB_id = Column( String )
    interactorB = relationship( 'Protein', foreign_keys = interactorB_id )
    # The method used to detect the interaction
    detectionMethod = Column( String )
    # The pubmedID of the publication reporting the interaction
    pubmedID = Column( String )
    # The type of the interaction
    interactionType = Column( String )
    # The database reporting where the interaction was found
    sourceDB = Column( String )
    # The confidence score associated to the interaction
    confidenceScore = Column( Float )
    # Define the Composite PrimaryKey and ForeignKeys
    __table_args__ = (
        PrimaryKeyConstraint( 'interactorA_id', 'interactorB_id', 'detectionMethod', 'pubmedID' ),
        ForeignKeyConstraint( ['interactorA_id', 'interactorB_id'],
                            ['Protein.uniprotAC', 'Protein.uniprotAC'] )
                            , )

    # #
    # The constructor
    #
    def __init__( self, id_A, id_B, alt_id_A, alt_id_B, method, pubmed_string, interaction_type, source_db, score_string ):

        Logger.get_instance().debug( "\nSearching proteins" )
        protein_A = self.find_protein( [id_A, alt_id_A] )
        Logger.get_instance().debug( "|--proteinA found = " + str( protein_A) )

        protein_B = self.find_protein( [id_B, alt_id_B] )
        Logger.get_instance().debug( "|--proteinB found = " + str( protein_B ) )

        # If both interacting proteins have been found
        if protein_A != None and protein_B != None:
            self.pubmedID = PatternUtil.find_single_group_in_string( DataConstants.INTERACTOME_PUBMED_REGEX, pubmed_string.lower(), False )
#             self.detectionMethod = PatternUtil.find_single_group_in_string( DataConstants.INTERACTOME_METHOD_REGEX, method, False )
#             self.interactionType = PatternUtil.find_single_group_in_string( DataConstants.INTERACTOME_TYPE_REGEX, interaction_type, False )
#             self.sourceDB = PatternUtil.find_single_group_in_string( DataConstants.INTERACTOME_SOURCEDB_REGEX, source_db, False )
            self.detectionMethod= method
            self.interactionType = interaction_type
            self.sourceDB = source_db
            self.confidenceScore = self.get_score( score_string )
            Logger.get_instance().debug( "--->score is " + str( self.confidenceScore))

            # Test if a similar interaction already exists (may be possible due to isoform of proteins)
            sql_session = SQLManager.get_instance().get_session()
            if self.search_similar_interaction( protein_A, protein_B, sql_session ) != None:
                raise NotRequiredInstantiationException( "ProteinInteraction: A similar interaction exists in database." )

            # Add interaction to interacting proteins
            self.interactorA = protein_A
            self.interactorB = protein_B
            sql_session.add( protein_A )
            sql_session.add( protein_B )
        else:
            raise NotRequiredInstantiationException( "ProteinInteraction: At least one interacting protein was not found in database." )

    # #
    # Search for a protein ID (uniprotAC or other) in the ids of the provided id_list
    #
    def find_protein( self, id_list ):

        #=======================================================================
        # Search for a UniprotAC in the provided IDs
        #=======================================================================
        uniprotkb_pattern = re.compile( DataConstants.INTERACTOME_ID_UNIPROTKB_REGEX )
        uniprot_ac_list = PatternUtil.find_groups_in_list( uniprotkb_pattern, id_list )
        # -- If the uniprotAC contains a "-", it is a isoform uniprotAC so only the base
        # of the AC must be kept

        # Search for other ID type using also cross references if no uniprot AC was found before
        if uniprot_ac_list == None or len ( uniprot_ac_list ) == 0:

            Logger.get_instance().debug( "|--Find_proteins :Trying crossrefences ---" )
            # Initialize uniprot_ac_list
            uniprot_ac_list = []

            # Get a SQLalchemy session
            sql_session = SQLManager.get_instance().get_session()

            # Parse the cross reference regular expression to find matching ID that will be
            # converted to uniprotAC thanks to cross references present in database
            for crossref_source, crossref_pattern in DataConstants.INTERACTOME_ID_CROSSREF_REGEX_DICT.items():
                Logger.get_instance().debug( "|--|--Cross reference = " + crossref_source )
                # Compile the regular expression
                crossref_compiled_pattern = re.compile( crossref_pattern )
                # Search matching string (if any) in the provided strings
                matched_crossref_list = PatternUtil.find_groups_in_list( crossref_compiled_pattern, id_list )
                Logger.get_instance().debug( "|--|--|--Cross reference list found = " + str( matched_crossref_list ) )
                # If matching string exists, look in database if corresponding crossreference is found
                # If so, add the equivalent uniprotAC to the final list.
                if matched_crossref_list != None and len( matched_crossref_list ) > 0:
                    for matched_crossref in matched_crossref_list:
                        db_regex = DataConstants.INTERACTOME_ID_CROSSREF_DB_REGEX_DICT[ crossref_source].replace( "VALUE", matched_crossref )
                        uniprot_ac = sql_session.query( ProteinCrossReference.protein_id ).filter( ProteinCrossReference.sourceDB == crossref_source, ProteinCrossReference.crossReferenceID.like( db_regex ) ).all()
                        if uniprot_ac != None and len( uniprot_ac) >= 1:
                            uniprot_ac_list.extend( uniprot_ac[0] )

        Logger.get_instance().debug( "|--Found unitproAC= " + str( uniprot_ac_list ) )

        #=======================================================================
        # Return the protein with the found uniprot_ac
        #=======================================================================
        try:
            return SQLUtil.find_unique_protein_from_uniprotAC_list( uniprot_ac_list )
        except RainetException as e:
            Logger.get_instance().debug( e.to_string() )
            return None

    # #
    # Retrieve the interaction score in the given string
    #
    # @param score_string : string - The string containing the score like "intact-miscore:0.96"
    #
    # @return float - The interaction score or -1 if an issue occurred
    #
    def get_score( self, score_string):

        compiled_pattern = re.compile( DataConstants.INTERACTOME_SCORE_REGEX )
        score = PatternUtil.find_groups_in_string( compiled_pattern, score_string )

        Logger.get_instance().debug( "|--|--|-score groups = " + str( score ) )
        if score == None:
            return -1
        elif len( score) >= 1:
            score_number = score[0]
        else:
            return -1

        try:
            return float( score_number )
        except ValueError:
            Logger.get_instance().warning( "ProteinInteraction.get_score : score is not a number : " + score_number )
            return -1

    ##
    # Search for a ProteinInteraction between protein_a and protein_b with detectionMethod and PubmedID
    # equals to those inserted in the current ProteinInteraction. The search is done for protein in
    # both directions.
    #
    # @param protein_a : Protein - The first protein
    # @param protein_b : Protein - The second protein
    # @param sql_session : SQLSession - The current SQLAlchemy session
    def search_similar_interaction( self, protein_a, protein_b, sql_session ):

        # Look at an interaction from A to B
        a_to_b_list = sql_session.query( ProteinInteraction ).filter( ProteinInteraction.interactorA == protein_a,
                                                       ProteinInteraction.interactorB == protein_b,
                                                       ProteinInteraction.detectionMethod == self.detectionMethod,
                                                       ProteinInteraction.pubmedID == self.pubmedID ).all()

        # Check the result: if a single interaction is found, return it. If several are found, raise an exception
        if a_to_b_list != None and len( a_to_b_list ) > 0:
            if len( a_to_b_list ) == 1:
                return a_to_b_list[0]
            else:
                raise RainetException( "ProteinInteraction.__init__ : Found several protein interaction for interactor A = " + protein_a.uniprotAC +
                                      " /interactor B = " + protein_b.uniprotAC + " /detection method = " + self.detectionMethod + " /pubmedID = " + self.pubmedID )

        # Look at an interaction from B to A
        b_to_a_list = sql_session.query( ProteinInteraction ).filter( ProteinInteraction.interactorA == protein_b,
                                                       ProteinInteraction.interactorB == protein_a,
                                                       ProteinInteraction.detectionMethod == self.detectionMethod,
                                                       ProteinInteraction.pubmedID == self.pubmedID ).all()

        # Check the result: if a single interaction is found, return it. If several are found, raise an exception
        if b_to_a_list != None and len( b_to_a_list ) > 0:
            if len( b_to_a_list ) == 1:
                return b_to_a_list[0]
            else:
                raise RainetException( "ProteinInteraction.__init__ : Found several protein interaction for interactor A = " + protein_b.uniprotAC +
                                      " /interactor B = " + protein_a.uniprotAC + " /detection method = " + self.detectionMethod + " /pubmedID = " + self.pubmedID )

        return None

    # #
    # Add the object to SQLAlchemy session
    def add_to_session( self ):

        sql_session = SQLManager.get_instance().get_session()
        sql_session.add( self )


# wrapper for observation preprocessing - features extraction from the game
import math, sys
from lux.game import Game
from lux.game_map import Cell, RESOURCE_TYPES
from lux.constants import Constants
from lux.game_constants import GAME_CONSTANTS
from lux import annotate
from kaggle_environments import make

import torch
import numpy as np

class EnvWrapper:
    def __init__(self, opponent):
        self.game_state = Game()
        self.opponent = opponent
        self.env = make("lux_ai_2021", configuration={"seed": 562124210, "loglevel": 0, "annotations": False}, debug=False)
        self.trainer = self.env.train([None,opponent])
        obs = self.trainer.reset()
        self.update_game_state(obs)
        self.device = "cuda" if (torch.cuda.is_available()) else "cpu"
        self.current_reward = 0

    def update_game_state(self, observation):
        """Update the local game state from observation"""

        if observation["step"] == 0:
            self.game_state = Game()
            self.game_state._initialize(observation["updates"])
            self.game_state._update(observation["updates"][2:])
            self.game_state.id = observation.player
        else:
            self.game_state._update(observation["updates"])
        self.obs = observation
        self.game_map = self.game_state.map
        self.player = self.game_state.players[observation.player]
        self.opponent = self.game_state.players[(observation.player + 1) % 2]
        self.width, self.height = self.game_map.width, self.game_map.height

        self.resource_tiles: list[Cell] = []
        self.city_tiles: list[Cell] = []

        for y in range(self.height):
            for x in range(self.width):
                cell = self.game_state.map.get_cell(x, y)
                if cell.has_resource():
                    self.resource_tiles.append(cell)
                if cell.citytile is not None:
                    self.city_tiles.append(cell.citytile)

        return self.game_state

    def get_reward(self):
        """Workaround to get a correct reward"""
        player = self.player
        ct_count = sum([len(v.citytiles) for k, v in player.cities.items()])
        unit_count = len(self.game_state.players[player.team].units)
        score = ct_count * 10000 + unit_count*10
        if score == self.current_reward:
            reward = 0
        if score > self.current_reward:
            reward = 1
        if score < self.current_reward:
            reward = -1
        self.current_reward = score

        return reward

    def get_cell_resources(self, cell: Cell):
        wood = 0
        coal = 0
        uranium = 0
        if cell.has_resource():
            wood = cell.resource.amount if cell.resource.type == "wood" else 0
            coal = cell.resource.amount if cell.resource.type == 'coal' else 0
            uranium = cell.resource.amount if cell.resource.type == 'uranium' else 0
        return wood, coal, uranium

    def get_adjacent_resources(self, pos):
        wood = 0
        coal = 0
        uranium = 0
        x, y = (pos.x, pos.y)
        for i in range(-1,1):
            for j in range(-1,1):
                cell = self.game_map.get_cell(x+i,y+j)
                if cell.has_resource():
                    wood += cell.resource.amount if cell.resource.type == "wood" else 0
                    coal += cell.resource.amount if cell.resource.type == 'coal' else 0
                    uranium += cell.resource.amount if cell.resource.type == 'uranium' else 0
        return wood, coal, uranium

    def add_units_state(self, state):
        # player units features
        for unit in self.player.units:
            active = 1 if unit.can_act() else 0.1
            cell = self.game_map.get_cell_by_pos(unit.pos)
            worker_type = 1 if unit.is_worker() else 0.5
            cargo = unit.get_cargo_space_left()
            x = unit.pos.x
            y = unit.pos.y
            is_in_city = cell.citytile != None
            adjacent_wood, adjacent_coal, adjacent_uranium = self.get_adjacent_resources(unit.pos)
            state[-8:-1, x, y] = torch.tensor([worker_type, cargo, adjacent_wood, adjacent_coal, adjacent_uranium, is_in_city, active]).to(self.device)
        # opponent units features
        for unit in self.opponent.units:
            x = unit.pos.x
            y = unit.pos.y
            cell_code = 1
            if unit.is_cart:
                cell_code +=0.5
            state[-8, x, y]= cell_code
        return state

    def get_global_state(self):
        state = torch.zeros(16,32,32, dtype=torch.float32).to(self.device)
        for x in range(self.width):
            for y in range(self.height):
                cell = self.game_map.get_cell(x,y)
                w, c, u = self.get_cell_resources(cell)
                cityid = int(cell.citytile.cityid[2:])+1 if cell.citytile else 0
                city = (cell.citytile.team+1) if cell.citytile else 0
                fuel = (self.opponent.cities[cell.citytile.cityid].fuel if city == self.opponent.team+1
                       else self.player.cities[cell.citytile.cityid].fuel if city == self.player.team+1 else 0)
                road = cell.road
                research = self.player.research_points
                state[:,x,y]= torch.tensor([w, c, u, cityid, fuel, city, road, research, 0, 0, 0, 0, 0, 0, 0, 0]).to(self.device)
        state = self.add_units_state(state)
        return state

    def step(self, action):
        obs, _, done, info = self.trainer.step(action)
        game_state = self.update_game_state(obs)
        reward = self.get_reward()
        global_state = self.get_global_state()
        return global_state, reward, done, info

    def reset(self):
        map_seed = np.random.randint(0,1000000000)
        self.env = make("lux_ai_2021", configuration={"seed": map_seed, "loglevel": 0, "annotations": False}, debug=False)
        self.trainer = self.env.train([None,self.opponent])
        obs = self.trainer.reset()
        self.update_game_state(obs)
        global_state = self.get_global_state()
        print("map size: ", self.height)
        return global_state

    def get_game_objects(self):
        return [self.player, self.city_tiles]


import os
import sys
import time
import socket
import select
from threading import Thread
from queue import Queue, Empty

import utp.utp_socket as utp
from dust.crypto.keys import KeyManager
from dust.extensions.lite.lite_socket import lite_socket
from dust.extensions.utp.dustUtpServerSocket import DustUtpServerSocket

if os.name == "nt":
  from errno import WSAEMSGSIZE as EMSGSIZE
  from errno import WSAECONNRESET as ECONNRESET
  from errno import WSAEWOULDBLOCK as EWOULDBLOCK
else:
  from errno import EMSGSIZE
  from errno import ECONNRESET
  from errno import EWOULDBLOCK

class DustUtpSocketServer:
  def __init__(self):
    self.connq=Queue()

    self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    self.udp_socket.bind(('127.0.0.1', 9000))
    self.udp_socket.setblocking(0)

    size = 2 * 1024 * 1024
    self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, size)
    self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, size)

    self.dust = lite_socket(KeyManager())
    self.dust.setAddress(self.udp_socket.getsockname())
    print(self.udp_socket.getsockname())

    self.utp_socket = utp.Socket()
    self.utp_socket.init_outgoing(self.send_to, ("127.0.0.1", 9000))
    print("connecting", self.utp_socket, self.utp_socket.getpeername())

  def start(self):
    t=Thread(target=self.run)
    t.setDaemon(True)
    t.start()

  def run(self):
    while self.utp_socket:
      try:
        print('main')
        self.udp_select(0.5)
        utp.CheckTimeouts()
      except:
        print('Exception in socket server')
        break

  def accept(self, inq, outq):
    print("Waiting for connection...")
    utp_socket=self.connq.get()
#    utp_socket.init_outgoing(self.send_to, ("127.0.0.1", 9000))
    print('Got connection: '+str(utp_socket))
    serverSock=DustUtpServerSocket(inq, outq, utp_socket, self.udp_socket)
    return serverSock

  # dust is the opposite of dirty
  def dustPacket(self, addr, data):
    print('dust')
#    return self.dust.encodePacket(addr, data).packet
    return data

  # dirty is the opposite of dust
  def dirtyPacket(self, addr, data):
    print('dirty')
#    return self.dust.decodePacket(addr, data).data
    return data

  def send_to(self, data, addr):
    print('send_to: '+str(len(data)))
    data = self.dustPacket(addr, data)
    try:
      self.udp_socket.sendto(data, 0, addr)
    except socket.error as se:
      print('socket error: '+str(se))

  def got_incoming_connection(self, utp_socket):
    print("got_incoming_connection!", utp_socket, utp_socket.getpeername())
    self.connq.put(utp_socket)

  def udp_select(self, timeout):
    r, w, e = select.select([self.udp_socket], [], [self.udp_socket], timeout)
    if not r and not e:
      return
    for s in r:
      while True:
        try:
          data, addr = s.recvfrom(8192)
        except socket.error as se:
          if se.args[0] in (ECONNRESET, EMSGSIZE):
            continue
          break
        data = self.dirtyPacket(addr, data)
        utp.IsIncomingUTP(self.got_incoming_connection, self.send_to, data, addr)
    for s in e:
      print("socket error!", s)


#!/usr/bin/env python3
"""
Evaluate the results of prediction by calculating the mean squared error (MSE) for each cell line

* Prerequisite
    1. Run 02_test_model.py
"""
from settings import PROJECT_DIR

def main():
    # param settings
    cell_lines = ['HCC1143', 'HCC1954', 'HCC1187', 'HCC2218']
    depth = '30x'
    model_ver = 'default'
    tumor_purities = list(range(5, 100, 5))

    # path settings
    pred_result_dir = f'{PROJECT_DIR}/results/prediction/{model_ver}'  # input
    mse_result_path = f'{pred_result_dir}/mse_{depth}.txt'  # output

    with open(mse_result_path, 'w') as outfile:
        for cell_line in cell_lines:
            pred_result_path = f'{pred_result_dir}/result_{cell_line}_{depth}.txt'
            pred_result_dict = {}  # key: actual purity, value: predicted purity

            with open(pred_result_path, 'r') as pred_result_file:
                for line in pred_result_file:
                    cols = line.strip().split('\t')
                    pred_result_dict[float(cols[1])] = float(cols[2])

            mse = 0

            for tumor_purity in tumor_purities:
                actual_purity = round(tumor_purity / 100, 2)
                predicted_purity = pred_result_dict[actual_purity]
                mse += (actual_purity - predicted_purity) ** 2

            mse /= len(tumor_purities)

            print(cell_line, round(mse, 4), sep='\t', file=outfile)

if __name__ == '__main__':
    main()


# Put code here

# Import required libraries

from tools import imputer, impute_scale, impute_one_hot_encode, yes_no_to_binary
from models import models
from sklearn.preprocessing import StandardScaler

import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from imblearn.over_sampling import SMOTE
from sklearn.decomposition import PCA
from sklearn.model_selection import cross_val_score

from tools import plot_confusion_matrix

import pandas as pd
from sklearn.linear_model import LogisticRegression
from imblearn.ensemble import BalancedBaggingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, f1_score, roc_auc_score

def impute_scale(X_train, X_test):
    # Separate the numerical and categorical features
    numerical_features = X_train.select_dtypes(include=[np.number]).columns.tolist()
    categorical_features = X_train.select_dtypes(include=object).columns.tolist()
    #X_train, X_test = imputer(X_train, X_test)
    scaler = StandardScaler()
    X_train[numerical_features] = scaler.fit_transform(X_train[numerical_features])
    X_test[numerical_features] = scaler.transform(X_test[numerical_features])
    return X_train, X_test

def models(X_train, X_test, y_train, y_test, labels):
    results = []
    print('Building Models...')

    # 5. Decision Tree Classifier
    method_name = 'Decision Tree Classifier'
    print(f'Building {method_name} model...')
    classifier = DecisionTreeClassifier(criterion='entropy', random_state=0)
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    y_pred_proba = classifier.predict_proba(X_test)[:, 1]
    accuracy = accuracy_score(y_test, y_pred)
    roc = roc_auc_score(y_test, y_pred_proba)
    # print("Decision Tree Accuracy: ", dtt_accuracy)
    # print("Decision Tree ROC AUC: ", dtt_roc)
    cm = confusion_matrix(y_test, y_pred)
    plot_confusion_matrix(cm, labels, method_name)
    report = classification_report(y_test, y_pred, output_dict=True)
    precision = report['weighted avg']['precision']
    recall = report['weighted avg']['recall']
    f1_score = report['weighted avg']['f1-score']
    results.append([method_name, accuracy, roc, precision, recall, f1_score])

    # 6. Random Forest Classifier
    method_name = 'Random Forest Classifier'
    print(f'Building {method_name} model...')
    classifier = RandomForestClassifier(n_estimators=10, criterion='entropy', random_state=0)
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    y_pred_proba = classifier.predict_proba(X_test)[:, 1]
    accuracy = accuracy_score(y_test, y_pred)
    roc = roc_auc_score(y_test, y_pred_proba)
    # print("Random Forest Accuracy: ", rf_accuracy)
    # print("Random Forest ROC AUC: ", rf_roc)
    cm = confusion_matrix(y_test, y_pred)
    plot_confusion_matrix(cm, labels, method_name)
    report = classification_report(y_test, y_pred, output_dict=True)
    precision = report['weighted avg']['precision']
    recall = report['weighted avg']['recall']
    f1_score = report['weighted avg']['f1-score']
    results.append([method_name, accuracy, roc, precision, recall, f1_score])

    # 7. Gaussian
    method_name = 'Naive Bayes'
    print(f'Building {method_name} model...')
    classifier = GaussianNB()
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    y_pred_proba = classifier.predict_proba(X_test)[:, 1]
    accuracy = accuracy_score(y_test, y_pred)
    roc = roc_auc_score(y_test, y_pred_proba)
    # print("Naive Bayes Accuracy: ", nb_accuracy)
    # print("Naive Bayes ROC AUC: ", nb_roc)
    cm = confusion_matrix(y_test, y_pred)
    plot_confusion_matrix(cm, labels, method_name)
    report = classification_report(y_test, y_pred, output_dict=True)
    precision = report['weighted avg']['precision']
    recall = report['weighted avg']['recall']
    f1_score = report['weighted avg']['f1-score']
    results.append([method_name, accuracy, roc, precision, recall, f1_score])

    results_df = pd.DataFrame(results, columns=['Model', 'Accuracy', 'ROC AUC', 'Precision', 'Recall', 'F1-score'])
    print(results_df)

df = pd.read_csv('weatherAUS.csv')
df = df.dropna(subset=['RainTomorrow'])
labels = ['No', 'Yes']
print(df.head(10))
print(df.info())
print(df.isnull().sum())

df['RainTomorrow'] = df['RainTomorrow'].apply(yes_no_to_binary)

df['Date'] = pd.to_datetime(df['Date'])
df['Year'] = df['Date'].dt.year
df['Month'] = df['Date'].dt.month
df['Day'] = df['Date'].dt.day
df = df.drop('Date', axis=1)

#df = df.dropna(axis=0, how='any', subset=["RainTomorrow"])

print(df.shape)

X = df.drop('RainTomorrow', axis=1)
y = df['RainTomorrow']

# Perform preprocessing for numerical features
X = imputer(X)

# 6 columns are of type 'object' and remaining of 'float'
cat, cont = [], []

# # MODELLING CODE STARTS HERE

# Split the dataset into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

print(X_train.info())
print(X_train.info())

#X_train, X_test = impute_scale(X_train, X_test)
X_train, X_test = impute_one_hot_encode(X_train, X_test)

# models(X_train, X_test, y_train, y_test, labels)
sns.countplot(x = y_train, hue = df['RainTomorrow'])
plt.show()

sm = SMOTE()
#(sampling_strategy='minority', random_state=42)

# Fit and resample the data
X_train, y_train = sm.fit_resample(X_train, y_train)
print(y_train)

sns.countplot(x = y_train)
plt.show()
models(X_train, X_test, y_train, y_test, labels)

# from lazypredict.Supervised import LazyClassifier
# clf = LazyClassifier(verbose=0, ignore_warnings=True, custom_metric=None)
# models, predictions = clf.fit(X_train, X_test, y_train, y_test)
#
# print(models)

pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)*100
d = [n for n in range(len(cumsum))]
plt.figure(figsize=(10, 10))
plt.plot(d, cumsum, color='red', label='cumulative explained variance')
plt.title('Cumulative Explained Variance as a Function of the Number of Components')
plt.ylabel('Cumulative Explained variance')
plt.xlabel('Principal components')
plt.axhline(y=95, color='k', linestyle='--', label='95% Explained Variance')
plt.legend(loc='best')

pca_train = PCA(.95)
pca_train.fit(X_train)
X_train_pca = pca_train.transform(X_train)
# pca_test = PCA(.95)
# pca_test.fit(X_test)
X_test_pca = pca_train.transform(X_test)

print('After PCA:')
models(X_train_pca, X_test_pca, y_train, y_test, labels)

print('k-Fold Cross Validation:')
# k = n_neighbors
knn = KNeighborsClassifier(n_neighbors=3)
# K Fold CV K=10
accuracies = cross_val_score(estimator=knn, X=X_train, y=y_train, cv=10)
print(accuracies)

print("average accuracy: ", np.mean(accuracies))
print("average std: ", np.std(accuracies))

knn.fit(X_train, y_train)
# print("test accuracy: ", knn.score(X_test,y_test))


# coding: utf-8

from parsers.CsvParser import CsvParser
from operation import Operation

from collections import defaultdict

def sort_by_month(operations: [Operation]):
    by_date = defaultdict(list)

    for op in operations:
        m = op.operation_date.month
        y = op.operation_date.year

        by_date[(m, y)].append(op)

    return by_date

def get_operations_from_file(filename: str) -> [Operation]:
    with open(filename, mode='r', encoding='utf-8-sig') as file:
        parser = CsvParser()
        return parser.parse_file(file)

def main():
    operations = get_operations_from_file('input.csv')
    by_month = sort_by_month(operations)
    print(by_month)

if __name__ == '__main__':
    main()


#Modified Smith-Waterman algorithm
#Authors: Devon Hobbs, William DeBliek
#23 December 2022

import numpy as np

#outputs the traceback matrix and the scoring matrix
def score_traceback_matrixs(seq1, seq2, match, mismatch, gap):

    cols = len(seq1) + 1
    rows = len(seq2) + 1

    score_matrix = np.zeros((rows, cols))
    traceback_matrix = []

    for i in range(rows):
        traceback_matrix.append([])
        for j in range(cols):
            traceback_matrix[i].append("")

    traceback_matrix[0][0] = "S"
    score_matrix[0][0] = 0

    for i in range(1, cols):
        score_matrix[0][i] = 0
        traceback_matrix[0][i] = "H"

    for i in range(1, rows):
        score_matrix[i][0] = 0
        traceback_matrix[i][0] = "V"

    for i in range(1, rows):

        for j in range(1, cols):

            vert = score_matrix[i-1][j] + gap
            hori = score_matrix[i][j-1] + gap

            diag = score_matrix[i-1][j-1] + (match if seq1[j - 1] == seq2[i - 1] else mismatch)
            max_val = max(0, vert, hori, diag)
            score_matrix[i][j] = max_val

            if vert == max_val:
                traceback_matrix[i][j] += "V"

            if hori == max_val:
                traceback_matrix[i][j] += "H"

            if diag == max_val:
                traceback_matrix[i][j] += "D"

            if traceback_matrix[i][j] == "":
                traceback_matrix[i][j] = "S"

    return score_matrix, traceback_matrix

def find_traceback(seq1, seq2, score_matrix, traceback_matrix, begin_index = [0,0]):

    max_index = np.unravel_index(np.argmax(score_matrix), np.shape(score_matrix))
    max_score = score_matrix[max_index[0]][max_index[1]]

    match_or_gap = find_traceback_recursive(score_matrix, traceback_matrix, max_index, ["",""], "")
    match_or_gap_list = match_or_gap.split(",")

    nested_match_or_gap_list = []
    end_indexes = []

    for i in match_or_gap_list:
        nested_match_or_gap_list.append(i.split(" "))

    for i in range(len(nested_match_or_gap_list)):

        seq1_count = 0
        seq2_count = 0
        aligns1 = list(nested_match_or_gap_list[i][0])
        aligns2 = list(nested_match_or_gap_list[i][1])

        for j in range(len(nested_match_or_gap_list[i][0])):

            if nested_match_or_gap_list[i][0][j] == "M":
                aligns1[j] = seq1[max_index[1]-seq1_count-1]
                seq1_count += 1

        for j in range(len(nested_match_or_gap_list[i][1])):

            if nested_match_or_gap_list[i][1][j] == "M":
                aligns2[j] = seq2[max_index[0]-seq2_count-1]
                seq2_count += 1

        nested_match_or_gap_list[i][0] = ''.join(aligns1)[::-1]
        nested_match_or_gap_list[i][1] = ''.join(aligns2)[::-1]
        end_indexes.append([max_index[0]+begin_index[0], max_index[1]+begin_index[1]])
        if i == 0:
            start_index = [max_index[0] - seq2_count + begin_index[0],  max_index[1] - seq1_count + begin_index[1]]

    #start_index = [max_index[0] + begin_index[0], max_index[1] + begin_index[1]]

    return nested_match_or_gap_list, max_score, end_indexes, start_index

def find_traceback_recursive(score_matrix, traceback_matrix, position, main_trace, secondary_trace, select = 0):

    if score_matrix[position[0]][position[1]] == 0:
        return str(main_trace[0]) + " " + str(main_trace[1]) + secondary_trace

    if select == 0:
        if len(traceback_matrix[position[0]][position[1]]) > 1:
            for i in range(1, len(traceback_matrix[position[0]][position[1]])):
                secondary_trace += "," + find_traceback_recursive(score_matrix, traceback_matrix, position, main_trace.copy(), "", i)

    if traceback_matrix[position[0]][position[1]][select] == "D":
        main_trace[0] += "M"
        main_trace[1] += "M"
        return find_traceback_recursive(score_matrix, traceback_matrix, [position[0]-1, position[1]-1], main_trace, secondary_trace)

    if traceback_matrix[position[0]][position[1]][select] == "V":
        main_trace[0] += "_"
        main_trace[1] += "M"
        return find_traceback_recursive(score_matrix, traceback_matrix, [position[0]-1, position[1]], main_trace, secondary_trace)

    if traceback_matrix[position[0]][position[1]][select] == "H":
        main_trace[1] += "_"
        main_trace[0] += "M"
        return find_traceback_recursive(score_matrix, traceback_matrix, [position[0], position[1]-1], main_trace, secondary_trace)

def multi_homolog_smith_waterman_alg(seq1, seq2, match, mismatch, gap, iters):

    seq1_list = [seq1]
    seq1_index = [[0,len(seq1)-1]]
    seq2_list = [seq2]
    seq2_index = [[0,len(seq2)-1]]

    return_values = []

    for i in range(iters):

        score_matrix_list = []
        traceback_matrix_list = []
        max_value = 0

        for j, seq1_sec in enumerate(seq1_list):

            if len(score_matrix_list) <= j:
                score_matrix_list.append([])

            if len(traceback_matrix_list) <= j:
                traceback_matrix_list.append([])

            for k, seq2_sec in enumerate(seq2_list):

                if len(score_matrix_list[j]) <= k:
                    score_matrix_list[j].append([])

                if len(traceback_matrix_list[j]) <= k:
                    traceback_matrix_list[j].append([])

                score_matrix_list[j][k], traceback_matrix_list[j][k] = score_traceback_matrixs(seq1_sec, seq2_sec, match, mismatch, gap)

                if np.max(score_matrix_list[j][k]) > max_value:
                    max_value = np.max(score_matrix_list[j][k])
                    max_matrix = [j,k]
                    align_index = [seq2_index[k][0], seq1_index[j][0]]

        aligns, max_scores, end_indexes, start_index = find_traceback(seq1, seq2, score_matrix_list[max_matrix[0]][max_matrix[1]], traceback_matrix_list[max_matrix[0]][max_matrix[1]], align_index)
        print(end_indexes)
        print("ALIGNS")
        print(aligns)
        max_seq1_end = 0
        max_seq2_end = 0
        for k, value2 in enumerate(end_indexes):
            print("value2")
            print(value2)
            if value2[1] > max_seq1_end:
                print("HEY YEAH")
                max_seq1_end = value2[1]
                max_seq1_end_index = k
            if value2[0] > max_seq2_end:
                max_seq2_end = value2[0]
                max_seq2_end_index = k
        for j, value in enumerate(seq1_index):
            print("END INDEX")
            print(max_seq1_end_index)
            if seq1_index[j][1] > max_seq1_end_index:
                print("if seq1_index[j][1] > max_seq1_end_index:")
                print(seq1_index[j])
                seq1_index.insert(j,[seq1_index[j][0], start_index[1]])
                print(seq1_index[j])
                seq1_index[j+1][0] = max_seq1_end
                print([seq1_index[j][0], start_index[1]])
                seq1_list.insert(j, seq1[seq1_index[j][0]:seq1_index[j][1]])
                seq1_list[j + 1] = seq1[seq1_index[j+1][0]:seq1_index[j+1][1]]

                for i, seq in enumerate(seq1_list):
                    if seq == '':
                        print("DELETED")
                        seq1_index.pop(i)
                        seq1_list.pop(i)
                        i-=1
                print("Index")
                print(seq1_index)
                return_values.append([aligns.copy(), start_index.copy(), end_indexes.copy(), max_scores.copy(), seq1_index.copy(), seq1_list.copy()])
                break
    return return_values

#Test
print("\n")

seq1 = "ABZSFJGHVNAJKFHSJJSDFLIQOWVLKASDFPOIQWERJKLZLBCGHYHI"
seq2 = "IKLKIBAJKFSDFKABCASDFHJASDFJLKASDJFLFASDOIFUJHNKYTP"
score, traceback = score_traceback_matrixs(seq1, seq2, 2, -1, -1)
print(find_traceback(seq1, seq2, score, traceback), "\n")

seq1split = ["ABZSFJGHVNAJK", "FPOIQWERJKLZLBCGHYHI"]
seq2split = ["IKLKIBAJKFSDFKABCASD", "LFASDOIFUJHNKYTP"]
scoresplit0, tracebacksplit0 = score_traceback_matrixs(seq1split[0], seq2split[0], 2, -1, -1)
scoresplit1, tracebacksplit1 = score_traceback_matrixs(seq1split[0], seq2split[1], 2, -1, -1)
scoresplit2, tracebacksplit2 = score_traceback_matrixs(seq1split[1], seq2split[0], 2, -1, -1)
scoresplit3, tracebacksplit3 = score_traceback_matrixs(seq1split[1], seq2split[1], 2, -1, -1)
print(find_traceback(seq1split[0], seq2split[0], scoresplit0, tracebacksplit0))
print(find_traceback(seq1split[0], seq2split[1], scoresplit1, tracebacksplit1))
print(find_traceback(seq1split[1], seq2split[0], scoresplit2, tracebacksplit2))
print(find_traceback(seq1split[1], seq2split[1], scoresplit3, tracebacksplit3))

print("\n")
#print(multi_homolog_smith_waterman_alg(sequ1, sequ2, 2, -1, -1, 2))


from dataclasses import dataclass
from typing import List, Set, Tuple
from math import inf
from argparse import ArgumentParser
import time
from itertools import combinations

@dataclass
class Valve:
    flowr: int
    direct: list
    distance: dict

def bad_bfs(valves: dict, starter: str):
    to_check = [starter]

    distances = dict()
    for k in valves.keys():
        if k == starter:
            distances.update({k: 0})
        else:
            distances.update({k: inf})

    while to_check:
        current = to_check.pop(0)
        for nbr in valves[current].direct:
            if distances[nbr] == inf:
                to_check.append(nbr)
            if distances[nbr] > distances[current] + 1:
                distances[nbr] = distances[current] + 1

    valves[starter].distance = distances

def parse_input(lines):
    valves = dict()
    # some dirty parsing to get all valves
    for line in lines:
        name = line[6:8]
        flowrate = int(line[line.index('=') + 1: line.index(';')])
        if 'valves' in line:
            direct = [neighbour.strip() for neighbour in line[line.index('valves') + 7:].split(', ')]
        else:
            direct = [line[line.index('valve') + 6: line.index('valve') + 8]]
        valves.update({name: Valve(flowrate, direct, dict())})
    return valves

if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument('-i', '--input', type=str, default='test.txt')
    parser.add_argument('-c', '--challenge', type=int, default=1)
    args = parser.parse_args()

    with open(args.input, 'r') as inp:
        lines = [line.strip() for line in inp.readlines()]

        '''
        IDEA:
            - get distances for each valve to all other valves
            - simplify graph by removing all 0 flow-rate nodes
            - brute force it
        '''
        tp0 = time.time_ns()
        valves = parse_input(lines)
        tp = (time.time_ns() - tp0) / 10**9
        print(f"Time parsing: {tp: .5f}s")

        # get all distances
        td0 = time.time_ns()
        for vname in valves.keys():
            bad_bfs(valves, vname)
        td = (time.time_ns() - td0) / 10**9
        print(f"Time for distances: {td: .5f}s")

        # remove all 0 flows except AA
        print(f"Total valves = {len(valves)}")
        discard = list()
        for vname in valves.keys():
            if vname != 'AA' and valves[vname].flowr == 0:
                discard.append(vname)
        print(f"Discarding {len(discard)} valves with 0 flow")
        for d in discard:
            valves.pop(d)
        for vname in valves.keys():
            for d in discard:
                valves[vname].distance.pop(d)
        relevant_valve_count = len(valves)
        print(f"Remaining valves {relevant_valve_count}")

        if args.challenge == 1:
            # brute force challenge 1
            def max_release(position: str, remaining_minutes: int, already_open: List[str]) -> int:
                if remaining_minutes <= 0:
                    return 0
                release_from_this = remaining_minutes * valves[position].flowr
                options = list(filter(lambda v: v not in already_open and v != position and v != 'AA', valves.keys()))
                if not options:
                    return release_from_this

                possibilities = [max_release(next, remaining_minutes - valves[position].distance[next] - 1, already_open.copy() + [position]) for next in options]
                return max(possibilities) + release_from_this

            ts0 = time.time_ns()
            solution = max_release('AA', 30, [])
            ts = (time.time_ns() - ts0) / 10**9
            print(f"Time to solve: {ts: .5f}s")
        else:
            # brute force with the elephant helper
            def max_release_el(position_me: str, position_el: str, worktime_me: int, worktime_el: int, remaining_minutes: int, already_targeted: List[str], h) -> Tuple[int, List]:
                if remaining_minutes <= 0:
                    return 0, []

                # one of the worktimes has to be 0 always (either I or elephant opened valve)
                if worktime_me != 0 and worktime_el != 0:
                    raise Exception("You implemented some dumb stuff. worktimes both nonzero")

                # am I or the elephant opening a valve this minute
                release_me = remaining_minutes * valves[position_me].flowr if worktime_me == 0 else 0
                release_el = remaining_minutes * valves[position_el].flowr if worktime_el == 0 else 0
                release_from_this = release_el + release_me

                options = list(filter(lambda v: v not in already_targeted and v != position_me and v != position_el and v != 'AA', valves.keys()))

                if release_me > 0:
                    h.append(f"I open valve {position_me} with {remaining_minutes} minutes remaining for {release_me} pressure. Options: {options}")
                if release_el > 0:
                    h.append(f"The elephant opens valve {position_el} with {remaining_minutes} minutes remaining for {release_el} pressure. Options: {options}")

                # without any decisions remaining calculate release for this configuration
                if not options:
                    if worktime_me > 0 and remaining_minutes - worktime_me > 0:
                        future_release_me = (remaining_minutes - worktime_me) * valves[position_me].flowr
                        h.append(f"Lastly I open valve {position_me} with {remaining_minutes - worktime_me} minutes remaining for {future_release_me} pressure.")
                    else:
                        future_release_me = 0
                    if worktime_el > 0 and remaining_minutes - worktime_el > 0:
                        future_release_el = (remaining_minutes - worktime_el) * valves[position_el].flowr
                        h.append(f"Lastly the elephant opens valve {position_el} with {remaining_minutes - worktime_el} minutes remaining for {future_release_el} pressure.")
                    else:
                        future_release_el = 0
                    return release_from_this + future_release_me + future_release_el, h

                # check who needs a new target valve (position)
                if worktime_me == 0 and worktime_el == 0:
                    # new target for both
                    if len(options) == 1:
                        # can only open one more valve
                        last_target = options.pop(0)
                        min_dist = min(valves[position_me].distance[last_target], valves[position_el].distance[last_target])
                        from_last = max((remaining_minutes - (min_dist + 1)) * valves[last_target].flowr, 0)
                        h.append(f"One of us opens valve {last_target} with {remaining_minutes - min_dist - 1} minutes remaining.")
                        return release_from_this + from_last, h

                    target_combinations = list(combinations(options, 2))
                    best_case, h_best = -1, []
                    for t_el, t_me in target_combinations:
                        new_worktime_me = valves[position_me].distance[t_me] + 1
                        new_worktime_el = valves[position_el].distance[t_el] + 1
                        next_choice_in = min(new_worktime_me, new_worktime_el)

                        comb_solution, h_new = max_release_el(t_me, t_el,
                                                       new_worktime_me - next_choice_in,
                                                       new_worktime_el - next_choice_in,
                                                       remaining_minutes - next_choice_in,
                                                       already_targeted.copy() + [t_me, t_el],
                                                       h.copy())

                        if comb_solution >= best_case:
                            best_case, h_best = comb_solution, h_new
                    return best_case + release_from_this, h_best

                elif worktime_me == 0 and worktime_el != 0:
                    # new target valve for me
                    best_case, h_best = -1, []
                    for t_me in options:
                        new_worktime_me = valves[position_me].distance[t_me] + 1
                        next_choice_in = min(new_worktime_me, worktime_el)
                        target_solution, h_new = max_release_el(t_me, position_el,
                                                         new_worktime_me - next_choice_in,
                                                         worktime_el - next_choice_in,
                                                         remaining_minutes - next_choice_in,
                                                         already_targeted.copy() + [t_me],
                                                         h.copy())
                        if target_solution >= best_case:
                            best_case, h_best = target_solution, h_new
                    return best_case + release_from_this, h_best
                elif worktime_me != 0 and worktime_el == 0:
                    # new target valve for el
                    best_case, h_best = -1, []
                    for t_el in options:
                        new_worktime_el = valves[position_el].distance[t_el] + 1
                        next_choice_in = min(worktime_me, new_worktime_el)
                        target_solution, h_new = max_release_el(position_me, t_el,
                                                         worktime_me - next_choice_in,
                                                         new_worktime_el - next_choice_in,
                                                         remaining_minutes - next_choice_in,
                                                         already_targeted.copy() + [t_el],
                                                         h.copy())
                        if target_solution >= best_case:
                            best_case, h_best = target_solution, h_new
                    return best_case + release_from_this, h_best
                else:
                    raise Exception("Worktime Error")

            ts0 = time.time_ns()
            solution, hist = max_release_el('AA', 'AA', 0, 0, 26, [], [])
            ts = (time.time_ns() - ts0) / 10 ** 9
            print(f"Time to solve: {ts: .5f}s")
            for moment in hist:
                print(moment)

        print(f"Challenge {args.challenge}: {solution}")


import numpy as np
import math

from pysc2.lib import actions as sc_action
from pysc2.lib import static_data
from pysc2.lib import units
from s2clientprotocol.raw_pb2 import Unit

class Translator(object):
    """description of class"""
    def crop_feature_layers(self, stencil, featureLayers, new_width, new_height):
        newFeature = []
        for layer in featureLayers:
            input = layer
            newInput = np.zeros((new_height,new_width),int)
            counterx = 0
            countery = 0
            for numy, y in enumerate(stencil):
                for numx, x in enumerate(y):
                    if (x != 0):
                        newInput[countery][counterx] = input[numy][numx]
                        counterx+=1
                    if (counterx == new_width):
                        countery+=1
                        counterx=0
            newFeature.append(newInput)
        return newFeature

    def translate_feature_layers(self, featurelayers):
        #This forces unit type to be defined by their index and then between 0 and 1
        unit_type = featurelayers[6]
        unit_type_compressed = np.zeros(featurelayers[6].shape, dtype=np.float)
        for y in range(len(featurelayers[6])):
            for x in range(len(featurelayers[6][y])):
                if unit_type[y][x] > 0 and unit_type[y][x] in static_data.UNIT_TYPES:
                    unit_type_compressed[y][x] = static_data.UNIT_TYPES.index(unit_type[y][x]) / len(static_data.UNIT_TYPES)

        newFeatureLayers = [
             featurelayers[0] / 255,               # height_map 0 - 256
             featurelayers[1] / 2,                 # visibility 0 - 2
             featurelayers[2],                     # creep 0 - 1
            #featurelayers[3]                      # power 0 - 1
             (featurelayers[5] == 1).astype(int),  # own_units 0 - 1
             (featurelayers[5] == 3).astype(int),  # neutral_units 0 - 1
             (featurelayers[5] == 4).astype(int),  # enemy_units 0 - 1
             unit_type_compressed,                 # unit types 0 - 1 << Categorical                                                                                # unit_type
             featurelayers[7],                     # selected 0 - 1
             featurelayers[9] / 255,               # unit_hit_points_ratio 0 - 256                                                                                # hit_points
             featurelayers[11] / 255,              # energy ratio 0 - 256
             featurelayers[13] / 255,              # shields ratio 0 - 256
             featurelayers[14],                    # unit_density 0 - 16
             featurelayers[16]                     # effects 0 - 16 << Categorical
        ]
        return newFeatureLayers

    #[0 "height_map",
    # 1 "visibility_map",
    # 2 "creep",
    # 3 "power",
    # 4 "player_id",
    # 5 "player_relative",
    # 6 "unit_type",
    # 7 "selected",
    # 8 "unit_hit_points",
    # 9 "unit_hit_points_ratio",
    # 10"unit_energy",
    # 11"unit_energy_ratio",
    # 12"unit_shields",
    # 13"unit_shields_ratio",
    # 14"unit_density",
    # 15"unit_density_aa",
    # 16"effects"]

#class Zerg(enum.IntEnum):
#  """Zerg units."""
#  Baneling = 9
#  BanelingBurrowed = 115
#  BanelingCocoon = 8
#  BanelingNest = 96
#  BroodLord = 114
#  BroodLordCocoon = 113
#  Broodling = 289
#  BroodlingEscort = 143
#  Changeling = 12
#  ChangelingMarine = 15
#  ChangelingMarineShield = 14
#  ChangelingZealot = 13
#  ChangelingZergling = 17
#  ChangelingZerglingWings = 16
#  Corruptor = 112
#  CreepTumor = 87
#  CreepTumorBurrowed = 137
#  CreepTumorQueen = 138
#  Drone = 104
#  DroneBurrowed = 116
#  Cocoon = 103
#  EvolutionChamber = 90
#  Extractor = 88
#  GreaterSpire = 102
#  Hatchery = 86
#  Hive = 101
#  Hydralisk = 107
#  HydraliskBurrowed = 117
#  HydraliskDen = 91
#  InfestationPit = 94
#  InfestedTerran = 7
#  InfestedTerranBurrowed = 120
#  InfestedTerranCocoon = 150
#  Infestor = 111
#  InfestorBurrowed = 127
#  Lair = 100
#  Larva = 151
#  Locust = 489
#  LocustFlying = 693
#  Lurker = 502
#  LurkerBurrowed = 503
#  LurkerDen = 504
#  LurkerCocoon = 501
#  Mutalisk = 108
#  NydusCanal = 142
#  NydusNetwork = 95
#  Overlord = 106
#  OverlordTransport = 893
#  OverlordTransportCocoon = 892
#  Overseer = 129
#  OverseerCocoon = 128
#  OverseerOversightMode = 1912
#  ParasiticBombDummy = 824
#  Queen = 126
#  QueenBurrowed = 125
#  Ravager = 688
#  RavagerBurrowed = 690
#  RavagerCocoon = 687
#  Roach = 110
#  RoachBurrowed = 118
#  RoachWarren = 97
#  SpawningPool = 89
#  SpineCrawler = 98
#  SpineCrawlerUprooted = 139
#  Spire = 92
#  SporeCrawler = 99
#  SporeCrawlerUprooted = 140
#  SwarmHost = 494
#  SwarmHostBurrowed = 493
#  Ultralisk = 109
#  UltraliskBurrowed = 131
#  UltraliskCavern = 93
#  Viper = 499
#  Zergling = 105
#  ZerglingBurrowed = 119


# Connector will connect from a component's output, to the next component's input.
class Connector :
    def __init__(self, owner, name, activates=0, monitor=0) :
        self.value = None
        self.owner = owner
        self.name = name
        self.activates = activates
        self.monitor = monitor
        self.connects = []

    def connect(self, inputs) :
        # ensure that inputs is a list
        if not isinstance(inputs, list):
            inputs = [inputs]
        for input in inputs:
            self.connects.append(input)

    def set(self, value):
        # commenting these lines will result in slower computation. However, it fixes the issue of some values not getting set.
        if self.value == value:
            return
        self.value = value
        if self.activates:
            self.owner.evaluate()
        if self.monitor and (self.value != None):
            # set the new value of the values dict
            # self.owner.values[self.name] = self.value
            print("{0}-{1} set to {2}".format(self.owner.name, self.name, self.value))
        for con in self.connects:
            if value == True:
                value = 1
            elif value == False:
                value = 0
            con.set(value)

class LogicCircuit :
    def __init__(self, name) :
        self.name = name

    def evaluate(self):
        return

class Gate(LogicCircuit) :
    def __init__(self, name) :
        LogicCircuit.__init__(self, name)
        self.A = Connector(self, "A", activates=1)
        self.B = Connector(self, "B", activates=1)
        self.C = Connector(self, "C")

class Not(LogicCircuit) :
    def __init__(self, name)  :
        LogicCircuit.__init__(self, name)
        self.A = Connector(self, "A", activates=1)
        self.B = Connector(self, "B")

    def evaluate(self) :
        boolValue = self.A.value
        if boolValue == True or boolValue == 1:
            self.B.set(0)
        elif boolValue == False or boolValue == 0:
            self.B.set(1)
        elif boolValue == None:
            self.B.set(None)

"""
              xxxxxxxxx
A             x        xx
 xxxxxxxxxxxxxx         xx
              x          x xxxxxxxxx
 xxxxxxxxxxxxxx         xx          C
B             x        xx
              xxxxxxxxx

"""
class And(Gate) :
    def __init__(self, name)  :
        Gate.__init__(self, name)

    def evaluate(self) :
        self.C.set(self.A.value and self.B.value)

"""

A             xxxxxxxxxx
 xxxxxxxxxxxxxxx        x
                x        x
                x        x xxxxxxxxxx
                x        x           C
 xxxxxxxxxxxxx x        x
B             xxxxxxxxxx

"""
class Or(Gate):
    def __init__(self, name)  :
        Gate.__init__(self, name)

    def evaluate(self):
        self.C.set(self.A.value or self.B.value)

"""

A             x xxxxxxxx
 xxxxxxxxxxxxx x x      x
                x x      x
                x x      x xxxxxxxxxx
                x x      x           C
 xxxxxxxxxxxxx x x      x
B             x xxxxxxxx

"""
class Xor(Gate):
    def __init__(self, name):
        Gate.__init__(self, name)
        self.A1 = And("A1")
        self.A2 = And("A2")
        self.N1 = Not("N1")
        self.N2 = Not("N2")
        self.O1 = Or("O1")
        self.A.connect([self.A1.A, self.N2.A])
        self.B.connect([self.N1.A, self.A2.A])
        self.N1.B.connect([self.A1.B])
        self.N2.B.connect([self.A2.B])
        self.A1.C.connect([self.O1.A])
        self.A2.C.connect([self.O1.B])
        self.O1.C.connect([self.C])

class HalfAdder(LogicCircuit) :
    def __init__(self, name)  :
        LogicCircuit.__init__(self, name)
        self.A = Connector(self, "A", activates=1)
        self.B = Connector(self, "B", activates=1)
        self.S = Connector(self, "S")
        self.C = Connector(self, "C")
        self.X1 = Xor("X1")
        self.A1 = And("A1")
        self.A.connect([self.X1.A, self.A1.A])
        self.B.connect([self.X1.B, self.A1.B])
        self.X1.C.connect([self.S])
        self.A1.C.connect([self.C])

# 2 half adders and 1 or
class FullAdder(LogicCircuit) :
    def __init__(self, name) :
        LogicCircuit.__init__(self, name)
        self.Cin = Connector(self, "Cin", activates=1, monitor=1)
        self.A = Connector(self, "A", activates=1, monitor=1)
        self.B = Connector(self, "B", activates=1, monitor=1)
        self.Cout = Connector(self, "Cout", monitor=1)
        self.S = Connector(self, "S", monitor=1)
        self.H1 = HalfAdder("H1")
        self.H2 = HalfAdder("H2")
        self.O1 = Or("O1")
        self.Cin.connect([self.H2.A])
        self.A.connect([self.H1.A])
        self.B.connect([self.H1.B])
        self.H1.S.connect(self.H2.B)
        self.H1.C.connect(self.O1.B)
        self.H2.S.connect(self.S)
        self.H2.C.connect(self.O1.A)
        self.O1.C.connect(self.Cout)


import os.path
import shutil
import sysconfig
from pathlib import Path
import time

from setuptools import setup
from setuptools.command.install import install
from setuptools.command.sdist import sdist
from wheel.bdist_wheel import bdist_wheel

home_dir = Path.home()
install_dir = sysconfig.get_paths()['purelib']
print(home_dir)
print(install_dir)

current = Path(__file__).parent.absolute()

VERSION = "0.0.5"

def generate_sumake(home_dir, current):
    sumake_dir = home_dir / ".sumake"
    bin_dir = current / "bin"

    utils_mk = sumake_dir / "utils.mk"

    if not os.path.exists(sumake_dir):
        os.makedirs(sumake_dir)
    if not os.path.exists(bin_dir):
        os.makedirs(bin_dir)
    print("current", current)

    shutil.copy(current / "utils.mk", utils_mk)
    # open("./sumake")
    with open(current / "./sumake", "r") as template:
        with open(bin_dir / "sumake", "w") as target:
            res = template.read().format(
                UTILS_MK=utils_mk,
                VERSION=VERSION, )
            target.write(res)

def generate_version():
    with open(current / "version.txt", "w") as f:
        f.write(VERSION)
        # f.write(f"""VERSION = "{VERSION}" """)

class PreBdistWheel(bdist_wheel):
    def run(self):
        generate_sumake(home_dir, current)
        bdist_wheel.run(self)

class PreSDist(sdist):
    def run(self):
        generate_sumake(home_dir, current)
        sdist.run(self)

class PreInstall(install):
    def run(self):
        generate_sumake(home_dir, current)
        install.run(self)

if __name__ == '__main__':
    generate_version()
    setup(
        author="SuCicada",
        author_email="pengyifu@gmail.com",
        classifiers=[],
        description="A generic makefile for projects.",
        scripts=["bin/sumake"],
        cmdclass={
            "install": PreInstall,
            "bdist_wheel": PreBdistWheel,
            "sdist": PreSDist,
        },
        install_requires=["setuptools"],

        include_package_data=True,
        # package_data={
        #     '': ['sumake', 'utils.mk'],
        # },
        # install_requires=[],
        # keywords="",
        # license="",
        long_description=open("README.md").read(),
        name="sumake",
        # namespace_packages=[],
        # packages=find_packages(),
        # py_modules=["."],
        # test_suite="",
        url="https://github.com/SuCicada/sumake",
        version=VERSION,
        zip_safe=False,
    )


"""
interactive.py - helper functions for Splendor interactive mode.
"""

import sys
from typing import (
        Any,
        Tuple,
        Type,
        )

import logging
logging.basicConfig(level=logging.INFO)

def prompt_yn(
        prompt: str="",
        out=sys.stdout,
        ) -> bool:
    """
    """
    if prompt != "":
        prompt += " (y/n): "
    while True:
        try:
            text_in = input(prompt)
        except EOFError as e:
            raise e
        text_in_cleaned = text_in.strip().upper()
        if text_in_cleaned == 'Y':
            return True
        elif text_in_cleaned == 'N':
            return False
        else:
            print("error: must enter 'y' or 'n'", file=out)
            continue

def prompt_number(
        prompt: str="",
        typ: Type=int,
        input_range: Tuple=None,
        out=sys.stdout,
        ) -> int:
    """
    Prompt the user for a number (int or float).

    If range (e.g. (3, 6)) is specified, the user-inputted int must be between the specified min and max, inclusive.
    """
    if typ != int and typ != float:
        raise Exception("type must be int or float")

    if input_range != None:
        if type(input_range) != tuple:
            raise Exception("input range must be a tuple")
        if len(input_range) != 2:
            raise Exception("input range be in the form (min, max)")
        if type(input_range[0]) != typ or type(input_range[1]) != typ:
            raise Exception(f"input range values must be {typ.__name__}s")
        if input_range[0] > input_range[1]:
            raise Exception("input range min must be less than or equal to the max")

    if prompt != "":
        prompt += f" ({typ.__name__}" + \
            ((" in range " + str(input_range)) if input_range != None else "") + \
            "): "

    while True:
        try:
            text_in = input(prompt)
        except EOFError as e:
            raise e
        text_in_cleaned = text_in.strip()

        try:
            text_in_casted = typ(text_in_cleaned)
        except ValueError:
            print(f"'{text_in_cleaned}' is not an {typ.__name__}", file=out)
            continue
        if input_range != None:
            if text_in_casted < input_range[0] or text_in_casted > input_range[1]:
                print(f"error: input must be between {input_range[0]} and {input_range[1]}, inclusive", file=out)
                continue

        return text_in_casted

def prompt_string(
        prompt: str="",
        max_len: int=255,
        out=sys.stdout,
        ) -> int:
    """
    Prompt the user for a string.
    """
    if prompt != "":
        prompt += " (string): "

    while True:
        try:
            text_in = input(prompt)
        except EOFError as e:
            raise e
        text_in_cleaned = text_in.strip()
        if text_in_cleaned == "":
            print("error: empty string", file=out)
            continue
        if len(text_in_cleaned) > max_len:
            print("error: too long", file=out)
            continue
        return text_in_cleaned


from neo4j.v1 import GraphDatabase

driver = GraphDatabase.driver("bolt://localhost", auth=("neo4j", "neo"))

with driver.session() as session:
    #     result = session.run("""\
    #     CALL db.propertyKeys()
    #     YIELD propertyKey
    #     WHERE propertyKey STARTS WITH "embedding_" AND not(propertyKey CONTAINS "lambda")
    #     RETURN propertyKey
    #     """)

    # property_keys = [row["propertyKey"] for row in result]
    property_keys = ["embedding_lpa"]

    # f1 f2 f3 f4 f5 .. fn label

    for property_key in property_keys:
        with open("emails/{0}-emails.txt".format(property_key), "w") as property_key_file:
            print("Property Key: ", property_key)
            result = session.run("""\
            // MATCH (enzyme:Person)
            // RETURN enzyme[$propertyKey] AS embedding, enzyme.label AS label
            MATCH (n:Node)
            WITH n.label as class, count(*) AS c
            ORDER BY c DESC
            WITH class WHERE c > 50
            WITH class ORDER BY class
            with collect(class) AS biggestClasses
            MATCH (p:Node) WHERE p.label IN biggestClasses
            RETURN p.`embedding-python` AS embedding, apoc.coll.indexOf(biggestClasses, p.label) AS label, p.label as initialLabel
            ORDER BY label
            """, {"propertyKey": property_key})

            for row in result:
                property_key_file.write(
                    "{0} {1}\n".format(" ".join([str(item) for item in row["embedding"]]), row["label"]))


import pytest
from httpx_backoff.backoff_options import Fibo

class TestBackoffFiboOption:
    @pytest.mark.parametrize(
        "expected",
        [[1, 1, 2, 3, 5, 8, 13], [1, 1, 2, 3, 5, 8, 13, 21, 34]],
    )
    def test_backoff_fibo_with_default_args(self, expected):
        fibo_gen = Fibo()

        for expect in expected:
            assert expect == next(fibo_gen)

    @pytest.mark.parametrize(
        "max_value,expected",
        [(8, [1, 1, 2, 3, 5, 8, 8, 8]), (9, [1, 1, 2, 3, 5, 8, 9]), (16, [1, 1, 2, 3, 5, 8, 13, 16])],
    )
    def test_backoff_fibo_with_max_value(self, max_value, expected):
        fibo_gen = Fibo(max_value=max_value)

        for expect in expected:
            assert expect == next(fibo_gen)