import sys
sys.dont_write_bytecode = True

from PyQt4 import QtCore, QtGui

import lib.gui.settings
import lib.settings
import stations
import lib.database

class MainWindow(QtGui.QMainWindow, lib.gui.settings.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        # inherited from Ui_MainWindow
        self.setupUi(self)
        self.setConnections()
        self.parent = parent

    def main(self):
        # access to parent's variables (main window's variables)
        self.parent.settings = self.parent.db_scisola.loadSettings(
                                                         self.parent.settings)
        if self.parent.settings:
            self.settings2GUI(self.parent.settings)

        # center window
        frame = self.frameGeometry()
        frame.moveCenter(QtGui.QDesktopWidget().availableGeometry().center())
        self.move(frame.topLeft())
        self.showMaximized()

    def closeEvent(self, event):
        self.parent.main()

    def setConnections(self):
        # right arrow -add- (distance selection)
        self.toolButton.clicked.connect(self.toolButtonClicked)
        # left arrow -remove- (distance selection)
        self.toolButton_2.clicked.connect(self.toolButton_2Clicked)
        # right arrow -add- (inversion time)
        self.toolButton_3.clicked.connect(self.toolButton_3Clicked)
        # right arrow -remove- (inversion time)
        self.toolButton_4.clicked.connect(self.toolButton_4Clicked)
        # right arrow -add- (inversion frequency)
        self.toolButton_5.clicked.connect(self.toolButton_5Clicked)
        # right arrow -remove- (inversion frequency)
        self.toolButton_6.clicked.connect(self.toolButton_6Clicked)
        # edit stations button
        self.pushButton_17.clicked.connect(self.editStationsButtonClicked)
        # apply settings
        self.pushButton.clicked.connect(self.applyButtonClicked)
        # browse button
        self.pushButton_14.clicked.connect(self.browseFileOnClicked_14)
        self.pushButton_2.clicked.connect(self.browseFileOnClicked_2)
        self.pushButton_20.clicked.connect(self.browseFileOnClicked_20)
        self.pushButton_18.clicked.connect(self.browseFileOnClicked_18)
        self.pushButton_16.clicked.connect(self.browseFileOnClicked_16)
        self.pushButton_19.clicked.connect(self.browseFileOnClicked_19)
        self.pushButton_9.clicked.connect(self.browseFileOnClicked_9)
        # update button
        self.pushButton_13.clicked.connect(self.updateButtonClicked)

    # browse functions
    def browseFileOnClicked_14(self):
        _text = QtGui.QFileDialog.getOpenFileName()
        if _text:
            self.lineEdit_19.setText(_text)

    def browseFileOnClicked_2(self):
        _text = QtGui.QFileDialog.getExistingDirectory()
        if _text:
            self.lineEdit_2.setText(_text)

    def browseFileOnClicked_20(self):
        _text = QtGui.QFileDialog.getOpenFileName()
        if _text:
            self.lineEdit_24.setText(_text)

    def browseFileOnClicked_18(self):
        _text = QtGui.QFileDialog.getOpenFileName()
        if _text:
            self.lineEdit_21.setText(_text)

    def browseFileOnClicked_16(self):
        _text = QtGui.QFileDialog.getOpenFileName()
        if _text:
            self.lineEdit_20.setText(_text)

    def browseFileOnClicked_19(self):
        _text = QtGui.QFileDialog.getOpenFileName()
        if _text:
            self.lineEdit_22.setText(_text)

    def browseFileOnClicked_9(self):
        _text = QtGui.QFileDialog.getExistingDirectory()
        if _text:
            self.lineEdit_3.setText(_text)

    def toolButtonClicked(self):
        _mag_min = self.doubleSpinBox.text()
        _mag_max = self.doubleSpinBox_2.text()
        _dist_min = self.spinBox_6.text()
        _dist_max = self.spinBox_8.text()
        _value = _mag_min + " <= magnitude <= " + _mag_max + " and " + \
                 _dist_min + " <= distance <= " + _dist_max
        self.listWidget.addItem(_value)

    def toolButton_2Clicked(self):
        self.listWidget.takeItem(self.listWidget.currentRow())

    def toolButton_3Clicked(self):
        _mag_min = self.doubleSpinBox_3.text()
        _mag_max = self.doubleSpinBox_5.text()
        _tl = self.comboBox_2.currentText()
        _value = _mag_min + " <= magnitude <= " + _mag_max + " and tl = " + \
                 _tl
        self.listWidget_2.addItem(_value)

    def toolButton_4Clicked(self):
        self.listWidget_2.takeItem(self.listWidget_2.currentRow())

    def toolButton_5Clicked(self):
        _mag_min = self.doubleSpinBox_9.text()
        _mag_max = self.doubleSpinBox_12.text()
        _f1 = self.doubleSpinBox_13.text()
        _f2 = self.doubleSpinBox_14.text()
        _f3 = self.doubleSpinBox_18.text()
        _f4 = self.doubleSpinBox_19.text()
        _value = _mag_min + " <= magnitude <= " + _mag_max + " and " + \
                 "frequencies = [" + _f1 + ", " + _f2 + ", " + _f3 + ", " + \
                 _f4 + "]"
        self.listWidget_3.addItem(_value)

    def toolButton_6Clicked(self):
        self.listWidget_3.takeItem(self.listWidget_3.currentRow())

    def editStationsButtonClicked(self):
        sta_win = stations.StationsWindow(self)
        sta_win.main()
        self.hide()

    def GUI2Settings(self):
        settings = lib.settings.Settings()
        settings.center_latitude = float(self.lineEdit_15.text())
        settings.center_longitude = float(self.lineEdit_14.text())
        settings.distance_range = int(self.spinBox_5.value())
        settings.magnitude_threshold = float(self.doubleSpinBox_4.value())
        settings.min_sectors = int(self.comboBox.currentText())
        settings.stations_per_sector = int(self.spinBox_7.value())
        settings.sources = int(self.spinBox_13.value())
        settings.source_step = int(self.spinBox_14.value())
        settings.clipping_threshold = float(self.doubleSpinBox_8.value())
        settings.time_grid_start = int(self.lineEdit_17.text())
        settings.time_grid_step = int(self.spinBox_15.value())
        settings.time_grid_end = int(self.lineEdit_18.text())
        settings.watch_interval = int(self.spinBox_2.value())
        settings.process_delay = int(self.spinBox_3.value())
        settings.process_timeout = int(self.spinBox_4.value())
        settings.crustal_model_path = str(self.lineEdit_19.text())
        settings.output_dir = str(self.lineEdit_2.text())
        settings.isola_path = str(self.lineEdit_3.text())
        settings.sc3_path = str(self.lineEdit_24.text())
        settings.sc3_scevtls = str(self.lineEdit_21.text())
        settings.sc3_scxmldump = str(self.lineEdit_20.text())
        settings.seedlink_path = str(self.lineEdit_22.text())
        settings.seedlink_host = str(self.lineEdit_23.text())
        settings.seedlink_port = int(self.spinBox_9.value())

         # distance selection list
        for i in xrange(self.listWidget.count()):
            _row = str(self.listWidget.item(i).text()).split(' ')
            settings.distance_selection.append([float(_row[0]),
                                                float(_row[4]),
                                                int(_row[6]),
                                                int(_row[10])
                                                ])

         # inversion time list
        for i in xrange(self.listWidget_2.count()):
            _row = str(self.listWidget_2.item(i).text()).split(' ')
            settings.inversion_time.append([float(_row[0]),
                                            float(_row[4]),
                                            float(_row[8])
                                            ])

         # inversion frequency list
        for i in xrange(self.listWidget_3.count()):
            _row = str(self.listWidget_3.item(i).text()).split(' ')
            settings.inversion_frequency.append([float(_row[0]),
                                                 float(_row[4]),
                                                 float(str(_row[8])[1:-1]),
                                                 float(str(_row[9])[:-1]),
                                                 float(str(_row[10])[:-1]),
                                                 float(str(_row[11])[:-1])
                                                 ])

        return settings

    def settings2GUI(self, settings):
        self.lineEdit_15.setText(str(settings.center_latitude))
        self.lineEdit_14.setText(str(settings.center_longitude))
        self.spinBox_5.setValue(int(settings.distance_range))
        self.doubleSpinBox_4.setValue(float(settings.magnitude_threshold))
        _index = self.comboBox.findText(str(settings.min_sectors))
        self.comboBox.setCurrentIndex(_index)
        self.spinBox_7.setValue(int(settings.stations_per_sector))
        self.spinBox_13.setValue(int(settings.sources))
        self.spinBox_14.setValue(int(settings.source_step))
        self.doubleSpinBox_8.setValue(float(settings.clipping_threshold))
        self.lineEdit_17.setText(str(settings.time_grid_start))
        self.spinBox_15.setValue(int(settings.time_grid_step))
        self.lineEdit_18.setText(str(settings.time_grid_end))
        self.spinBox_2.setValue(int(settings.watch_interval))
        self.spinBox_3.setValue(int(settings.process_delay))
        self.spinBox_4.setValue(int(settings.process_timeout))
        self.lineEdit_19.setText(str(settings.crustal_model_path))
        self.lineEdit_2.setText(str(settings.output_dir))
        self.lineEdit_3.setText(str(settings.isola_path))
        self.lineEdit_24.setText(str(settings.sc3_path))
        self.lineEdit_21.setText(str(settings.sc3_scevtls))
        self.lineEdit_20.setText(str(settings.sc3_scxmldump))
        self.lineEdit_22.setText(str(settings.seedlink_path))
        self.lineEdit_23.setText(str(settings.seedlink_host))
        self.spinBox_9.setValue(int(settings.seedlink_port))
        self.label_4.setText(str(settings.timestamp))
        self.listWidget.clear()
        self.listWidget_2.clear()
        self.listWidget_3.clear()

        for _entry in settings.distance_selection:
            _value = str(_entry[0]) + " <= magnitude <= " + str(_entry[1]) + \
                     " and " + str(_entry[2]) + " <= distance <= " + \
                     str(_entry[3])

            self.listWidget.addItem(_value)

        for _entry in settings.inversion_time:
            _value = str(_entry[0]) + " <= magnitude <= " + str(_entry[1]) + \
                     " and tl = " + str(_entry[2])

            self.listWidget_2.addItem(_value)

        for _entry in settings.inversion_frequency:
            _value = str(_entry[0]) + " <= magnitude <= " + str(_entry[1]) + \
                     " and frequencies = [" + str(_entry[2]) + ", " + \
                     str(_entry[3]) + ", " + str(_entry[4]) + ", " + \
                     str(_entry[5]) + "]"

            self.listWidget_3.addItem(_value)

        self.label_4.setText(str(settings.timestamp))

    # apply button
    def applyButtonClicked(self):
        self.__settings = self.GUI2Settings()
        self.setEnabled(False)
        _message = self.parent.db_scisola.saveSettings(self.__settings)

        if not _message:
            QtGui.QMessageBox.information(self, 'Saving to scisola',
                                          'Settings successfully applied.')
            self.parent.master_log.info("Settings successfully applied.")
            # apply new settings to system
            self.parent.settings = self.__settings
            # apply new settings to watcher
            if self.parent.watcher:
                self.parent.watcher.setConf(self.parent.settings)
                self.parent.watcher.restart()

        else:
            QtGui.QMessageBox.critical(self, 'Saving to scisola',
                                       "Could not save settings.\nError:\n" +\
                                       str(_message[:2]))
        self.setEnabled(True)

    # update button
    def updateButtonClicked(self):
        if self.checkBox.checkState() == QtCore.Qt.Checked:
            _reset = True
        else:
            _reset = False

        self.setEnabled(False)
        _message = self.parent.db_scisola.importFromSc3(self.parent.db_sc3,
                                                        reset=_reset)

        if not _message:
            QtGui.QMessageBox.information(self, 'Saving to scisola',
                                          'Stations successfully updated.')
            self.parent.master_log.info("Stations successfully updated.")
        else:
            QtGui.QMessageBox.critical(self, 'Saving to scisola',
                                   "Could not update stations.\nError:\n" +\
                                   str(_message[:2]))
        self.setEnabled(True)


from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from database_setup import Base, Location, HikingTrail

engine = create_engine('sqlite:///hiking.db')
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()

location1 = Location(country='Peru')
session.add(location1)
session.commit()

location2 = Location(country='Spain')
session.add(location2)
session.commit()

location3 = Location(country='France')
session.add(location3)
session.commit()

location4 = Location(country='United States')
session.add(location4)
session.commit()

hikingEntry1 = HikingTrail(trailName='Inca Trail to Machu Picchu',
						description='Inca Trail to Machu Picchu consists of three overlapping trails: Mollepata, Classic, and One Day.',
						website='http://www.incatrail.org/',
						province='Various',
						park='Machu Picchu',
						location=location1)
session.add(hikingEntry1)
session.commit()

hikingEntry2 = HikingTrail(trailName='The Cares Route',
						description='The Cares Route is in the heart of Picos de Europa.',
						website='https://www.larutadelcares.es/en/ruta/',
						province='Leon',
						park='Picos de Europa',
						location=location2)
session.add(hikingEntry2)
session.commit()

hikingEntry3 = HikingTrail(trailName='Le Chemin des Rognes',
						description='Le Chemin des Rognes is a challenging mountainous route that begins in Bellevue and ends in Baraque des Rognes and takes three to four hours. Although it\'s difficult, this hike offers wonderful views of the Chamonix Valley, the Aiguille du Midi, and the Aravis Mountain Range.',
						website='http://www.planetware.com/france/top-rated-hiking-trails-in-france-f-1-3.htm',
						province='Courmayeur, Les Houches, and Chamonix',
						park='Tour du Mont Blanc',
						location=location3)
session.add(hikingEntry3)
session.commit()

hikingEntry4 = HikingTrail(trailName='John Muir Trail',
						description='The John Muir Trail is the premier hiking trail in the United States.The trail starts in America\'s treasure, Yosemite National Park, and continues 215 miles through the Ansel Adams Wilderness, Sequoia National Park, King\'s Canyon National Park, and ends at the highest peak in continental United States, Mount Whitney at 14,496 ft.',
						website='http://johnmuirtrail.org/index.html',
						province='California',
						park='Yosemite/Mount Whitney',
						location=location4)
session.add(hikingEntry4)
session.commit()

hikingEntry5 = HikingTrail(trailName='Continental Divide Trail',
						description='The John Muir Trail is the premier hiking trail in the United States.The trail starts in America\'s treasure, Yosemite National Park, and continues 215 miles through the Ansel Adams Wilderness, Sequoia National Park, King\'s Canyon National Park, and ends at the highest peak in continental United States, Mount Whitney at 14,496 ft.',
						website='http://www.continentaldividetrail.org',
						province='New Mexico, Colorado, Wyoming, Idaho and Montana',
						park='Rocky Mountains',
						location=location4)
session.add(hikingEntry5)
session.commit()


# coding: utf-8
# creates table-like custom objects from the items in CSV files
# and connects path for CSV to be read or written to/from
import csv
from pathlib import Path

"""The purpose of this program is to analyze loan data and
determine the fair price of the loans to ensure loans are bought
at the best possible value.

This program is organized into five parts;
Part 1: Automation of Calculations
Part 2: Analysis of Loan Data
Part 3: Financial Calculations
Part 4: Conditionally filters lists of loans
Part 5: Save results to CSV file

"""

"""Part 1 - Automation of Calculations"""

# list of loan prices
loan_costs = [500, 600, 200, 1000, 450]

# tells us how many loan prices are inside of the loan_costs list, then includes them in a statement
print("The number of loans is:", len(loan_costs))

# adds the values in the loan_costs list then includes them in a statement
print("The total value of the loans:$",sum(loan_costs))

# averages the loan cost values from previous "loan_cost" list data then includes in a stament
print("The average loan amount is:$", (sum(loan_costs) / len(loan_costs)))

""" Part 2: Analysis of Loan Data

*** Using a dictionary of additional loan information, we can determine fair price using the following
formula;

Present Value = Future Value / (1 + Discount_Rate/12) ** remaining_months

    @NOTE
        ***Future value:  the amount of money the borrower must pay back upon maturity of the
        loan (also known as the face value).

        ***Remaining months: the remaining maturity (in months) before the loan needs to be fully repaid.

        ***Fair Value: the loan is worth at least the cost to buy it.

        *** Discount Rate- 20%

"""

# dictionary of loan data

loan = {
    "loan_price": 500,
    "remaining_months": 9,
    "repayment_interval": "bullet",
    "future_value": 1000,
}

# uses get() function to extract the Future Value on the loan

print("Future Value:$", loan.get("future_value"))

# uses get() function to extract the remaining amount of months on the loan

print("Remaining Months:", loan.get("remaining_months"))

# Calculates fair value using **monthly** present value formula with 20% discount rate value

discount_rate = 0.2

present_value = loan.get("future_value") / (1 + discount_rate/12) ** loan.get("remaining_months")

# print(present_value)- **kept as test tool

# determines present value using .get function which pulls data from loan_price list
# if the loan data is less than or equal to present value it prints a statement confirming
# its a good buy, otherwise it will say the loan is not a good buy

if present_value >= loan.get("loan_price"):
    print("The loan is worth at least the cost to buy it.")

else :
    print("The loan is too expensive and not worth the price")

""" Part 3: Financial Calculations - This function can be used with any newly uploaded loan data """

# new list of loan data
new_loan = {
    "loan_price": 800,
    "remaining_months": 12,
    "repayment_interval": "bullet",
    "future_value": 1000,
}

#   Using calculate_present_value as a calculating function we give paramaters of `future_value`, `remaining_months`,
#   and the `annual_discount_rate`

#   This function gives us the `present_value` for the loan.

def calculate_present_value(future_value, remaining_months, annual_discount_rate):
    discount_rate = annual_discount_rate
    present_value = loan.get("future_value") / (1 + discount_rate/12) ** loan.get("remaining_months")
    return present_value

# This function recalculates the present value with new data using an annual discount rate of 20%

calculate_present_value(new_loan.get("future_value"),new_loan.get("remaining_months"),0.2)

# displays present value loan amount with rounding for two decimal places

print(f"The present value of the loan is:$ {round(present_value, 2)}")

"""Part 4: Conditionally filters lists of loans.

This is will filter your lists of loan prices/values based on your specifications and
take the desired data and put it into a new list for clean reading.

"""
# list of dictionaries containing loan data
loans = [
    {
        "loan_price": 700,
        "remaining_months": 9,
        "repayment_interval": "monthly",
        "future_value": 1000,
    },
    {
        "loan_price": 500,
        "remaining_months": 13,
        "repayment_interval": "bullet",
        "future_value": 1000,
    },
    {
        "loan_price": 200,
        "remaining_months": 16,
        "repayment_interval": "bullet",
        "future_value": 1000,
    },
    {
        "loan_price": 900,
        "remaining_months": 16,
        "repayment_interval": "bullet",
        "future_value": 1000,
    },
]

# empty list created to receive data that will be filtered
inexpensive_loans = []

# Loops through loan data in 'loans' list with condition of $500 or less, the
# values which meet that condition will then be uploaded into a new list called 'inexpensive_loans'

for loan in loans:
    loanPrice = loan.get("loan_price")
    if loanPrice <= 500:
        inexpensive_loans.append(loan)

# shows newly generated list with filtered results

print("The following loans are considered inexpensive:",[inexpensive_loans])

"""Part 5: Save the results.

Saves(writes) newly generated data to a CSV file"""

# Sets the output header
header = ["loan_price", "remaining_months", "repayment_interval", "future_value"]

# Sets the output file path, added r for absolute file path raw, otherwise unicode error appears

output_path = Path(r"C:\Users\brien\Desktop\FinTech_Workspace\Challenges\loan_analyzer.csv")

# loop for CSV files to be written, using 'header' variable that was created and uploading
# it to out_path (where you would like the CSV saved)

with open(output_path,'w', newline='') as output_path:
    csvwriter = csv.writer(output_path)
    csvwriter.writerow(header)
    for row in inexpensive_loans:
      csvwriter.writerow(row.values())

# You should now have a new CSV file saved in your specified folder (output_path)


import keyword

def soma(*numeros):
    total = 0
    for n in numeros:
        total += n
    return total

def resultado_final(**kwargs):
    status = 'Aprovado'
    if kwargs['nota'] >= 7:
        print(status)
    else:
        print('Reprovado')
    return f'{kwargs["nome"]} foi {status}'

print(resultado_final)


import tensorflow as tf

from dnn.models.conv.blocks import ChongTayConvLstmBottleneckBlock

def test_conv_lstm_bottleneck_forward_default():
    model = ChongTayConvLstmBottleneckBlock()
    x = tf.random.normal((1, 26, 26, 10, 64))
    out = model(x)
    assert tf.TensorShape(out.shape) == tf.TensorShape((1, 26, 26, 10, 64))


import unittest
from classifying_tree import Tree, add_product

class TestMatchingSeparators(unittest.TestCase):
    """
     When a product json
     has no defined `family` field,
     it should go under the
     undefined_family_node
    """
    def setUp(self):
        self.product_a = {
            "announced-date": "2000-02-01T19:00:00.000-05:00",
            "family": "Cyber-shot",
            "manufacturer": "Sony",
            "model": "DSC-S70",
            "product_name": "Sony_Cyber-shot_DSC-S70"
        }

        self.product_b = {
            "announced-date": "2000-02-01T19:00:00.000-05:00",
            "family": "CyberShot",
            "manufacturer": "Sony",
            "model": "DSC-S70",
            "product_name": "Sony_Cyber-shot_DSC-S70"
        }

    def test_match_dashed_with_camelcase(self):
        tree = Tree()
        node_a = add_product(tree, self.product_a)
        node_b = add_product(tree, self.product_b)
        self.assertIs(node_a, node_b)

    def test_match_camelcase_with_dashed(self):
        tree = Tree()
        node_b = add_product(tree, self.product_b)
        node_a = add_product(tree, self.product_a)
        self.assertIs(node_a, node_b)


from typing import List

from telegram import Update
from telegram.ext import (
    Updater,
    Filters,
    CallbackContext,
    MessageHandler,
)

from coinbase import CoinbaseTicker
from rates import CurrencyPair

DEFAULT_DISPLAY_CURRENCY = "USD"
MESSAGE_RATE_PATTERN = r"(?i)[a-zA-Z]+[\/](?i)[a-zA-Z]*"
MESSAGE_RATE_DISPLAY_FORMAT = "1 {} is {} {}"

def send_rates(update: Update, context: CallbackContext):
    pair = CurrencyPair.from_str(value=update.message.text, fallback_counter=DEFAULT_DISPLAY_CURRENCY)

    rate, errors = CoinbaseTicker(pair).query()

    if errors is not None:
        context.bot.send_message(chat_id=update.effective_chat.id, text="\n".join(errors))
    else:
        context.bot.send_message(
            chat_id=update.effective_chat.id,
            text=MESSAGE_RATE_DISPLAY_FORMAT.format(
                rate.pair.base,
                rate.amount_formatted(),
                rate.pair.counter
            )
        )

class CryptoRatesBot:

    def __init__(self, bot_token: str, allowed_updates: List[str]):
        self._allowed_updates = allowed_updates
        self._updater = Updater(token=bot_token, use_context=True)
        self._dispatcher = self._updater.dispatcher
        self.register_handlers()

    def process_update(self, update):
        self._dispatcher.process_update(Update.de_json(update, self._updater.bot))

    def register_handlers(self):
        self._dispatcher.add_handler(MessageHandler(Filters.regex(MESSAGE_RATE_PATTERN), send_rates))


#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# fuzz for vulnserver

import sys
from time import sleep
import socket

initSize = int(sys.argv[3])

def attack(dst, dst_port, data):
    try:
        socks = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        socks.connect((dst, dst_port))
        conn = socks.recv(1024)
        # print(conn)

        dd = data.encode('utf-8')
        socks.send(dd)
        print("Sending buffer: {}".format(dd))
        # print(conn)
    except socket.error as err:
        print("Socket connect failed! {}".format(err))
    finally:
        sleep(1)
        socks.close()

if __name__ == '__main__':

    # buffer create python -c 'print("A"*100)'
    buff = "A" * initSize
    host = str(sys.argv[1])
    port = int(sys.argv[2])

    for i in range(20):
        buff = buff * i
        attack(host, port, buff)


'''
    Python function that takes an input string, a position, and a character.
    The function replaces the character in the input
    string at the specified position and returns an updated string
'''
def mutate_string(string, position, character):
    # Function to mutate the input string by replacing the character at the given position.

    l = list(string)        # Convert the input string to a list of characters.
    l[position] = character  # Replace the character at the specified position with the new character.
    string = ''.join(l)      # Convert the list back to a string.
    return string           # Return the updated string.

if __name__ == '__main__':
    s = input()              # Read the original string from the user.
    i, c = input().split()   # Read the position and character to replace from the user.
    s_new = mutate_string(s, int(i), c)  # Call the mutate_string function.
    print(s_new)             # Print the updated string.


"""Module for the traffic simulation menu."""
import pygame

from model_and_simulate.utilities.pygame_simple import Color
from model_and_simulate.utilities.pygame_button import SwitchButton, TextButton, Button
from model_and_simulate.utilities.start_screen import SimulationStartScreen
from .traffic_simulation import TrafficParameters

class TrafficStartScreen(SimulationStartScreen):
    """The start screen for TrafficSimulation`."""

    def create_menu_items(self) -> tuple[int, int, TrafficParameters, list[Button]]:
        """Creates the GUI buttons with `TrafficParameters` logic."""
        simulation_parameters = TrafficParameters()

        width, height = pygame.display.get_window_size()
        col_w, row_h = round(width / 8), round(height / 8)
        menu_items, buttons = self.create_default_items(col_w, row_h)
        button_all_at_once = SwitchButton(
            (col_w, row_h),
            (col_w, 4 * row_h),
            text="All at once",
            in_and_active_color=(Color.RED, Color.SILVER),
        )
        button_all_at_once.on = simulation_parameters.all_vehicles_at_once

        def on_click_listener_all_at_once(*args):
            """The callback function for the switch button."""
            simulation_parameters.all_vehicles_at_once = (
                not simulation_parameters.all_vehicles_at_once
            )
            self.simple_pygame.play_effect("hit_low")

        self.add_switch_buttons([button_all_at_once], menu_items, on_click_listener_all_at_once)
        buttons.append(button_all_at_once)
        buttons_text_input: dict[TextButton, str] = {
            TextButton(
                (col_w * 2, row_h),
                (5 * col_w, y * row_h),
                str(getattr(simulation_parameters, attr) * 100)[:2],
            ): attr
            for y, attr in zip(
                range(3, 5),
                ("occupation", "dawdling_factor"),
            )
        }
        button_length = TextButton(
            (col_w * 2, row_h),
            (6 * col_w, 5 * row_h),
            str(simulation_parameters.length),
            text_color=Color.GREEN,
        )
        buttons_text_input[button_length] = "length"

        def on_start_input_listener(text_button: TextButton):
            """The callback function for start input event."""
            if self.read_input_to is not None:
                self.read_input_to.finish_input()
            self.read_input_to = text_button

        def on_finish_input_listener(text_button: TextButton):
            """The callback function for finish input."""
            simulation_par = buttons_text_input[text_button]
            old_value = getattr(simulation_parameters, simulation_par)
            if text_button.text == "":
                if simulation_par == "length":
                    text_button.text = str(old_value)
                else:
                    text_button.text = str(old_value*100)[:2]
            else:
                user_value = int(text_button.text)
                if simulation_par == "length":
                    if user_value < 300:
                        user_value = 300
                    elif user_value > 3500:
                        user_value = 3500
                    setattr(simulation_parameters, simulation_par, user_value)
                else:
                    if simulation_par == "occupation":
                        user_value = max(user_value, 10)
                    setattr(simulation_parameters, simulation_par, user_value / 100)  # percentage
                text_button.text = str(user_value)

            self.read_input_to = None

        on_hover_text = "Click and use Digits, BACKSPACE or ENTER"

        def on_hover_text_listener(*args):
            """The callback function for hovering text input buttons."""
            self.simple_pygame.draw_text(on_hover_text, (4 * col_w, 2.5 * row_h))

        for button in buttons_text_input.keys():
            button.add_on_start_listener(on_start_input_listener)
            button.add_on_finish_listener(on_finish_input_listener)
            button.add_on_hover_listener(on_hover_text_listener)
            menu_items.add(button)
        buttons += list(buttons_text_input.keys())
        return col_w, row_h, simulation_parameters, buttons

    @staticmethod
    def create_menu_texts(col_w: int, row_h: int) -> list:
        """Gets the raw texts for the start menu. This texts are just drawn, with no logic."""
        menu_texts = SimulationStartScreen.create_menu_texts(col_w, row_h)
        y = 3
        for text, limit in zip(("total density", "dawdling probability"), (10, 0)):
            menu_texts.append((text, (3.4 * col_w, y * row_h + row_h / 2)))
            menu_texts.append((f">= {limit:0>2d} %", (7 * col_w, y * row_h + row_h / 2)))
            y += 1
        menu_texts += [
            ("increment density one by one", (0, 3.5 * row_h, 25, Color.RED)),
            (f"{300} <= length <= {3500}", (4 * col_w, y * row_h + row_h / 2, 20, Color.HGREEN)),
        ]
        return menu_texts


import numpy as np
import shelve
from cvxopt import matrix, solvers
from collections import Counter
from lib.preprocessing.read_data import read_file

# soft margin SVM (dual from with gaussian kernel)
class SVM:

    def __init__(self, file_name, c=1.0, sigma2=1, precision=1e-05, no_kernel=False):
        self.file_name = file_name
        self.c, self.sigma2 = c, sigma2
        self.sol = None
        self.X, self.Y = SVM.read_file(file_name)
        self.precision = precision
        if not no_kernel:
            self.kernel = SVM.gauss_kernel_generator(sigma2)
        else:
            self.kernel = np.inner
        self.lmda = None
        self.sv, self.sv_label, self.sv_index, self.sv_lmda = None, None, None, None
        self.B = 0
        # self.sv_bs, self.b = None, 0
        # self.W, self.B = None, None
        self.fit()

    @staticmethod
    def kernel_matrix_key(file_name, sigma2):
        return file_name + "..." + str(sigma2)

    @staticmethod
    def b_key(file_name, c, sigma2, precision):
        return file_name + "..." + str(c) + "..." + str(sigma2) + "..." + str(precision)

    # cache of kernel constants matrix of same sigma and same filename
    @staticmethod
    def get_kernel_matrix(file_name, X, Y, sigma2):
        K = np.zeros((X.shape[0], X.shape[0]))
        kernel = SVM.gauss_kernel_generator(sigma2)
        for i in range(X.shape[0]):
            for j in range(X.shape[0]):
                K[i, j] = kernel(X[i], X[j])
        P = matrix(np.outer(Y, Y) * K)
        return P

    def fit(self):
        # P
        P = SVM.get_kernel_matrix(self.file_name, self.X, self.Y, self.sigma2)

        # q
        q = -np.ones(self.X.shape[0])
        q = matrix(q)

        # G
        G = np.zeros((self.X.shape[0] * 2, self.X.shape[0]))
        for i in range(self.X.shape[0]):
            G[i][i] = -1
        for i in range(self.X.shape[0], 2 * self.X.shape[0]):
            G[i][i - self.X.shape[0]] = 1
        G = matrix(G)

        # h
        h = []
        for i in range(self.X.shape[0]):
            # h = h + [self.c]
            h = h + [0.0]
        for i in range(self.X.shape[0]):
            # h = h + [0.0]
            h = h + [self.c]
        h = matrix(h)

        # A
        A = np.zeros((1, self.X.shape[0]))
        for i in range(self.X.shape[0]):
            A[0][i] = self.Y[i]
        A = matrix(A)

        b = matrix(0.0)
        self.sol = solvers.qp(P, q, G=G, h=h, A=A, b=b)
        self.lmda = np.ravel(np.array(self.sol['x']))

        # find support vector index
        sv_index = []
        for l in self.lmda:
            if l >= self.precision:
                sv_index += [True]
            else:
                sv_index += [False]
        self.sv_index = np.array(sv_index)
        self.sv, self.sv_label, self.sv_lmda = self.X[self.sv_index], self.Y[self.sv_index], self.lmda[self.sv_index]
        self.B = self.calc_B()

    def calc_B(self):
        b_projects = self.project(self.sv)
        b_projects_count = Counter(b_projects).most_common()[0][0]
        # return 1 - b_projects_count
        return 1 - np.mean(b_projects)

    def project(self, X):
        projects = np.zeros(X.shape[0])
        for a in range(X.shape[0]):
            pa = np.zeros(self.X.shape[0])
            for i in range(self.X.shape[0]):
                xi = self.X[i]
                yi = self.Y[i]
                lmdai = self.lmda[i]
                pa[i] = lmdai * yi * self.kernel(X[a], xi)
            projects[a] = sum(pa)
        return projects + self.B

    def predict(self, X):
        return np.sign(self.project(X))

    @staticmethod
    def gradient(X, Y, W, B, c):
        grad_W, grad_B = np.array(W), 0
        hinge_grad = np.zeros(X.shape[1])
        for i in range(X.shape[0]):
            xi = X[i]
            yi = Y[i]
            for j in range(X.shape[1]):
                if yi * xi[j] * W[j] < 1:
                    hinge_grad[j] = hinge_grad[j] - yi * xi[j]
            if yi * xi[j] < 1:
                grad_B = grad_B - yi
        hinge_grad = c * hinge_grad
        grad_W = grad_W + hinge_grad
        grad_B = c * grad_B
        return grad_W, grad_B

    @staticmethod
    def loss(X, Y, W, B, c):
        nm = (1/2) * np.linalg.norm(W)
        hinge = 0
        for i in range(X.shape[0]):
            xi = X[i]
            yi = Y[i]
            u = 1 - yi * (np.inner(W, xi)) + B
            hinge = hinge + max(0, u)
        hinge = c * hinge
        return nm + hinge

    @staticmethod
    def linear_kernel_generator():
        def linear_kernel(x1, x2):
            return np.inner(x1, x2)
        return linear_kernel

    @staticmethod
    def gauss_kernel_generator(sigma2):
        def gauss_kernel(x1, x2):
            return np.exp(-np.linalg.norm(x1 - x2) ** 2 / (2. * sigma2))
        return gauss_kernel

def distance(hyperplane, point):
    return np.inner(hyperplane.flatten(), point) / np.linalg.norm(hyperplane[1:-1])

if __name__ == "__main__":
    # read data
    train_X, train_Y = read_file("train.data")
    validation_X, validation_Y = read_file("validation.data")
    test_X, test_Y = read_file("test.data")

    train_len = train_X.shape[0]
    validation_len = validation_X.shape[0]
    test_len = test_X.shape[0]
    #
    c_arr = [1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7, 1e8]
    sigma2_arr = [0.1, 1, 10, 100, 1000]
    #
    s = dict()
    for c in c_arr:
        for sigma2 in sigma2_arr:
            # s[c, sigma2] = SVM("spam_train.data", c=c, sigma2=sigma2)
            print("c: ", c, " sigma2: ", sigma2)
            s[c, sigma2] = SVM("train.data", c=c, sigma2=sigma2, no_kernel=False)

    ## training
    accu_training = dict()
    pred_training = dict()
    for c in c_arr:
        for sigma2 in sigma2_arr:
            pred_training[c, sigma2] = s[c, sigma2].predict(train_X)
            accu_training[c, sigma2] = np.sum(pred_training[c, sigma2] == train_Y) / train_len

    ## validation
    accu_validation = dict()
    pred_validation = dict()
    for c in c_arr:
        for sigma2 in sigma2_arr:
            pred_validation[c, sigma2] = s[c, sigma2].predict(validation_X)
            accu_validation[c, sigma2] = np.sum(pred_validation[c, sigma2] == validation_Y) / validation_len

    ## test
    accu_test = dict()
    pred_test = dict()
    for c in c_arr:
        for sigma2 in sigma2_arr:
            pred_test[c, sigma2] = s[c, sigma2].predict(test_X)
            accu_test[c, sigma2] = np.sum(pred_test[c, sigma2] == test_Y) / test_len


import numpy as np
import pandas as pd
from optparse import OptionParser

def get_comma_separated_args(option, opt, value, parser):
	print("Inside callback\n")
	setattr(parser.values, option.dest, value.split(','))

parser = OptionParser()

########-----------------------------------------------------#########
parser.add_option('--numConfigs',
									action = 'store', type='int', dest='numConfigs', default = 1)
parser.add_option('--baselineList',
                  type='string',
									action='callback',
									callback=get_comma_separated_args,
									dest = "getBaselineList")

parser.add_option('--targetList',
                  type='string',
									action='callback',
									callback=get_comma_separated_args,
									dest = "getTargetList")

(options, args) = parser.parse_args()

baselineList = options.getBaselineList
targetList = options.getTargetList ##For Date paper, targetList is hardcoded below.
numConfigs = options.numConfigs

########-----------------------------------------------------#########
## I think the baseline should be picked from Train, since the model is trained on it.
## The target should be taken from test as the test will be the one that the model will be tested on.
## tracesPerConfig -> Traces to pick from a specific key per baselineconfiguration. This will change from
## tracesPerConfig=7500 for 1 config, 7500/2 for 2 configs, 7500/3 for 3 configs
def traceDifference(baselineConfig, targetConfig, tracesPerConfig):
	## Get the baseline data for key=15(chosen at random) to compare against mean from other configsconfig
	data_baseline = pd.DataFrame()
	for config in baselineConfig:
		baselineCsvPath = "/xdisk/rlysecky/manojgopale/xdisk/gem5DataCollection/csvResult/" + config + "/aesData_config9_Train_0.csv"
		data_baseline_inter = pd.read_csv(baselineCsvPath, header=None)
		## Get key==15 traces, shuffle them, and take top traces for concatenation
		data_baseline_inter = data_baseline_inter.loc[data_baseline_inter.iloc[:,1500]==15].sample(frac=1).reset_index(drop=True).iloc[0:tracesPerConfig,0:1500]
		data_baseline = pd.concat([data_baseline, data_baseline_inter], ignore_index=True)
	key15_baseline_train_mean = data_baseline.mean(axis=0)
	key15_baseline_train_std =  data_baseline.std(axis=0)

	## Get mean for the same key(15) on a target configuration and see id mean lies in the 3sigma limit
	targetCsvPath = "/xdisk/rlysecky/manojgopale/xdisk/gem5DataCollection/csvResult/" + targetConfig + "/aesData_config9_Test_0.csv"
	data_target = pd.read_csv(targetCsvPath, header=None)
	key15_target_500 = data_target.loc[data_target.iloc[:,1500]==15].sample(frac=1).reset_index(drop=True).iloc[0:500,0:1500]
	key15_target_test_mean = key15_target_500.mean(axis=0)
	key15_target_test_std =  key15_target_500.std(axis=0)
	print("data_baseline shape = %s, data_target shape= %s" %(data_baseline.shape, key15_target_500.shape))

	## Analysis
	## Get the count where the target is out of bounds
	(np.abs(key15_baseline_train_mean-key15_target_test_mean) > 3*key15_baseline_train_std).value_counts()
	#False    1353
	#True      148
	## To directly get the count
	count_outside = key15_baseline_train_mean[np.abs(key15_baseline_train_mean-key15_target_test_mean) > 3*key15_baseline_train_std].count()
	print("baseline config= %s target config= %s difference in count= %s\n" %(baselineConfig, targetConfig, count_outside))

	## this gets the index where the keys are putside the limits
	key15_baseline_train_mean[np.abs(key15_baseline_train_mean-key15_target_test_mean) > 3*key15_baseline_train_std].index.values

targetList= ["config3p1", "config3p2", "config3p3", "config3p4", "config4p1", "config4p2", "config4p3", "config4p4","config5p1", "config5p2", "config5p3", "config5p4"]
## 2500x3=7500 traces/key was found to be optimal training size. So to keep the training size identical for 2 and 1 configs,
## we need to make sure that the trainig size doesn't wary
tracesPerConfig = int(7500/numConfigs)
##M
##MbaselineConfig = ["config3p3", "config3p4", "config4p3", "config4p4", "config5p3", "config5p4"]
#for idx, baselineConfig in enumerate(baseList):
for index, targetConfig in enumerate(targetList):
	traceDifference(baselineList, targetConfig, tracesPerConfig)

#python3.5 /xdisk/rlysecky/manojgopale/extra/gem5KeyPrediction/scr/trace_differences.py > ~/GEM5_Analysis/trace_difference_group_output_allcombo.log


from __future__ import annotations

__all__ = (
    "LoadVector",
    "DownselectVector",
    "MultiCriteriaDownselectVector",
    "ConvertFluxToMag",
    "ConvertUnits",
    "CalcSn",
    "MagDiff",
    "ExtinctionCorrectedMagDiff",
    "PerGroupStatistic",
    "ResidualWithPerGroupStatistic",
    "RAcosDec",
)

import logging
import warnings
from typing import Optional, cast

import numpy as np
import pandas as pd
from astropy import units as u
from lsst.pex.config import DictField, Field
from lsst.pex.config.configurableActions import ConfigurableActionField, ConfigurableActionStructField

from ...interfaces import KeyedData, KeyedDataSchema, Vector, VectorAction
from .selectors import VectorSelector

_LOG = logging.getLogger(__name__)

# Basic vectorActions

class LoadVector(VectorAction):
    """Load and return a Vector from KeyedData."""

    vectorKey = Field[str](doc="Key of vector which should be loaded")

    def getInputSchema(self) -> KeyedDataSchema:
        return ((self.vectorKey, Vector),)

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        return np.array(cast(Vector, data[self.vectorKey.format(**kwargs)]))

class DownselectVector(VectorAction):
    """Get a vector from KeyedData, apply specified selector, return the
    shorter Vector.
    """

    vectorKey = Field[str](doc="column key to load from KeyedData")

    selector = ConfigurableActionField[VectorAction](
        doc="Action which returns a selection mask", default=VectorSelector
    )

    def getInputSchema(self) -> KeyedDataSchema:
        yield (self.vectorKey, Vector)
        yield from cast(VectorAction, self.selector).getInputSchema()

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        mask = cast(VectorAction, self.selector)(data, **kwargs)
        return cast(Vector, data[self.vectorKey.format(**kwargs)])[mask]

class MultiCriteriaDownselectVector(VectorAction):
    """Get a vector from KeyedData, apply specified set of selectors with AND
    logic, and return the shorter Vector.
    """

    vectorKey = Field[str](doc="column key to load from KeyedData")

    selectors = ConfigurableActionStructField[VectorAction](
        doc="Selectors for selecting rows, will be AND together",
    )

    def getInputSchema(self) -> KeyedDataSchema:
        yield (self.vectorKey, Vector)
        for action in self.selectors:
            yield from action.getInputSchema()

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        mask: Optional[Vector] = None
        for selector in self.selectors:
            subMask = selector(data, **kwargs)
            if mask is None:
                mask = subMask
            else:
                mask *= subMask  # type: ignore
        return cast(Vector, data[self.vectorKey.format(**kwargs)])[mask]

# Astronomical vectorActions

class CalcSn(VectorAction):
    """Calculate the signal-to-noise ratio from a single flux vector."""

    fluxType = Field[str](doc="Flux type (vector key) to calculate the S/N.", default="{band}_psfFlux")
    uncertaintySuffix = Field[str](
        doc="Suffix to add to fluxType to specify the uncertainty column", default="Err"
    )

    def getInputSchema(self) -> KeyedDataSchema:
        yield self.fluxType, Vector
        yield f"{self.fluxType}{self.uncertaintySuffix}", Vector

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        signal = np.array(data[self.fluxType.format(**kwargs)])
        noise = np.array(data[f"{self.fluxType}{self.uncertaintySuffix}".format(**kwargs)])
        sn = signal / noise

        return np.array(sn)

class ConvertFluxToMag(VectorAction):
    """Turn nano janskies into magnitudes."""

    vectorKey = Field[str](doc="Key of flux vector to convert to mags")
    fluxUnit = Field[str](doc="Astropy unit of flux vector", default="nJy")
    returnMillimags = Field[bool](doc="Use millimags or not?", default=False)

    def getInputSchema(self) -> KeyedDataSchema:
        return ((self.vectorKey, Vector),)

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore", r"invalid value encountered")
            warnings.filterwarnings("ignore", r"divide by zero")
            vec = cast(Vector, data[self.vectorKey.format(**kwargs)])
            mags = (np.array(vec) * u.Unit(self.fluxUnit)).to(u.ABmag).value  # type: ignore
            if self.returnMillimags:
                mags *= 1000
            return mags

class ConvertUnits(VectorAction):
    """Convert the units of a vector."""

    buildAction = ConfigurableActionField(doc="Action to build vector", default=LoadVector)
    inUnit = Field[str](doc="input Astropy unit")
    outUnit = Field[str](doc="output Astropy unit")

    def getInputSchema(self) -> KeyedDataSchema:
        return tuple(self.buildAction.getInputSchema())

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        dataWithUnit = self.buildAction(data, **kwargs) * u.Unit(self.inUnit)
        return dataWithUnit.to(self.outUnit).value

class MagDiff(VectorAction):
    """Calculate the difference between two magnitudes;
    each magnitude is derived from a flux column.
    Parameters
    ----------
    TO DO:
    Returns
    -------
    The magnitude difference in milli mags.
    Notes
    -----
    The flux columns need to be in units (specifiable in
    the fluxUnits1 and 2 config options) that can be converted
    to janskies. This action doesn't have any calibration
    information and assumes that the fluxes are already
    calibrated.
    """

    col1 = Field[str](doc="Column to subtract from")
    fluxUnits1 = Field[str](doc="Units for col1", default="nanojansky")
    col2 = Field[str](doc="Column to subtract")
    fluxUnits2 = Field[str](doc="Units for col2", default="nanojansky")
    returnMillimags = Field[bool](doc="Use millimags or not?", default=True)

    def getInputSchema(self) -> KeyedDataSchema:
        return ((self.col1, Vector), (self.col2, Vector))

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        flux1 = np.array(data[self.col1.format(**kwargs)]) * u.Unit(self.fluxUnits1)
        mag1 = flux1.to(u.ABmag)

        flux2 = np.array(data[self.col2.format(**kwargs)]) * u.Unit(self.fluxUnits2)
        mag2 = flux2.to(u.ABmag)

        magDiff = mag1 - mag2

        if self.returnMillimags:
            magDiff = magDiff.to(u.mmag)

        return np.array(magDiff.value)

class ExtinctionCorrectedMagDiff(VectorAction):
    """Compute the difference between two magnitudes and correct for extinction
    By default bands are derived from the <band>_ prefix on flux columns,
    per the naming convention in the Object Table:
    e.g. the band of 'g_psfFlux' is 'g'. If column names follow another
    convention, bands can alternatively be supplied via the band1 or band2
    config parameters.
    If band1 and band2 are supplied, the flux column names are ignored.
    """

    magDiff = ConfigurableActionField[VectorAction](
        doc="Action that returns a difference in magnitudes", default=MagDiff
    )
    ebvCol = Field[str](doc="E(B-V) Column Name", default="ebv")
    band1 = Field[str](
        doc="Optional band for magDiff.col1. Supercedes column name prefix",
        optional=True,
        default=None,
    )
    band2 = Field[str](
        doc="Optional band for magDiff.col2. Supercedes column name prefix",
        optional=True,
        default=None,
    )
    extinctionCoeffs = DictField[str, float](
        doc="Dictionary of extinction coefficients for conversion from E(B-V) to extinction, A_band."
        "Key must be the band",
        optional=True,
        default=None,
    )

    def getInputSchema(self) -> KeyedDataSchema:
        return self.magDiff.getInputSchema() + ((self.ebvCol, Vector),)

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        diff = self.magDiff(data, **kwargs)
        if not self.extinctionCoeffs:
            _LOG.debug("No extinction Coefficients. Not applying extinction correction")
            return diff

        col1Band = self.band1 if self.band1 else self.magDiff.col1.split("_")[0]
        col2Band = self.band2 if self.band2 else self.magDiff.col2.split("_")[0]

        # Return plain MagDiff with warning if either coeff not found
        for band in (col1Band, col2Band):
            if band not in self.extinctionCoeffs:
                _LOG.warning(
                    "%s band not found in coefficients dictionary: %s" " Not applying extinction correction",
                    band,
                    self.extinctionCoeffs,
                )
                return diff

        av1: float = self.extinctionCoeffs[col1Band]
        av2: float = self.extinctionCoeffs[col2Band]

        ebv = data[self.ebvCol]
        # Ignore type until a more complete Vector protocol
        correction = np.array((av1 - av2) * ebv) * u.mag  # type: ignore

        if self.magDiff.returnMillimags:
            correction = correction.to(u.mmag)

        return np.array(diff - correction.value)

class RAcosDec(VectorAction):
    """Construct a vector of RA*cos(Dec) in order to have commensurate values
    between RA and Dec."""

    raKey = Field[str](doc="RA coordinate", default="coord_ra")
    decKey = Field[str](doc="Dec coordinate", default="coord_dec")

    def getInputSchema(self) -> KeyedDataSchema:
        return ((self.decKey, Vector), (self.raKey, Vector))

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        ra = data[self.raKey]
        dec = data[self.decKey]
        return ra.to_numpy() * np.cos((dec.to_numpy() * u.degree).to(u.radian).value)

# Statistical vectorActions

class PerGroupStatistic(VectorAction):
    """Compute per-group statistic values and return result as a vector with
    one element per group. The computed statistic can be any function accepted
    by pandas DataFrameGroupBy.aggregate passed in as a string function name.
    """

    groupKey = Field[str](doc="Column key to use for forming groups", default="obj_index")
    buildAction = ConfigurableActionField[VectorAction](doc="Action to build vector", default=LoadVector)
    func = Field[str](doc="Name of function to be applied per group")

    def getInputSchema(self) -> KeyedDataSchema:
        return tuple(self.buildAction.getInputSchema()) + ((self.groupKey, Vector),)

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        df = pd.DataFrame({"groupKey": data[self.groupKey], "value": self.buildAction(data, **kwargs)})
        result = df.groupby("groupKey")["value"].aggregate(self.func)
        return np.array(result)

class ResidualWithPerGroupStatistic(VectorAction):
    """Compute residual between individual elements of group and the per-group
    statistic."""

    groupKey = Field[str](doc="Column key to use for forming groups", default="obj_index")
    buildAction = ConfigurableActionField(doc="Action to build vector", default=LoadVector)
    func = Field[str](doc="Name of function to be applied per group", default="mean")

    def getInputSchema(self) -> KeyedDataSchema:
        return tuple(self.buildAction.getInputSchema()) + ((self.groupKey, Vector),)

    def __call__(self, data: KeyedData, **kwargs) -> Vector:
        values = self.buildAction(data, **kwargs)
        df = pd.DataFrame({"groupKey": data[self.groupKey], "value": values})
        result = df.groupby("groupKey")["value"].aggregate(self.func)

        joinedDf = df.join(result, on="groupKey", validate="m:1", lsuffix="_individual", rsuffix="_group")

        result = joinedDf["value_individual"] - joinedDf["value_group"]
        return np.array(result)


"""
주소: https://leetcode.com/problems/count-subtrees-with-max-distance-between-cities/

내용
- 도시간 연결 상태를 나타낸 양방향 그래프가 주어진다
  - 이 그래프는 트리로 전환 가능하다 (=사이클이 존재하지 않는다)
- 도시간 거리가 1, 2, ..., n-1 이 되는 sub graph의 개수를 각각 구하라

예제
Example 1:
Input: n = 4, edges = [[1,2],[2,3],[2,4]]
Output: [3,4,0]
Explanation:
The subtrees with subsets {1,2}, {2,3} and {2,4} have a max distance of 1.
The subtrees with subsets {1,2,3}, {1,2,4}, {2,3,4} and {1,2,3,4} have a max distance of 2.
No subtree has two nodes where the max distance between them is 3.

Example 2:
Input: n = 2, edges = [[1,2]]
Output: [1]

Example 3:
Input: n = 3, edges = [[1,2],[2,3]]
Output: [2,1]

풀이방법
- 모든 경우의수를 고려
- 각 노드에서 시작하면서 옆 노드를 하나씩 추가하고, 이때 포함된 노드간의 최대 거리를 계산
- 단, 중복 제거 및 가지치기를 위해 frozenset을 활용하여 현재 포함된 노드를 저장
- 저장된 노드가 그대로 다시 나타날경우 재귀 종료

"""
class Solution:
    def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]:
        dist_map = [[-1 for _ in range(n+1)] for _ in range(n+1)]
        found = [set([]) for _ in range(n)]
        ebn = [set([]) for _ in range(n+1)]

        for edge in edges:
            ebn[edge[0]].add(edge[1])
            ebn[edge[1]].add(edge[0])
            dist_map[edge[0]][edge[1]] = 1
            dist_map[edge[1]][edge[0]] = 1

        for i in range(n+1):
            dist_map[i][i] = 0

        def search(dist: int, nodes: frozenset):
            if dist >= 1:
                if nodes in found[dist]:
                    return
                else:
                    found[dist].add(nodes)

            for con_node in nodes:
                for new_node in ebn[con_node]:
                    if not new_node in nodes:
                        new_nodes = set([new_node])

                        max_dist = -1
                        max_dist_nodes = []
                        for node in nodes:
                            new_nodes.add(node)
                            dist_map[node][new_node] = dist_map[node][con_node] + 1
                            dist_map[new_node][node] = dist_map[node][con_node] + 1

                            if dist_map[node][con_node] > max_dist:
                                max_dist = dist_map[node][con_node]
                                max_dist_nodes = [node]
                            elif dist_map[node][con_node] == max_dist:
                                max_dist_nodes.append(node)

                        if max_dist == dist:
                            for node in max_dist_nodes:
                                search(dist + 1, frozenset(new_nodes))
                        else:
                            search(dist, frozenset(new_nodes))

        for i in range(1, n+1):
            search(0, frozenset([i]))
            #print(found[2])

        return [len(found[i]) for i in range(1, n)]


import cv2
import numpy as np

img=cv2.imread('Photo.jpg',cv2.IMREAD_GRAYSCALE)
cv2.imshow("anh",img)
cv2.waitKey()
img_binary=cv2.adaptiveThreshold(img, maxValue=255, adaptiveMethod= cv2.ADAPTIVE_THRESH_MEAN_C,
                                 thresholdType=cv2.THRESH_BINARY, blockSize=15, C=8)
cv2.imshow("anh nhi phan:",img_binary)
cv2.waitKey()


import operator
class Calculator:

    def Number1IsPressed(self):
        getNumber1 = input()
        global Zahl1
        Zahl1 = Zahl1 + getNumber1
        print(Zahl1)
        return Zahl1

    def Number2IsPressed(self):
        getNumber2 = input()
        global Zahl2
        Zahl2 = Zahl2 + getNumber2
        print(Zahl2)
        return Zahl2

    def OperatorIsPressed(self):
        op = input()
        #op = '+'
        return op

    def Calculate(self):
        Ergebnis = operations[char](Zahl1, Zahl2)
        print(Ergebnis)
        return  Ergebnis

c = Calculator()

Zahl1 = ""
Zahl2 = ""

he = int(c.Number1IsPressed())
op = c.OperatorIsPressed()
ha = int(c.Number2IsPressed())

result = eval('{}{}{}'.format(he, op, ha))
print(result)


import cv2, os
import numpy as np
from numpy.lib.function_base import unwrap

def drawCube(img_rgb, rvecs, tvecs, mtx, dist, cube_color = (255, 0, 0), tag_size = 1 ,is_centered = True):
	# Define the ar cube

	if is_centered == True:
		# It is important to note that the center of the marker corresponds to the origin and we must therefore move 0.5 away from the origin
		axis = np.float32([[-0.5, -0.5, 0], [-0.5, 0.5, 0], [0.5, 0.5, 0], [0.5, -0.5, 0],
							[-0.5, -0.5, 1], [-0.5, 0.5, 1], [0.5, 0.5, 1],[0.5, -0.5, 1]])
	else:
		axis = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
						[0, 0, 1], [0, 1, 1], [1, 1, 1],[1, 0, 1]])

	axis = axis * tag_size # scale the axis to match the tag size

	# Now we transform the cube to the marker position and project the resulting points into 2d
	imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
	imgpts = np.int32(imgpts).reshape(-1, 2)

	# Now comes the drawing.
	# In this example, I would like to draw the cube so that the walls also get a painted
	# First create six copies of the original picture (for each side of the cube one)
	side1 = img_rgb.copy()
	side2 = img_rgb.copy()
	side3 = img_rgb.copy()
	side4 = img_rgb.copy()
	side5 = img_rgb.copy()
	side6 = img_rgb.copy()

	# Draw the bottom side (over the marker)
	side1 = cv2.drawContours(side1, [imgpts[:4]], -1, cube_color, -2)
	# Draw the top side (opposite of the marker)
	side2 = cv2.drawContours(side2, [imgpts[4:]], -1, cube_color, -2)
	# Draw the right side vertical to the marker
	side3 = cv2.drawContours(side3, [np.array(
		[imgpts[0], imgpts[1], imgpts[5],
			imgpts[4]])], -1, cube_color, -2)
	# Draw the left side vertical to the marker
	side4 = cv2.drawContours(side4, [np.array(
		[imgpts[2], imgpts[3], imgpts[7],
			imgpts[6]])], -1, cube_color, -2)
	# Draw the front side vertical to the marker
	side5 = cv2.drawContours(side5, [np.array(
		[imgpts[1], imgpts[2], imgpts[6],
			imgpts[5]])], -1, cube_color, -2)
	# Draw the back side vertical to the marker
	side6 = cv2.drawContours(side6, [np.array(
		[imgpts[0], imgpts[3], imgpts[7],
			imgpts[4]])], -1, cube_color, -2)

	# Until here the walls of the cube are drawn in and can be merged
	img_rgb = cv2.addWeighted(side1, 0.1, img_rgb, 0.9, 0)
	img_rgb = cv2.addWeighted(side2, 0.1, img_rgb, 0.9, 0)
	img_rgb = cv2.addWeighted(side3, 0.1, img_rgb, 0.9, 0)
	img_rgb = cv2.addWeighted(side4, 0.1, img_rgb, 0.9, 0)
	img_rgb = cv2.addWeighted(side5, 0.1, img_rgb, 0.9, 0)
	img_rgb = cv2.addWeighted(side6, 0.1, img_rgb, 0.9, 0)

	# Now the edges of the cube are drawn thicker and stronger
	img_rgb = cv2.drawContours(img_rgb, [imgpts[:4]], -1, cube_color, 2)
	for i, j in zip(range(4), range(4, 8)):
		img_rgb = cv2.line(img_rgb, tuple(imgpts[i]), tuple(imgpts[j]), cube_color, 2)
	img_rgb = cv2.drawContours(img_rgb, [imgpts[4:]], -1, cube_color, 2)

	return img_rgb

def draw_cube_list(img_rgb, detection_list, calib_mtx, dist_coef, tag_size =1, cube_color = (125,125,125), is_centered = True):

	for detection in detection_list:
		img_rgb = drawCube(img_rgb, detection['rvec'], detection['tvec'], calib_mtx, dist_coef, cube_color, tag_size = tag_size, is_centered = True)

	return img_rgb

def calibrator():
	import cv2
	import numpy as np
	import pyrealsense2 as rs

	# Defines the path to save the calibration file and the dictonary used
	device_name = input("Please enter the device name :")
	name = device_name + "_" + get_time() + ".npz"
	dictionary = cv2.aruco.DICT_APRILTAG_36h10

	# Initialize communication with intel realsense
	pipeline = rs.pipeline()
	realsense_cfg = rs.config()
	realsense_cfg.enable_stream(rs.stream.color, 1920, 1080, rs.format.rgb8, 30)
	pipeline.start(realsense_cfg)

	# Check communication
	print("Test data source...")
	try:
		np.asanyarray(pipeline.wait_for_frames().get_color_frame().get_data())
	except:
		raise Exception("Can't get rgb frame from data source")

	# Define what the calibration board looks like (same as the pdf)
	board = cv2.aruco.CharucoBoard_create(6, 6, 0.16/6, 0.16/6*0.8, cv2.aruco.Dictionary_get(dictionary))
	record_count = 0
	# Create two arrays to store the recorded corners and ids
	all_corners = []
	all_ids = []

	print("Start recording [1/4]")
	print("1. Move the grid from calibration directory a little bit in front of the camera and press [r] to make a record (if auto record is not set to True)")
	print("2. Finish this task and start calculation press [c]")
	print("3. Interrupt application [ESC]")
	while True:
		# Get frame from realsense and convert to grayscale image
		frames = pipeline.wait_for_frames()
		img_rgb = np.asanyarray(frames.get_color_frame().get_data())
		img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)

		# Detect markers on the gray image
		res = cv2.aruco.detectMarkers(img_gray, cv2.aruco.getPredefinedDictionary(dictionary))
		# Draw the detected markers
		cv2.aruco.drawDetectedMarkers(img_rgb, res[0], res[1])
		# Display the result
		display_image = cv2.resize(img_rgb, (960, 540))
		cv2.imshow("AR-Example", cv2.cvtColor(display_image, cv2.COLOR_RGB2BGR))

		key = cv2.waitKey(10)
		# If 18 markers have been found, allow recording
		if len(res[0]) == 18:
			# Interpolate the corners of the markers
			res2 = cv2.aruco.interpolateCornersCharuco(res[0], res[1], img_gray, board)
			# Add the detected interpolated corners and the marker ids to the arrays if the user press [r] and the interpolation is valid
			if key == ord('r') and res2[1] is not None and res2[2] is not None and len(res2[1]) > 18:
				all_corners.append(res2[1])
				all_ids.append(res2[2])
				record_count += 1
				print("Record: " + str(record_count))
		# If [c] pressed, start the calculation
		if key == ord('c'):
			# Close all cv2 windows
			cv2.destroyAllWindows()
			# Check if recordings have been made
			if(record_count != 0):
				print("Calculate calibration [2/4] --> Use "+str(record_count)+" records"),
				# Calculate the camera calibration
				ret, mtx, dist, rvecs, tvecs = cv2.aruco.calibrateCameraCharuco(all_corners, all_ids, board, img_gray.shape, None, None)
				print("Save calibration [3/4]")
				# Save the calibration information into a file
				np.savez_compressed(getCalibPath(name), ret=ret, mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
				print("Done [4/4]")
			else:
				print("Interrupted since there are no records...")
			break
		# If [ESC] pressed, close the application
		if key == 27:
			print("Application closed without calculation")
			# Close all cv2 windows
			cv2.destroyAllWindows()
			break

def getCalibPath(filename):
	# Defines the path of the calibration file and the dictonary used
	return os.path.join(os.path.split(os.path.dirname(__file__))[0], 'calibration','calibfiles', filename)

def get_test_path(filename):
	# Defines the path of the calibration file and the dictonary used
	return os.path.join(os.path.split(os.path.dirname(__file__))[0], 'testresults', filename)

def displayCalibMat(filename, showmtx= True, showDist=True):
	# Defines the path of the calibration file and the dictonary used
	calibration_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'calibration','calibfiles', filename)

	# Load calibration from file
	mtx = None
	dist = None
	with np.load(calibration_path) as X:
		mtx, dist, _, _ = [X[i] for i in ('mtx', 'dist', 'rvecs', 'tvecs')]

	if showmtx==True:print('Calibration Matrix : \n' , mtx,'\n')
	if showDist==True:print('Distortion Parameters : \n' ,dist,'\n')

def getCalibData(filename):
	# Defines the path of the calibration file and the dictonary used
	calibration_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'calibration','calibfiles', filename)

	# Load calibration from file
	mtx = None
	dist = None
	with np.load(calibration_path) as X:
		mtx, dist = [X[i] for i in ('mtx', 'dist')]

	print("\nThe file used for the camera calibration :", calibration_path)

	return mtx, dist

def angle_error(v1, v2):
	# v1 is your firsr vector
	# v2 is your second vector
    return np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))

def angle_error_rowwise(A, B):
	A, B = unwrap(A), unwrap(B)
	p1 = np.einsum('ij,ij->i',A,B)
	p2 = np.einsum('ij,ij->i',A,A)
	p3 = np.einsum('ij,ij->i',B,B)
	p4 = p1 / np.sqrt(p2*p3)
	return np.arccos(np.clip(p4,-1.0,1.0))

def distance_matrix(A, B, squared=False):
    """
    Compute all pairwise distances between vectors in A and B.

    Parameters
    ----------
    A : np.array
        shape should be (M, K)
    B : np.array
        shape should be (N, K)

    Returns
    -------
    D : np.array
        A matrix D of shape (M, N).  Each entry in D i,j represnets the
        distance between row i in A and row j in B.

    See also
    --------
    A more generalized version of the distance matrix is available from
    scipy (https://www.scipy.org) using scipy.spatial.distance_matrix,
    which also gives a choice for p-norm.
    """
    M = A.shape[0]
    N = B.shape[0]

    assert A.shape[1] == B.shape[1], f"The number of components for vectors in A \
        {A.shape[1]} does not match that of B {B.shape[1]}!"

    A_dots = (A*A).sum(axis=1).reshape((M,1))*np.ones(shape=(1,N))
    B_dots = (B*B).sum(axis=1)*np.ones(shape=(M,1))
    D_squared =  A_dots + B_dots -2*A.dot(B.T)

    if squared == False:
        zero_mask = np.less(D_squared, 0.0)
        D_squared[zero_mask] = 0.0
        return np.sqrt(D_squared)

    return D_squared

def elementwise_distance(A, B, squared = False):
	"""
	Compute all elementwise distances between vectors in A and B.

	Parameters
	----------
	A : np.array
		shape should be (M, K)
	B : np.array
		shape should be (M, K)

	Returns
	-------
	D : np.array
		A matrix D of shape (M, 1).  Each entry in D i represnets the
		distance between row i in A and row i in B.

	"""

	M = A.shape[0]
	N = B.shape[0]

	assert A.shape[0] == B.shape[0], f"The number of components for vectors in A \
		{A.shape[1]} does not match that of B {B.shape[1]}!"

	try:
		D_squared = np.sum(np.square(A - B), axis = 1)
	except:
		D_squared = np.sum(np.square(A - B), axis = 0)

	if squared == False:
		return np.sqrt(D_squared)

	return D_squared

def moving_average(old_mean, new_data, n_of_frames):

	return old_mean + 1 / n_of_frames * (new_data - old_mean)

def get_time():
	import time
	return time.strftime("%d%m%Y_%H%M%S")

def get_process_memory():
	import psutil, os

	process = psutil.Process(os.getpid())
	mem_info = process.memory_info()
	return mem_info.rss + mem_info.vms + mem_info.shared  # in bytes

def user_prompt(question: str) -> bool:
    """ Prompt the yes/no-*question* to the user. """
    from distutils.util import strtobool

    while True:
        user_input = input(question + " [y/n]: ")
        try:
            return bool(strtobool(user_input))
        except ValueError:
            print("Please use y/n or yes/no.\n")

class GetImages():
	def __init__(self, is_camera = True, dataset_name = None):
		if is_camera:
			import pyrealsense2 as rs

			self.is_camera = True

			# Initialize communication with intel realsense
			self.pipeline = rs.pipeline()
			realsense_cfg = rs.config()
			realsense_cfg.enable_stream(rs.stream.color, 1920, 1080, rs.format.rgb8, 30)
			self.pipeline.start(realsense_cfg)

			# Check communication
			print("Testing the connection with the camera...")
			try:
				np.asanyarray(self.pipeline.wait_for_frames().get_color_frame().get_data())
			except:
				raise Exception("Can't get rgb frame from camera")

			print("Connection with the camera is succesful!")

		else:
			self.is_camera = False   #use dataset
			self.dataset_folder_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'datasets', dataset_name)
			file_list = os.listdir(self.dataset_folder_path)
			print("Path of the dataset to be used :", self.dataset_folder_path)
			self.filtered_file_list = [k for k in file_list if ".png" in k]
			self.i = 0

	def get_image(self):
		if self.is_camera:
			frames = self.pipeline.wait_for_frames()
			img_rgb = np.asanyarray(frames.get_color_frame().get_data())

			return img_rgb

		else:
			if self.i >= len(self.filtered_file_list):
				raise Exception("No more image left in the dataset!")
			else:
				img_rgb = cv2.imread(os.path.join(self.dataset_folder_path, self.filtered_file_list[self.i]))
				self.i += 1
				return img_rgb

def record_dataset(folder_name, n_of_image = 100, file_name = None):
	import pathlib

	if file_name == None : file_name = folder_name
	file_id_padding = 5

	dataset_folder_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'datasets', folder_name)
	pathlib.Path(dataset_folder_path).mkdir(parents=True, exist_ok=True)

	get_image = GetImages(is_camera = True)

	for i in range(n_of_image):
		img_rgb = get_image.get_image()
		cv2.imwrite(os.path.join(dataset_folder_path, file_name + "_{:>0{}}.png".format(i, file_id_padding)), img_rgb)

def display_dataset(folder_name, n):
	get_image = GetImages(False, folder_name)

	for i in range(n):
		cv2.imshow(folder_name, cv2.cvtColor(get_image.get_image(), cv2.COLOR_RGB2BGR))
		cv2.waitKey(0)
	cv2.destroyAllWindows()

def image_saver(folder_name = None, wait_time = 5):
	import time, pathlib

	if folder_name == None: folder_name = input("What should the folder name for the images to be saved ? : ")
	get_image = GetImages(is_camera=True)

	image_folder_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], 'calibration', 'calibration_images', folder_name)
	pathlib.Path(image_folder_path).mkdir(parents=True, exist_ok=True)
	print("Images will be saved to :", image_folder_path)

	img_counter = 0
	start_time = time.time()

	print("Press [ESC] to close the application")

	while True:
		img_rgb = get_image.get_image()
		display_image = cv2.resize(img_rgb, (960, 540))

		remaining_time = str(wait_time - (time.time() - start_time))[0] + ' second(s) left'
		info = str(img_counter) + ' image(s) captured'

		#--- Position the time at (10, 70) coordinate with certain font style, size and color ---
		cv2.putText(display_image, remaining_time + " | " + info , (10,70), cv2.FONT_HERSHEY_SIMPLEX, 1.5,(15,200,15),2,cv2.LINE_AA)
		cv2.imshow("Image Saver", cv2.cvtColor(display_image, cv2.COLOR_RGB2BGR))

		if time.time() - start_time >= wait_time: # Check if 5 wait time is passed
			img_name = "image_{:>0{}}.png".format(img_counter, 3)

			cv2.imwrite(os.path.join(image_folder_path, img_name), cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR))
			print("{} written!".format(img_name))
			start_time = time.time()
			img_counter += 1

		# If [ESC] pressed, close the application
		if cv2.waitKey(100) == 27:
			print("Application closed")
			break
	cv2.destroyAllWindows()

def unwrap(rvecs):
	"""Make sures that angels are in range of -pi and pi"""
	return (rvecs + np.pi) % (2 * np.pi) - np.pi

def find_ambiguity(ids, rvecs, tvecs):

	if ids is None: return []

	n = ids.size
	flipped_ids = []

	for i in range(n):
		R = cv2.Rodrigues(rvecs[i])[0]

		if R[1,2] > 0:
			flipped_ids.append(int(ids[i]))

	return flipped_ids

def boxplot_data(data):
	if data.size == 0 : return None
	data_dict = {}
	data_dict['median'] = np.median(data)
	data_dict['average'] = np.average(data)
	data_dict['upper_quartile'] = np.percentile(data, 75)
	data_dict['lower_quartile'] = np.percentile(data, 25)
	data_dict['max'] = np.max(data)
	data_dict['min'] = np.min(data)

	return data_dict


from collections import defaultdict, deque
import re

def parse_input(lines):
	return construct_rooms(lines[0][1:-1])

def follow_dir(adj_rooms, x, y, direc):
	if direc == "N":
		end = (x, y+1)
	elif direc == "S":
		end = (x, y-1)
	elif direc == "W":
		end = (x-1, y)
	else:
		end = (x+1, y)

	adj_rooms[(x, y)].add(end)
	adj_rooms[end].add((x, y))
	return end

def construct_rooms(directions):
	adj_rooms = defaultdict(set)

	# | is intentionally left out of here. It just breaks up \w+, and is otherwise not needed.
	#   The string '|(' does not exist anywhere in the regex.
	chunks = re.findall(r"\w+|[()]|\|+", directions + ")")

	def parse(cur):
		parts = [[]]
		while True:
			if chunks[cur] == "|":
				parts.append([])

			elif chunks[cur] == "(":
				cur, part = parse(cur+1)
				parts[-1].append(part)

			elif chunks[cur] == ")":
				# Empty pipe part only ever comes at end of capture group.
				if not parts[-1]:
					parts[-1].append("")
				return cur, parts

			else:
				parts[-1].append(chunks[cur])

			cur += 1

	parts = parse(0)[1]

	# Note: The first character in the regex must not be a (.

	def follow_ppart(ppart, strt):
		curs = {strt}
		for part in ppart:
			new_curs = set()
			if isinstance(part, str):
				for cur in curs:
					for direc in part:
						cur = follow_dir(adj_rooms, *cur, direc)
					new_curs.add(cur)
			else:
				for cur in curs:
					for pp in part:
						new_curs |= follow_ppart(pp, cur)
			curs = new_curs

		return curs

	for pp in parts:
		follow_ppart(pp, (0, 0))

	return adj_rooms

def part1(adj_rooms):
	# Do a bfs, and return the very last thing found.
	found = set()
	to_explore = deque()
	to_explore.appendleft(((0, 0), 0))
	while to_explore:
		cur, dist = to_explore.pop()
		for adj_room in adj_rooms[cur]:
			if adj_room not in found:
				found.add(adj_room)
				to_explore.appendleft((adj_room, dist+1))

	return dist

def part2(adj_rooms):
	# Do a bfs, keeping track of rooms with distance > 1000
	far_rooms = 0
	found = set()
	to_explore = deque()
	to_explore.appendleft(((0, 0), 0))
	while to_explore:
		cur, dist = to_explore.pop()
		if dist >= 1000:
			far_rooms += 1
		for adj_room in adj_rooms[cur]:
			if adj_room not in found:
				found.add(adj_room)
				to_explore.appendleft((adj_room, dist+1))

	return far_rooms


from django.db.models import Count, OuterRef, Subquery, F, ExpressionWrapper, IntegerField
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.functions import Coalesce
from django.shortcuts import render, redirect
from django.http import HttpResponse
from contextlib import suppress
import simplejson as json
from .models import Tag, Question, Deck, Pain, Answer, Vote

# Landing
def index(request):
      return HttpResponse("hello, world you are at card question")

def landing(request):
  all_tags = Tag.objects.all().annotate(num_questions=Count('question')).order_by('-num_questions')
  top_tags = all_tags[0:3]
  return render(request, 'flashr/landing.html', {'all_tags': all_tags, 'top_tags': top_tags})

#Questions
#show one card from anywhere to anywhere
@login_required
def card_show(request, **kwargs):
  print('is this thing ieven ariirna')
  profile = request.user.profile
  if 'idx' in kwargs:
    idx = kwargs['idx']
    deck = Deck.objects.filter(profile=profile) #grabs the subquery so only one db delve
    count = deck.count() #counts the cards obv
    card = deck.get(order_idx=idx).question #gets the single card in question
    values = {'question': card, 'tag': kwargs['tag'], 'deck_idx': idx, 'last_card': (idx == count)}
    # values['last_card'] = (idx == count)
  else:
    card = Question.objects.get(pk=kwargs['pk'])
    values = {'question': card}

  values['card_tags'] = card.tags.all()
  print('pre community gathering')
  values['community'] = Answer.objects.filter(question=card, public=True).exists()
  with suppress(ObjectDoesNotExist):
    print('no valuse')
    values['current_answer'] = Answer.objects.get(author=profile, question=card)
    print('curent answer value', values['current_answer'])
  with suppress(ObjectDoesNotExist):
    values['pain'] = Pain.objects.filter(profile=profile, question=card).latest('time_stamp')#.order_by('-time_stamp')[0:1].get()

  return render(request, 'flashr/card.html', values)

@login_required
def deck_create(request, tag):
  user = request.user
  tag.lower()
  # Delete records for any existing deck
  Deck.objects.filter(profile=user.profile).delete()
  # Find all the questions for the new deck
  deck = Question.objects.filter(tags__content=tag)

  # Note: targeting specific user part is not fully tested
  # Create a subquery that gets its id # from the calling query. Gets a users latest pain for a specific question
  # values('level')[:1] extracts just the level, trimmed to the latest one based on the order_by sort
  # Using '.latest()', '[0]', and '.level' don't work since they trigger immediate evaluation in the subquery
  pains = Pain.objects.all().prefetch_related('profile', 'question') # get once and cache in pains variable
  user_pain = pains.filter(profile=user.profile,question__id=OuterRef('pk')).order_by('-time_stamp').values('level')[:1]
  # Use the subquery to add the users latest pain to all of the questions with the deck tag.
  deck_pain = deck.annotate(user_pain=Subquery(user_pain))#.values('user_pain')
  # Now sort but user pain, descending, with nulls first
  deck_sorted = deck_pain.order_by(F('user_pain').desc(nulls_first=True))

  # for idx, card in enumerate(deck):
  #   Deck.objects.create(profile=user.profile, question=deck[idx], order_idx=(idx+1))
  for idx, card in enumerate(deck_sorted):
    Deck.objects.create(profile=user.profile, question=card, order_idx=(idx+1))

  return redirect('deck_show', tag=tag, idx=1)

@login_required
def card_community(request, **kwargs):
  profile = request.user.profile
  if 'idx' in kwargs:
    deck = Deck.objects.filter(profile=profile)
    question = deck.get(order_idx=kwargs['idx']).question #gets the single card in question
  else:
    question = Question.objects.get(pk=kwargs['pk'])

  card_tags = question.tags.all()

  # Cache all public answers for current question, and all votes for those answers
  answers = Answer.objects.filter(public=True,question=question).prefetch_related('question')
  votes = Vote.objects.filter(answer__question=question).prefetch_related('answer')
  ## get all the users votes
  user_votes = votes.filter(profile=profile)
  ## append them to the relevant answer
  user_votes = votes.filter(profile=profile,answer_id=OuterRef('pk')).values('vote')
  answers = answers.annotate(user_vote=Subquery(user_votes))

  ## Count up all the votes and sort by the top Answers
  ## Step 1: Count up the yes' and the no's
  # 1. a) Subqueries: Count up number of votes, filtered by answer and vote type. Saves it to 'yes' or 'no', and returns it.
  count_yes = votes.filter(answer__id=OuterRef('pk'),vote=1).values('vote').annotate(yes=Count('vote')).values('yes')
  count_no = votes.filter(answer__id=OuterRef('pk'),vote=-1).values('vote').annotate(no=Count('vote')).values('no')
  # 1. b) Main outer queries: Runs subquery on all answers for current question, saving returned vote counts to each answer.
  # Note: The only thing Coalesce is doing is replacing any 'none' responses with '0'
  answers = answers.annotate(yes_count=Coalesce(Subquery(count_yes, output_field=IntegerField()), 0))
  answers = answers.annotate(no_count=Coalesce(Subquery(count_no, output_field=IntegerField()), 0))
  # answers[0].yes_count
  # answers[0].no_count
  ## Step 2: Subtract no's from yes' and save as the total. (Coalesce setting 0's instead of Nones makes this work)
  annotated = answers.annotate(total_vote=ExpressionWrapper(F('yes_count') - F('no_count'), output_field=IntegerField()))
  # annotated[0].total_vote
  ## Step 3: Sort by the new total_vote field, highest count first.
  annotated_sorted = annotated.order_by(F('total_vote').desc(nulls_last=True))

  ## Notes on aggregate Sum vs annotate Count:
  # Aggregate works for individual answers, but you can't put .aggregate() inside a subquery. It evaluates right away.
  # sum_votes = Vote.objects.filter(answer__id=1).aggregate(yes=Sum('vote'))
  # So instead we need to count up all the Yes votes and all the No votes, and then subtract them

  values = {'question': question, 'card_tags': card_tags, 'answers': annotated_sorted}
  values['community'] = Answer.objects.filter(question=question, public=True).exists()
  with suppress(ObjectDoesNotExist):
    print('no valuse')
    values['current_answer'] = Answer.objects.get(author=profile, question=question)
    print('curent answer value', values['current_answer'])
  with suppress(ObjectDoesNotExist):
    values['pain'] = Pain.objects.filter(profile=profile, question=question).latest('time_stamp')#.order_by('-time_stamp')[0:1].get()
  return render(request, 'flashr/card_community.html', values)

## API Endpoints
# Send PAIN API Endpoint
def send_pain(request):
  # print(request.POST)
  if request.method == 'POST':
    profile = request.user.profile
    pain_level = request.POST['level']
    question = Question.objects.get(id=request.POST['question_id'])
    Pain.objects.create(level=pain_level, question=question, profile=profile)

    response = {}
    response['status'] = 200
    response['pain_level'] = pain_level

    return HttpResponse(json.dumps(response), content_type="application/json")
  else:
    return HttpResponse(json.dumps({"error": "wasn't a POST request"}), content_type="application/json")

# Send Answer API Endpoint
def send_answer(request):
  print(request.POST)
  if request.method == 'POST':
    profile = request.user.profile
    content = request.POST['content']
    public = ((True, False) [int(request.POST['public']) == 1 ])
    question = Question.objects.get(id=request.POST['question_id'])

    with suppress(ObjectDoesNotExist):
      Answer.objects.get(author=profile,question=question ).delete()

    Answer.objects.create(content=content, public=public, question=question, author=profile)

    response = {}
    response['status'] = 200
    response['content'] = content
    response['public'] = public

    return HttpResponse(json.dumps(response), content_type="application/json")
  else:
    return HttpResponse(json.dumps({"no info": "wasn't a POST request"}), content_type="application/json")

# Send Vote API Endpoint
def send_vote(request):
  # print(request.POST)
  if request.method == 'POST':
    profile = request.user.profile
    vote_choice = int(request.POST['vote'])
    answer = Answer.objects.get(id=request.POST['answer_id'])
    action = request.POST['action']

    # print('vote:', vote_choice)
    # print('type? ', type(vote_choice))
    # print('answer :', answer)
    response = {}
    # Validate the vote value for the expected values
    if (vote_choice == 1) or (vote_choice == -1):
      # print('yes or no vote detected...')
      # Delete any existing votes from this user for this answer
      Vote.objects.filter(profile=profile, answer=answer).delete()
      # Add the user's vote for this question
      Vote.objects.create(vote=vote_choice, answer=answer, profile=profile)
      response['status'] = 200
      response['vote'] = vote_choice
      response['a_id'] = answer.id
    elif (vote_choice == 0):
      # print('un-vote detected...')
      # 0 means they are 'un-voting', ie taking back a vote
      # Delete any existing votes from this user for this answer
      Vote.objects.filter(profile=profile, answer=answer).delete()
      response['status'] = 200
      response['vote'] = vote_choice
      response['action'] = action
      response['a_id'] = answer.id
    else:
      # print('invalid vote detected...')
      # Not a valid vote
      response['status'] = 400
      response['error'] = "Invalid vote option"

    return HttpResponse(json.dumps(response), content_type="application/json")
  else:
    return HttpResponse(json.dumps({"error": "wasn't a POST request"}), content_type="application/json")


import os
import socket
import struct
import time
from . import logging

def main(monitor_file, options):
    sel_src_port, sel_dest_port, log, sel_time = options.values()
    logger = logging.get_logger("scanner", log)

    with open(monitor_file, 'w') as fp:
        pass

    def perform_action(content):
        os.utime(monitor_file, (time.time(), time.time()))
        logger.info(content)

    no_ports = not sel_src_port and not sel_dest_port
    logger.info(f"Source: {sel_src_port}, Destination: {sel_dest_port}")
    logger.info(f"Service will monitor traffic for: {int(sel_time * 3600)} seconds")

    conn = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(3))
    while True:
        raw_data, addr = conn.recvfrom(65536)
        dest_mac, src_mac, eth_proto, data = ethernet_frame(raw_data)
        if eth_proto == 8:
            (version, header_length, ttl, proto, src, target, data) = ipv4_packet(data)
            if proto == 6:
                (src_port, dest_port, seq, ack, flag_urg, flag_ack, flag_psh, flag_rst, flag_syn, flag_fin, content) = tcp_segment(data)
                if no_ports:
                    perform_action(content)
                else:
                    if sel_src_port and src_port == sel_src_port:
                        perform_action(content)
                    if sel_dest_port and dest_port == sel_dest_port:
                        perform_action(content)

def ethernet_frame(data):
    dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])
    return get_mac_addr(dest_mac), get_mac_addr(src_mac), socket.htons(proto), data[14:]

def get_mac_addr(bytes_addr):
    return ':'.join(map('{:02x}'.format, bytes_addr)).upper()

def ipv4_packet(data):
    version_header_length = data[0]
    version = version_header_length >> 4
    header_length = (version_header_length & 15)  * 4
    ttl, proto, src, target = struct.unpack('! 8x B B 2x 4s 4s', data[:20])
    return version, header_length, ttl, proto, ipv4(src), ipv4(target), data[header_length:]

def ipv4(addr):
    return '.'.join(map(str, addr))

def tcp_segment(data):
    (src_port, dest_port, seq, ack, off_flag) = struct.unpack('! H H L L H', data[:14])
    offset = (off_flag >> 12) * 4
    flag_urg = (off_flag & 32) * 5
    flag_ack = (off_flag & 16) * 4
    flag_psh = (off_flag & 8) * 3
    flag_rst = (off_flag & 4) * 2
    flag_syn = (off_flag & 2) * 1
    flag_fin = (off_flag & 1)
    return src_port, dest_port, seq, ack, flag_urg, flag_ack, flag_psh, flag_rst, flag_syn, flag_fin, data[offset:]

if __name__ == "__main__":
    main('/tmp/.monitor')


import os
import math
import numpy as np
import tensorflow as tf
from sklearn.utils import shuffle
from tensorflow.contrib.layers import flatten
from tensorflow.examples.tutorials.mnist import input_data
from tensorflow.python.framework.graph_util import convert_variables_to_constants

mnist = input_data.read_data_sets('MNIST_data', one_hot=True,reshape=False)

index=[]
file=open('index.txt','r')
for line in file:
    index.append(int(line.strip('\n')))

test_images=np.delete(mnist.test.images,index,axis=0)
test_labels=np.delete(mnist.test.labels,index,axis=0)

TEST_KEEP_PROB = 1.0

def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

tf.GraphKeys.PRUNING_MASKS = "pruning_masks"

x = tf.placeholder("float", shape=[None,28,28,1],name='input_node')
y_ = tf.placeholder("float",shape=[None,10],name='input_labels')

# TODO: Layer 1: Convolutional. Input = 28x28x1. Output = 28x28x6.
conv1_w = weight_variable([5,5,1,6])
# prune_conv1 = tf.Variable(tf.ones_like(conv1_w),trainable=False, collections=[tf.GraphKeys.PRUNING_MASKS],name='prune_conv1')
# conv1_pruned = tf.multiply(conv1_w, prune_conv1) #element-wise multiplication
conv1_b = bias_variable([6])
# conv1 = tf.nn.conv2d(x,conv1_pruned, strides = [1,1,1,1], padding = 'SAME') + conv1_b
conv1 = tf.nn.conv2d(x,conv1_w, strides = [1,1,1,1], padding = 'SAME') + conv1_b
conv1 = tf.nn.relu(conv1)

# TODO: Pooling. Input = 28x28x6. Output = 14x14x6.
pool_1 = tf.nn.max_pool(conv1,ksize = [1,2,2,1], strides = [1,2,2,1], padding = 'VALID')

# TODO: Layer 2: Convolutional. Output = 10x10x16.
conv2_w = weight_variable([5,5,6,16])
# prune_conv2 = tf.Variable(tf.ones_like(conv2_w),trainable=False, collections=[tf.GraphKeys.PRUNING_MASKS],name='prune_conv2')
# conv2_pruned = tf.multiply(conv2_w, prune_conv2) #element-wise multiplication
conv2_b = bias_variable([16])
conv2 = tf.nn.conv2d(pool_1, conv2_w, strides = [1,1,1,1], padding = 'VALID') + conv2_b
conv2 = tf.nn.relu(conv2)

# TODO: Pooling. Input = 10x10x16. Output = 5x5x16.
pool_2 = tf.nn.max_pool(conv2, ksize = [1,2,2,1], strides = [1,2,2,1], padding = 'VALID')

# TODO: Flatten. Input = 5x5x16. Output = 400.
fc1 = flatten(pool_2)

# TODO: Layer 3: Fully Connected. Input = 400. Output = 120.
fc1_w = weight_variable([400,120])
prune_mask1 = tf.Variable(tf.ones_like(fc1_w),trainable=False, collections=[tf.GraphKeys.PRUNING_MASKS],name='prune_mask1')
fc1_pruned = tf.multiply(fc1_w, prune_mask1) #element-wise multiplication
fc1_b = bias_variable([120])
fc1 = tf.matmul(fc1,fc1_pruned) + fc1_b
# fc1 = tf.matmul(fc1,fc1_w) + fc1_b
fc1 = tf.nn.relu(fc1)
keep_prob1 = tf.placeholder("float",name='keep_prob1')
fc1_drop = tf.nn.dropout(fc1, keep_prob1)

# TODO: Layer 4: Fully Connected. Input = 120. Output = 84.
fc2_w = weight_variable([120,84])
prune_mask2 = tf.Variable(tf.ones_like(fc2_w), trainable=False, collections=[tf.GraphKeys.PRUNING_MASKS],name='prune_mask2')
fc2_pruned = tf.multiply(fc2_w, prune_mask2)
fc2_b = bias_variable([84])
fc2 = tf.matmul(fc1_drop,fc2_pruned) + fc2_b
# fc2 = tf.matmul(fc1_drop,fc2_w) + fc2_b
fc2 = tf.nn.relu(fc2)
keep_prob2 = tf.placeholder("float",name='keep_prob2')
fc2_drop = tf.nn.dropout(fc2, keep_prob2)

# TODO: Layer 5: Fully Connected. Input = 84. Output = 10.
fc3_w = weight_variable([84,10])
prune_mask3 = tf.Variable(tf.ones_like(fc3_w), trainable=False, collections=[tf.GraphKeys.PRUNING_MASKS],name='prune_mask3')
fc3_pruned = tf.multiply(fc3_w, prune_mask3)
fc3_b = bias_variable([10])
logits = tf.matmul(fc2_drop,fc3_pruned) + fc3_b
# logits = tf.matmul(fc2_drop,fc3_w) + fc3_b
y = tf.nn.softmax(logits, name='output_node')

cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y_)
# l2_loss = tf.nn.l2_loss(tf.concat([tf.reshape(conv1_w, [-1]),tf.reshape(conv2_w, [-1]),tf.reshape(fc1_w, [-1]), tf.reshape(fc2_w, [-1]), tf.reshape(fc3_w, [-1])],0))
l2_loss = tf.nn.l2_loss(tf.concat([tf.reshape(fc1_w, [-1]), tf.reshape(fc2_w, [-1]), tf.reshape(fc3_w, [-1])],0))
l2_weight_decay = 0.001
loss = cross_entropy + l2_loss * l2_weight_decay
train_step = tf.train.AdamOptimizer(1e-3).minimize(loss)
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

threshold = 0.0001
t1 = tf.sqrt(tf.nn.l2_loss(fc1_w)) * threshold
t2 = tf.sqrt(tf.nn.l2_loss(fc2_w)) * threshold
t3 = tf.sqrt(tf.nn.l2_loss(fc3_w)) * threshold
# t4 = tf.sqrt(tf.nn.l2_loss(conv1_w)) * threshold
# t5 = tf.sqrt(tf.nn.l2_loss(conv2_w)) * threshold

nonzero_indicator1 = tf.to_float(tf.not_equal(fc1_w, tf.zeros_like(fc1_w)))
nonzero_indicator2 = tf.to_float(tf.not_equal(fc2_w, tf.zeros_like(fc2_w)))
nonzero_indicator3 = tf.to_float(tf.not_equal(fc3_w, tf.zeros_like(fc3_w)))
# nonzero_indicator4 = tf.to_float(tf.not_equal(conv1_w, tf.zeros_like(conv1_w)))
# nonzero_indicator5 = tf.to_float(tf.not_equal(conv2_w, tf.zeros_like(conv2_w)))
count_parameters1 = tf.reduce_sum(nonzero_indicator1)
count_parameters2 = tf.reduce_sum(nonzero_indicator2)
count_parameters3 = tf.reduce_sum(nonzero_indicator3)
# count_parameters4 = tf.reduce_sum(nonzero_indicator4)
# count_parameters5 = tf.reduce_sum(nonzero_indicator5)

indicator_matrix1 = tf.multiply(tf.to_float(tf.greater_equal(fc1_w, tf.ones_like(fc1_w) * t1)), prune_mask1)
indicator_matrix2 = tf.multiply(tf.to_float(tf.greater_equal(fc2_w, tf.ones_like(fc2_w) * t2)), prune_mask2)
indicator_matrix3 = tf.multiply(tf.to_float(tf.greater_equal(fc3_w, tf.ones_like(fc3_w) * t3)), prune_mask3)
# indicator_matrix4 = tf.multiply(tf.to_float(tf.greater_equal(conv1_w, tf.ones_like(conv1_w) * t4)), prune_conv1)
# indicator_matrix5 = tf.multiply(tf.to_float(tf.greater_equal(conv2_w, tf.ones_like(conv2_w) * t5)), prune_conv2)

update_mask1 = tf.assign(prune_mask1, indicator_matrix1)
update_mask2 = tf.assign(prune_mask2, indicator_matrix2)
update_mask3 = tf.assign(prune_mask3, indicator_matrix3)
# update_mask4 = tf.assign(prune_conv1, indicator_matrix4)
# update_mask5 = tf.assign(prune_conv2, indicator_matrix5)
# update_all_masks = tf.group(update_mask1, update_mask2, update_mask3, update_mask4, update_mask5)
update_all_masks = tf.group(update_mask1, update_mask2, update_mask3)

prune_fc1 = fc1_w.assign(fc1_pruned)
prune_fc2 = fc2_w.assign(fc2_pruned)
prune_fc3 = fc3_w.assign(fc3_pruned)
# prune_conv1w = conv1_w.assign(conv1_pruned)
# prune_conv2w = conv2_w.assign(conv2_pruned)
# prune_all = tf.group(prune_fc1, prune_fc2, prune_fc3,prune_conv1w,prune_conv2w)
prune_all = tf.group(prune_fc1, prune_fc2, prune_fc3)

sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess.run(tf.variables_initializer(tf.get_collection(tf.GraphKeys.PRUNING_MASKS)))

def print_validation_accuracy(iter):
    feed_dict = {x: mnist.validation.images, y_: mnist.validation.labels, keep_prob1: TEST_KEEP_PROB, keep_prob2: TEST_KEEP_PROB}
    print("step %d validation accuracy %g" % (iter,sess.run(accuracy, feed_dict=feed_dict)))

def print_test_accuracy():
    feed_dict = {x:test_images, y_:test_labels, keep_prob1: TEST_KEEP_PROB, keep_prob2: TEST_KEEP_PROB}
    print("test accuracy %g" % sess.run(accuracy, feed_dict=feed_dict))

def train(iterations=10000, kp1=0.5, kp2=0.5):
    print('######### Started Training ##########')
    for i in range(iterations):
        batch_xs, batch_ys = mnist.train.next_batch(50)
        if i % 100 == 0:
            feed_dict = {x: batch_xs, y_: batch_ys, keep_prob1: TEST_KEEP_PROB, keep_prob2: TEST_KEEP_PROB}
            train_accuracy = sess.run(accuracy, feed_dict=feed_dict)
            print("step %d training accuracy %g" % (i, train_accuracy))
        if i % 1000==0:
            print_validation_accuracy(i)
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys, keep_prob1: kp1, keep_prob2: kp2})
    print_test_accuracy()
    print('######### Done Training ##########')

def print_parameter_counts():
    print("W Parameter Counts:")
    print("FC1_Parameters: {0}".format(sess.run(count_parameters1)))
    print("FC2_Parameters: {0}".format(sess.run(count_parameters2)))
    print("FC3_Parameters: {0}".format(sess.run(count_parameters3)))

def calculate_new_keep_prob(original_keep_prob, original_connections, retraining_connections):
    return 1.0 - ((1.0 - original_keep_prob) * math.sqrt(retraining_connections / original_connections))

def compress(times=1):
    kp1 = kp2 = 0.5
    for i in range(times):
        print('######### Start ##########')
        print("Compressing iteration " +str(i + 1))
        c1 = sess.run(count_parameters1)
        c2 = sess.run(count_parameters2)
        print("Before pruning")
        print_parameter_counts()
        sess.run(update_all_masks)
        sess.run(prune_all)
        c1_retrain = sess.run(count_parameters1)
        c2_retrain = sess.run(count_parameters2)
        kp1 = calculate_new_keep_prob(kp1, c1, c1_retrain)
        kp2 = calculate_new_keep_prob(kp2, c2, c2_retrain)
        if(i==4):
            continue
        else:
            train(10000, kp1, kp2)
        print("After pruning")
        print_parameter_counts()
        print('######### Done ##########')
        # save_wts(i)
        write_to_pb(i)

def save_wts(k):
    w = [p.eval(sess) for p in tf.trainable_variables()]
    np.savez('./Wts_{}.npz'.format(k), *w)

def write_to_pb(iter):
    # name='models2/compressed_conv_MNIST_'+str(iter+1)+'.pb'
    minimal_graph = convert_variables_to_constants(sess,sess.graph_def,["output_node"])
    tf.train.write_graph(minimal_graph,'.','z.pb', as_text=False)
    os.system('gzip -c '+name+' > '+name+'.gz')

if __name__ == '__main__':
    sess.run(tf.global_variables_initializer())
    train()
    compress(5)