import sys
import pygame
import time
import random

class Game():
    def __init__(self):
        # basic colors
        self.WHITE = (255,255,255)
        self.BLACK = (0,0,0)
        self.RED = (255,0,0)
        self.GREEN = (0,255,0)
        self.BLUE =(0,244,204)

        self.size_block = 30
        self.margin = 1
        self.under_margin = 25
        self.count_block = 20
        self.captiom = 'Snake'
        self.board_color = self.BLACK

        self.screen_w = self.screen_h = self.size_block*self.count_block + self.margin*(self.count_block+1)
        self.window = (self.screen_w, self.screen_h + self.under_margin)

        self.fps_controller = pygame.time.Clock()
        self.FPS = 5

        # count food
        self.score = 0

    def init_and_check_errors(self):
        check_errors = pygame.init()
        if check_errors[1] > 0:
            sys.exit(0)
        else:
            print('OK')

    def set_surface_and_title(self):
        self.screen = pygame.display.set_mode(self.window)
        pygame.display.set_caption(self.captiom)
        self.screen.fill(self.board_color)

    def draw_the_blocks(self, color, row, column):
        x = self.size_block * row + self.margin * (row + 1)
        y = self.size_block * column + self.margin * (column + 1)
        pygame.draw.rect(self.screen, color, (x, y, self.size_block, self.size_block))

    def draw_the_game_board(self):
        for row in range(self.count_block):
            for column in range(self.count_block):
                if (row + column) % 2 == 0:
                    self.color = self.WHITE
                else:
                    self.color = self.BLUE

                game.draw_the_blocks(self.color, row, column)

    def event_loop(self, change_to):
        """ Отслеживание нажатий клавиш """

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit(0)
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP or event.key == ord('w'):
                    change_to = 'UP'
                elif event.key == pygame.K_DOWN or event.key == ord('s'):
                    change_to = 'DOWN'
                elif event.key == pygame.K_RIGHT or event.key == ord('d'):
                    change_to = 'RIGHT'
                elif event.key == pygame.K_LEFT or event.key == ord('a'):
                    change_to = 'LEFT'
                elif event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit(0)

        return change_to

    def refresh_screen(self):
        pygame.display.flip()
        self.fps_controller.tick(self.FPS)

    def show_score(self, status = 1):
        score_font = pygame.font.SysFont('monaco', 36)
        score_surf = score_font.render(
            'Score: {0}'.format(self.score), True, self.WHITE, self.BLACK)
        score_rect = score_surf.get_rect()

        if status == 1:
            score_rect.midtop = (self.screen_w//2, self.screen_h * 0.999)
        else:
            score_rect.midtop = (self.screen_w//2, self.screen_h//2)

        self.screen.blit(score_surf, score_rect)

    def game_over(self):
        self.screen.fill(self.BLACK)
        game_over_font = pygame.font.SysFont('KOYSAN', 30)
        game_over_surf = game_over_font.render(
            'GAME OVER', True, self.BLUE
        )
        game_over_rect = game_over_surf.get_rect()
        game_over_rect.midtop = ((self.screen_w//2, self.screen_h//2 - 50))
        self.screen.blit(game_over_surf, game_over_rect)

        self.show_score(0)
        pygame.display.update()
        time.sleep(1)
        pygame.quit()
        sys.exit(0)

class Snake():
    def __init__(self, snake_color):
        self.snake_color = snake_color
        self.direction = 'RIGHT'
        self.snake_speed = 1
        self.x = 10
        self.y = 10
        self.snake_pos = [
            [self.x, self.y],
        ]

        self.chanched_to = self.direction

    def check_crash(self):

        # Столкновение со стенкой
        if not -1 <= self.x <= game.count_block or not -1 <= self.y <= game.count_block:
            game.game_over()

        # Столкновение с телом
        for block in self.snake_pos[1:]:
            if (block[0] == self.snake_pos[0][0] and
                block[1] == self.snake_pos[0][1]):
                game.game_over()

    def check_eat(self):
        if self.snake_pos[0][0] == food.food_pos[0] and self.snake_pos[0][1] == food.food_pos[1]:
            game.score += 1
            snake.snake_pos.append(food.food_pos)
            food.food_pos = [random.randint(1, game.count_block-1),
                            random.randint(1, game.count_block-1)]
            print('YUMMY')

    def move_on(self):
        if any((self.direction == 'UP' and not self.chanched_to == 'DOWN',
                self.direction == 'DOWN' and not self.chanched_to == 'UP',
                self.direction == 'RIGHT' and not self.chanched_to == 'LEFT',
                self.direction == 'LEFT' and not self.chanched_to == 'RIGHT')):
            self.direction = self.chanched_to

        # Двигаемся в сторону
        if self.direction == 'UP':
            self.y -= self.snake_speed
        elif self.direction == 'DOWN':
            self.y += self.snake_speed
        elif self.direction == 'RIGHT':
            self.x += self.snake_speed
        elif self.direction == 'LEFT':
            self.x -= self.snake_speed

        for block in self.snake_pos:
            x, y = block
            game.draw_the_blocks(self.snake_color, x, y)

        self.snake_pos.append([self.x, self.y])
        self.snake_pos = self.snake_pos[1:]

        # Проверяем столкновение со стенкой или с телом змейки
        self.check_crash()
        self.check_eat()

class Food():
    def __init__(self, food_color):
        self.food_color = food_color
        self.food_pos = [random.randint(1, game.count_block-1),
                        random.randint(1, game.count_block-1)]

    def draw_food(self):
        game.draw_the_blocks(self.food_color, self.food_pos[0], self.food_pos[1])

game = Game()
snake = Snake(game.GREEN)
food = Food(game.RED)

game.init_and_check_errors()
game.set_surface_and_title()

while True:
    game.draw_the_game_board()

    snake.chanched_to = game.event_loop(snake.chanched_to)
    snake.move_on()
    print(snake.snake_pos)

    food.draw_food()

    game.show_score()
    game.refresh_screen()


import os, re
from collections import deque

input_path = os.path.join(os.getcwd(),'9','input.txt')

data = open(input_path).read()

expression = re.compile('\d+')
result = expression.findall(data)
num_players, num_marbles = int(result[0]),int(result[1])
num_marbles *= 100
num_marbles += 1 # The input is in points, but there is an extra marble worth 0

scores = [0 for i in range(num_players)]
available = [i for i in reversed(range(num_marbles))]

circle = deque([])
marble = available.pop()
circle.append(marble)

def insert_marble(player):
    marble = available.pop()
    if marble % 23 != 0:
        if len(circle) > 1:
            circle.rotate(-2)

        circle.appendleft(marble)
    else:
        scores[player] += marble
        circle.rotate(7)
        scores[player] += circle.popleft()

while len(available) > 0:
    for i in range(num_players):
        if len(available) == 0:
            break
        insert_marble(i)
        # print(f'{i+1}: {circle}')

print('Scores:')
print(f'High Score: {max(scores)}')
#for i in range(num_players):
#    print(f'{i+1}: {scores[i]}')


__author__ = 'ee364g07'

# $Author: ee364g07 $
# $Date: 2015-04-20 15:54:28 -0400 (Mon, 20 Apr 2015) $
# $Revision: 79275 $
# $HeadURL: svn+ssh://ece364sv@ecegrid-thin1/home/ecegrid/a/ece364sv/svn/S15/students/ee364g07/Lab11/SteganographyBrowser.py $
# $Id: SteganographyBrowser.py 79275 2015-04-20 19:54:28Z ee364g07 $

import sys
import os
import glob
import re

from PySide.QtGui import *
from PySide.QtCore import *
from NewSteganography import *
from SteganographyGUI import *

class SteganographyBrowser(QMainWindow, Ui_MainWindow):
    """
    this function adds items to the tree widget based on if the item has a message, if it does it adds the message
    as a child. It also sets different colors to the different types of items entered to the list

    """
    def additem(self, path, item):
        self.fileTreeWidget.addTopLevelItem(item)
        horizontalread = NewSteganography(path, 'horizontal')
        verticalread = NewSteganography(path, 'vertical')
        ah,bh = horizontalread.checkIfMessageExists()
        av,bv = verticalread.checkIfMessageExists()
        if ah == True:
            item.setForeground(0,QtGui.QBrush(QtGui.QColor("#FF0000")))
            item.addChild(QTreeWidgetItem([bh]))
            self.childdict[item] = [bh, 'horizontal']
        elif av == True:
            item.setForeground(0,QtGui.QBrush(QtGui.QColor("#FF0000")))
            item.addChild(QTreeWidgetItem([bv]))
            self.childdict[item] = [bv, 'vertical']
        else:
            item.setForeground(0,QtGui.QBrush(QtGui.QColor("#0000FF")))

    def __init__(self, parent=None):
        """
        the initialization function: this pops up a message box asking for the folder you want to get the files from
        it also creates a dictionary of filepaths and message types for future use in the rest of the application
        it goes through all the files in the folder and sends them to the additem function to be added to the tree widget
        :param parent:
        :return:
        """
        #initial set-up
        super(SteganographyBrowser, self).__init__(parent)
        self.setupUi(self)

        #message box pop-up
        msgbox = QMessageBox()
        msgbox.setText("Select a folder")
        msgbox.setStandardButtons(QMessageBox.Open | QMessageBox.Cancel)
        option = msgbox.exec_()
        if option == QMessageBox.Open:
            self.fpdict = {} #filepath dictionary whose keys are the QTreeWidget Items
            self.childdict = {} #child dictionary to figure out the type of message in a medium
            folderPath = QFileDialog.getExistingDirectory(None, 'Select a folder:')
            if folderPath:
                files = glob.glob('{}/*.png'.format(folderPath))
                for file in files:
                     filepath = file
                     file = file.split('/')
                     item = QTreeWidgetItem(file[-1:])
                     foldername = file[-2:-1]
                     self.fpdict[item] = filepath
                     self.additem(filepath, item)
            else:
                exit()

            #disabling of buttons and views
            self.fileTreeWidget.headerItem().setText(0, foldername[0])
            self.btnExtract.setEnabled(False)
            self.btnWipeMedium.setEnabled(False)
            self.viewMedium.setHidden(True)
            self.viewMessage.setHidden(True)

            """Reactions when buttons are clicked"""

            self.fileTreeWidget.itemClicked.connect(self.clickedtree)
            self.btnExtract.clicked.connect(self.extract)
            self.btnWipeMedium.clicked.connect(self.wipe)

        elif option == QMessageBox.Cancel:
            exit()

    def displayimageinmedium(self, path):
        """
        given the path of the image, it displays the image in the medium
        :param path:
        :return:
        """
        scene = QGraphicsScene()
        pix = scene.addPixmap(QPixmap(path))
        self.viewMedium.setScene(scene)
        self.viewMedium.fitInView(pix, Qt.KeepAspectRatio)

    def displayimageinmessage(self, path):
        """
        given the path of the image, it displays the image in the message space given

        :param path:
        :return:
        """
        scene = QGraphicsScene()
        pix = scene.addPixmap(QPixmap(path))
        self.viewMessage.setScene(scene)
        self.viewMessage.fitInView(pix, Qt.KeepAspectRatio)

    def clickedtree(self):
        """
        this is the reaction button for  when an item in the tree is clicked . It sets the item in use to the current item
        that was clicked and gets the file path and child using the item as a dictionary key. It also selects from Line Edit
        and graphics view based on what type of message is in the medium
        :return:
        """
        parentcheck = str(self.fileTreeWidget.currentItem().parent())
        if parentcheck == 'None':
            current = self.fileTreeWidget.currentItem()
            num_of_children = current.childCount()
        else:
            current = self.fileTreeWidget.currentItem().parent()
            num_of_children = 1

        if num_of_children == 0:
            self.btnExtract.setEnabled(False)
            self.btnWipeMedium.setEnabled(False)
            self.viewMedium.setHidden(False)
            self.viewMessage.setHidden(True)
            self.txtMessage.setHidden(True)
            self.lblTextMessage.setDisabled(True)
            self.lblImageMessage.setDisabled(True)
            self.displayimageinmedium(self.fpdict[current])
        elif num_of_children == 1:
            self.btnExtract.setEnabled(True)
            self.btnWipeMedium.setEnabled(True)
            self.viewMedium.setHidden(False)
            self.viewMessage.setHidden(False)
            self.txtMessage.setHidden(False)
            self.lblTextMessage.setDisabled(True)
            self.lblImageMessage.setDisabled(True)
            self.displayimageinmedium(self.fpdict[current])

            #differentiating between Line Edit and Graphics VIew
            if self.childdict[current][0] == 'Text':
                self.stackMessage.setCurrentIndex(1)
                self.txtMessage.setPlainText("")
            else:
                self.stackMessage.setCurrentIndex(0)
                scene = QGraphicsScene()
                self.viewMessage.setScene(scene)

    def extract(self):
        """
        extracts the message in the medium and places it in the graphics view or line edit based on what type the message is
        :return:
        """
        parentcheck = str(self.fileTreeWidget.currentItem().parent())
        if parentcheck == 'None':
            current = self.fileTreeWidget.currentItem()
        else:
            current = self.fileTreeWidget.currentItem().parent()
        path = self.fpdict[current]
        direction = self.childdict[current][1]
        medium = NewSteganography(path, direction)
        ex_message = medium.extractMessageFromMedium()
        ex_messagetype = ex_message.messageType

        """Options for message extraction put back"""

        if ex_messagetype == 'Text':
            dcode = ex_message.findmessage()                      #finds the message in the xml file
            msg = base64.b64decode(bytes(dcode, "UTF-8"))   #decodes the base64 message
            msg = str(msg, "UTF-8")
            self.txtMessage.setPlainText(msg)
            self.btnExtract.setEnabled(False)
        elif ex_messagetype == 'ColorImage':
            ex_message.saveToTarget('help.png')
            self.displayimageinmessage('help.png')
            self.btnExtract.setEnabled(False)
        elif ex_messagetype == 'GrayImage':
            ex_message.saveToTarget('help.png')
            self.displayimageinmessage('help.png')
            self.btnExtract.setEnabled(False)

    def wipe(self):
        """
        cleans the message from the medium and refreshes the item based on the new properties of the medium
        :return:
        """
        msgbox = QMessageBox()
        msgbox.setText("Are you sure you want to wipe the medium?")
        msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        option = msgbox.exec_()
        if option == QMessageBox.No:
            pass
        elif option == QMessageBox.Yes:
            self.viewMessage.setHidden(True)
            self.txtMessage.setHidden(True)
            current = self.fileTreeWidget.currentItem()
            path = self.fpdict[current]
            direction = self.childdict[current][1]
            medium = NewSteganography(path, direction)
            medium.wipeMedium()
            scene = QGraphicsScene()
            self.viewMessage.setScene(scene)
            current.takeChild(0)
            self.fileTreeWidget.removeItemWidget(current, 0)
            self.additem(path, current)

if __name__ == "__main__":
    currentApp = QApplication(sys.argv)
    currentForm = SteganographyBrowser()
    currentForm.show()
    currentApp.exec_()


import csv
import json
import boto3
from botocore.exceptions import ClientError

from django.conf import settings
from django.core.management.base import BaseCommand

from regluit.core.models import Edition, Identifier

s3 = boto3.resource('s3',
         aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
         aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY)

class Command(BaseCommand):
    help = "translate doab ids to handles"
    def add_arguments(self, parser):
        parser.add_argument('filename', nargs='?', help="filename")
        parser.add_argument('--old_id', nargs='?', default=None, help="id to translate")

    def handle(self, filename, **options):
        self.stdout.write("doab ids to start: %s" % Identifier.objects.filter(type='doab').count())
        with open(filename, 'r') as jsonfile:
            newdoab = json.loads(jsonfile.read())
        done = 0
        if options['old_id']:
            to_do = Identifier.objects.filter(type='doab', value=options['old_id'])
        else:
            to_do = Identifier.objects.filter(type='doab')
        for doab in to_do:
            if doab.value.startswith("20.500.12854"):
                continue
            if doab.value in newdoab:
                # already done
                if Identifier.objects.filter(type='doab', value=newdoab[doab.value]).exists():
                    doab.delete()
                else:
                    old_cover_file_name = 'doab/%s/cover' % doab.value
                    new_cover_file_name = 'doab/%s' % newdoab[doab.value]
                    self.move_cover(old_cover_file_name, new_cover_file_name)
                    doab.value = newdoab[doab.value]
                    doab.save()
            else:
                doab.delete()
            done += 1
        self.stdout.write("doab ids at end: %s" % Identifier.objects.filter(type='doab').count())
        self.stdout.write("done:: %s" % done)

    def move_cover(self, old_name, new_name):
        if old_name == new_name:
            return
        old_url = "https://{}.s3.amazonaws.com/{}".format(
            settings.AWS_STORAGE_BUCKET_NAME, old_name)
        new_url = "https://{}.s3.amazonaws.com/{}".format(
            settings.AWS_STORAGE_BUCKET_NAME, new_name)
        copy_source = {
            'Bucket': settings.AWS_STORAGE_BUCKET_NAME,
            'Key': old_name
        }
        try:
            s3.meta.client.copy_object(
                CopySource=copy_source,
                Bucket=settings.AWS_STORAGE_BUCKET_NAME,
                Key=new_name, ACL='public-read')

            for ed in Edition.objects.filter(cover_image__contains=old_name):
                ed.cover_image = new_url
                ed.save()

            s3.meta.client.delete_object(Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=old_name,)
        except ClientError:
            self.stdout.write("problem moving %s to %s" % (old_name, new_name))


# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    ''' Solution bitch '''
    def mergeTwoLists(self, l1: ListNode, l2: ListNode):
        l1_ptr = l1
        l2_ptr = l2
        merge_list = ListNode()
        merge_ptr = merge_list
        #  set first value, then always update next
        if l1_ptr.val <= l2_ptr.val:
            merge_ptr.val = l1_ptr.val
            l1_ptr = l1_ptr.next
        else:
            merge_ptr.val = l2_ptr.val
            l2_ptr = l2_ptr.next

        while l1_ptr and l2_ptr:
            if l1_ptr.val <= l2_ptr.val:
                merge_ptr.next = ListNode(l1_ptr.val)
                l1_ptr = l1_ptr.next
            else:
                merge_ptr.next = ListNode(l2_ptr.val)
                l2_ptr = l2_ptr.next
            merge_ptr = merge_ptr.next

        while l1_ptr:
            merge_ptr.next = ListNode(l1_ptr.val)
            l1_ptr = l1_ptr.next
            merge_ptr = merge_ptr.next

        while l2_ptr:
            merge_ptr.next = ListNode(l2_ptr.val)
            l2_ptr = l2_ptr.next
            merge_ptr = merge_ptr.next

        return merge_list

    def print_list(self, l):
        ''' Prints a list '''
        p = l

        while p:
            print(p.val, end='')
            if p.next:
                print('->', end='')
            p = p.next
        print("")

l1 = ListNode(1, ListNode(2, ListNode(4, ListNode(5, ListNode(7)))))
l2 = ListNode(1, ListNode(3, ListNode(4, ListNode(6))))

Solution().print_list(l1)
Solution().print_list(l2)
Solution().print_list(Solution().mergeTwoLists(l1, l2))

# l1 = ListNode(0, ListNode(1, ListNode(2)))

# l2 = ListNode(5)
# print(f'l2={l1}, l2.val={l2.val}, l2.next={l2.next}')
# print("-----------------------------")
# p = l2
# p.next = ListNode(6)
# print(f'p={p}, p.val={p.val}, p.next={p.next}')
# print(f'l2={l2}, l2.val={l2.val}, l2.next={l2.next}')
# print("-----------------------------")
# p = p.next
# print(f'l2={l2}, l2.val={l2.val}, l2.next={l2.next}')
# p.next = ListNode(7)
# print(f'l2={l2}, l2.val={l2.val}, l2.next={l2.next}')

# # print(f'l1={l1}, l1.val={l1.val}, l1.next={l1.next}')
# # l1 = l1.next
# # print(f'l1={l1}, l1.val={l1.val}, l1.next={l1.next}')
# # l1 = l1.next
# # print(f'l1={l1}, l1.val={l1.val}, l1.next={l1.next}')
# # l1 = l1.next
# # print(f'l1={l1}, l1.val={l1.val}, l1.next={l1.next}')

# # print("============================")
# # while l1:
# #     print(f'val={l1.val}')
# #     l1 = l1.next
# print("============================")

# while l2:
#     print(f'val={l2.val}')
#     l2 = l2.next
# print("============================")

# while p:
#     print(f'val={p.val}')
#     p = p.next
# print("============================")

# # OUTPUT
# # ============================
# # 5
# # 6
# # 7
# # ============================
# # 5
# # 6
# # ============================

# # l2 = ListNode()
# # test = Solution().mergeTwoLists(l1, l2)
# # print(f'{test}')


#!/usr/bin/python3
import os,sys,time,importlib,simplejson
if sys.platform[0] == 'w': figpath=os.getenv('USERPROFILE') +os.sep+'.rt'+os.sep+ '.config.hash'
else: figpath=os.getenv('HOME') +os.sep+'.rt'+os.sep+ '.config.hash'
def getTable( file ):
		json_data={}
		if os.path.isfile(file):
				with open(file,'r', encoding="latin-1") as json_file: json_data = simplejson.load(json_file)
		return json_data
fig=getTable(figpath)
sys.path.append( fig['w']+'/widgets/python'.replace('/',os.sep) )


def solution(arr):
    answer = []
    if arr.count(2) == 0:
        answer = [-1]
    elif arr.count(2) == 1:
        answer = [2]
    else:
        min_idx = 100001
        max_idx = -1
        for i in range(len(arr)):
            if arr[i] == 2:
                if i < min_idx:
                    min_idx = i
                if i > max_idx:
                    max_idx = i

        answer = arr[min_idx:max_idx+1]

    return answer


"""
Jake Gaughan

CS200 Programming Assignment 1

Purpose: Computer Assisted Logical Truth Tables
            Take in group of string logical statements and return whether or not statement in question is true/false/n/a
Outside Code:
    n/a
Outside Collaboration:
    n/a
Sample Output:
>>> %Run CS200PA1.py
enter prove prompt: prove(‘A or not C’, ‘(A and B) or not C’, ‘C or B’))
True
>>>
Time Spent:
3 hrs
"""

CAPITALS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
logic_stats = ["not", "or", "and"]

def store_input(group_of_logic):
    """
    purpose of this function is to store the input as a set of strings
    inputs:
        input string
    outputs:
        cleaned set of inputs
    """
    inputs = []
    store =""
    count = 0
    #for the length of the statement (minus the "prove" at the beginning)
    for i in range(len(group_of_logic) - 6):
        z = 6 + i
        letter = group_of_logic[z]
        #figuring out the beginning / end of the boolean statement
        if letter == "‘" or letter == "’":
            count += 1
        #if it isn't a comma
        elif letter != "," :
            #add to current string
            store += letter
        #if it is the end of the statement then add the statement to the set and prepare for next statement
        if count == 2:
            inputs.append(store)
            store = ""
            count = 0
    #return the set
    return inputs

def get_variables(inputs):
    """
    purpose of this function is to figure out which variables are in the boolean statements
    inputs:
        set of inputs
    outputs:
        set of variables
    """
    variables = []
    #iterating through all sep. inputs
    for i in range(len(inputs)):
        statement = inputs[i]
        for j in range(len(statement)):
            #if the letter is capitalized and hasn't already been added, add it to the set
            if statement[j] in CAPITALS and not statement[j] in variables:
                variables.append(statement[j])
    #return the set of variables
    return variables

def truth_table(inputs, variables):
    """
    purpose of this funciton is to mimic a truth table and implement a proof by exhaustion
    inputs:
        set of all inputs
        set of all variables
    outputs:
        set of dictionaries
            each dictionary contains the boolean inputs that resulted in "True" for truth inputs
    """
    corrects = []
    #for every row of the truth table
    for c in range(2**(len(variables))):
        check = 0
        keys = {}
        #for every truth input statement
        for i in range(len(inputs) - 1):
            #select a specific truth input
            statement = inputs[i + 1]
            test = ""
            #iterate through the selected statement
            for j in range(len(statement)):
                #if the letter is a variable
                if statement[j] in variables:
                    #figuring out which variable the letter is
                    for letter in range(len(variables)):
                        if statement[j] == variables[letter]:
                            z = letter + 1
                            #figuring out how often the variable is supposed to oscilate between true and false
                            split = (2**(len(variables))) / (2**z)
                            break
                    #oscilating True
                    if ((split + c)//split) % 2 == 1:
                        keys[statement[j]] = "True"
                        test += "True"
                    #oscilating False
                    else:
                        keys[statement[j]] = "False"
                        test += "False"
                #if the letter is not a variable
                else:
                    test += statement[j]
            #if the truth input is actually true
            if eval(test):
                check += 1
            #if all truth inputs are true for a given row
            if check == (len(inputs) - 1):
                #append the dictionary of correct inputs
                corrects.append(keys)
    #return the set of correct inputs
    return corrects

def final_step(corrects, inputs):
    """
    purpose of this function is to test out the test input with the variables that we know result in truths from the input statements
    inputs:
        corrects, returned from truth_table
        inputs, the set of input statements
    outputs:
        n/a

    """
    #the first statement in inputs is the test input
    test = inputs[0]
    results = []
    #if there is nothing in corrects
    if len(corrects) == 0:
        print("not enough information")
        return
    #for every thing in corrects
    for j in range(len(corrects)):
        #selecting specific dictionary
        test_bools = corrects[j]
        test2 = ""
        #for every letter in the test statement
        for i in range(len(test)):
            #substitute the variables with boolean values
            if test[i] in test_bools.keys():
                test2 += test_bools[(test[i])]
            else:
                test2 += test[i]
        #add the boolean value of the statement to the result set
        results.append(eval(test2))
    #for all results
    for x in range(len(results) - 1):
        #if the results do not match
        if results[x+1] != results[x]:
            #inconclusive
            print("not enough information")
            return
    #if all of the results do match, print the result
    print(results[0])

if __name__ == "__main__":
    original = input("enter prove prompt: ")
    inputs = store_input(original)
    variables = get_variables(inputs)
    corrects = truth_table(inputs, variables)
    final_step(corrects,inputs)


from shared import *

searchbox = document.getElementById("searchbox")
searchresults = document.getElementById("searchresults")
showrandom = document.getElementById("showrandom")

async def update_grid():
    grid = document.querySelector(".grid")
    if grid and grid.masonry:
        grid.masonry.reloadItems()
        grid.masonry.layout()

async def do_search():
    document.location = "/search?q=" + searchbox.value

async def do_search_suggest():
    console.log(".")

async def searchbox_keyup(event):
    event.preventDefault()
    if event.keyCode == 13:
        do_search()

    if document.searchbounce > 0:
        clearTimeout(document.searchbounce)
    document.searchbounce = setTimeout(do_search_suggest, 500)

async def result_handler(event):
    anid = find_attr_parents(event.target, "anid")
    if anid:
        event.preventDefault()
        document.location = "/id/" + anid

async def set_location_to_search(event):
    event.preventDefault()
    document.location = "/search"

async def init():
    searchbox.addEventListener("keyup", searchbox_keyup)
    if showrandom:
        showrandom.addEventListener("click", set_location_to_search)
    searchresults.addEventListener("click", result_handler)
    searchbox.focus()

window.addEventListener("load", init)


################################################################################
# Watchdogs
#
# Created by Zerynth Team 2015 CC
# Authors: G. Baldi, D. Mazzei
################################################################################

##
## This example only works on a Secure Firmware enabled Virtual Machine!
##

import streams
# import the Secure Firmware module
# Check documentation here: https://docs.zerynth.com/latest/official/core.zerynth.stdlib/docs/official_core.zerynth.stdlib_sfw.html
import sfw

streams.serial()

sleep(2000)

# Check for reset reason
try:
    print("Watchdog triggered:",sfw.watchdog_triggered())
except Exception as e:
    print("Watchdog not suppported by this Virtual Machine!")
    while True:
        sleep(1000)

# Do something without fearing a reset
for x in range(10):
    sleep(1000)
    print("Printing something for a while, no watchdog can reset me! 8‑D")

# Configure watchdog in normal mode with a 5 seconds timeout
print("Configuring watchdog to a 5 seconds timeout...")
sfw.watchdog(0,5000)
sleep(100)

# Kick the watchdog every second
for x in range(10):
    sleep(1000)
    sfw.kick()
    print("Kick!")

# Stop kicking and wait for reset
while True:
    print("Printing something for a while waiting for the watchdog! D-8")
    sleep(1000)


# Gilbert Velasquez
# CS 4375: Theory of Operating Systems
# Dr. Freudenthal
# This file is a part of Lab Assignment #2 TCP File Transfer. This file act as the Client and
# is built from the demo code provided by Dr. Fruedenthal.

import socket,sys,re,time,os
sys.path.append("../lib")
import params
from read import my_getLine
from read import parseTCPInput
from socketFramed import framedSocket

switchesVarDefaults = (
    (('-s','--server'),'server',"127.0.0.1:50001"),
    (('-d','--delay'),'delay',"0"),
    (('-?','--usage'),"usage",False),
    )

progname = "framedClient"
paramMap = params.parseParams(switchesVarDefaults)

server,usage = paramMap["server"],paramMap["usage"]

if usage:
    params.usage()

try:
    serverHost,serverPort = re.split(":",server)
    serverPort = int(serverPort)
except:
    print("Can't parse server:port from '%s'" % server)
    sys.exit(1)

s = None
for res in socket.getaddrinfo(serverHost, serverPort, socket.AF_UNSPEC, socket.SOCK_STREAM):
    af, socktype,proto,canonname, sa = res
    try:
        print("creating sock: af=%d, type=%d, proto=%d" % (af,socktype,proto))
        s = socket.socket(af,socktype,proto)
    except socket.error as msg:
        print(" error %s" % msg)
        s = None
        continue
    try:
        print("attempting to connect to %s" % repr(sa))
        s.connect(sa)
    except socket.error as msg:
        print(" error: %s" % msg)
        s.close()
        s = None
        continue
    break

if s is None: # failed
    print("could not open socket")
    sys.exit(1)

delay = float(paramMap['delay'])
if delay != 0:
    print(f"sleeping for {delays}s")
    time.sleep(delay)
    print("done sleeping")

fs = framedSocket(s)

input = my_getLine()
command,localfile,remotefile = parseTCPInput(input)

fs.sendMessage(remotefile.encode()) # send filename

reply = fs.receiveMessage() # get server's response

if reply == "NO": # File can't be transferred
    os.write(2,("Failed").encode())
    sys.exit(1)

else: # "OK"
    fd = os.open(localfile, os.O_RDONLY)
    buffer = ""
    message = ""

    while(True):
        buffer = os.read(fd,100) # read 100 bytes
        string = buffer.decode()
        if len(string) == 0: # terminate if empty
            break
        message += string

    fs.sendMessage(message.encode()) # send the contents as a message
    s.close()


from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from .models import Fruits
from .serializers import FruitsSerializer
from rest_framework.response import Response
from rest_framework import status
# Create your views here.

class FruitList(APIView):
	def get(self, request):
		queryset = Fruits.objects.all()
		serializer = FruitsSerializer(
			queryset, many=True, context={'request': request})
		return Response(serializer.data)

	def post(self, request):
		serializer = FruitsSerializer(data=request.data)
		serializer.is_valid(raise_exception=True)
		serializer.save()
		return Response(serializer.data, status=status.HTTP_201_CREATED)

class FruitDetail(APIView):
	def get(self, request, id):
		fruit = get_object_or_404(Fruits, pk=id)
		serializer = FruitsSerializer(fruit)
		return Response(serializer.data)

	def put(self, request, id):
		fruit = get_object_or_404(Fruits, pk=id)
		serializer = FruitsSerializer(fruit, data=request.data)
		serializer.is_valid(raise_exception=True)
		serializer.save()
		return Response(serializer.data)

	def delete(self, request, id):
		fruit = get_object_or_404(Fruits, pk=id)
		fruit.delete()
		return Response(status=status.HTTP_204_NO_CONTENT)


from categories.views import addCategory, categoryListView

from django.urls import path
from . import views
app_name = 'categories'
urlpatterns = [
    path('',views.categoryListView,name ='list'),
    path('add/',views.addCategory, name ='add'),
    path('details/<int:pk>',views.categoryDetailView, name = 'details'),
    path('delete/<int:pk>',views.deleteCategoryView,name ='delete'),
]


import numpy as np
import plotly.express as px
import streamlit as st
import altair as alt
import matplotlib.pyplot as plt
import pandas as pd
import time

st.title("Animation Time Trial")
mode = st.selectbox("Select Plotting Library",('Plotly','Altair','Matplotlib'))
FPS = st.slider('Attempted FPS',0,120,30)
numPoints = st.slider('Number of Plot Points',1,5000,5)
animTime = st.slider('Time to Animate',1,10,1)

#Data
X = np.random.rand(numPoints)
Y = np.random.rand(numPoints)
data = pd.DataFrame({"x":X,"y":Y})

extraTime = 0

if st.button("Animate"):

    if mode == "Plotly":

      df = pd1.DataFrame(dict(r=[1, 5, 2, 2, 3],
        theta=['processing cost','mechanical properties','chemical stability',
           'thermal stability', 'device integration']))
      fig = px.line_polar(df, r='r', theta='theta', line_close=True)
      fig.show()

    if mode == "Altair":
        fig1 = alt.Chart(data).mark_point().encode(x="x",y="y")
        drawn_fig1 = st.altair_chart(fig1,use_container_width = True)
        for i in range(FPS*animTime):
            frameStartTime = time.time()
            X = X +1.0/FPS
            Y = Y +1.0/FPS
            data = pd.DataFrame({"x":X,"y":Y})
            frameDrawTime = time.time()
            fig1 = alt.Chart(data).mark_point().encode(x="x",y="y")
            drawn_fig1.altair_chart(fig1,use_container_width = True)
            frameEndTime = time.time()
            try:
                time.sleep(1/FPS-(frameEndTime-frameStartTime))
            except:
                time.sleep(1/FPS)

    if mode == "Matplotlib":
        st.set_option('deprecation.showPyplotGlobalUse', False)
        fig1 = plt.scatter(X,Y)
        drawn_fig1 = st.pyplot()
        for i in range(FPS*animTime):
            frameStartTime = time.time()
            X = X +1.0/FPS
            Y = Y +1.0/FPS
            data = pd.DataFrame({"x":X,"y":Y})
            frameDrawTime = time.time()
            fig1 = plt.scatter(X,Y)
            drawn_fig1.pyplot()
            frameEndTime = time.time()
            try:
                time.sleep(1/FPS-(frameEndTime-frameStartTime))
            except:
                time.sleep(1/FPS)


# ---------------------------------------------------------------------------------
#                            Main Python file
# Desc: Run the models
from modules import outcome as outcome, survival as survival
import pandas

def generate_model_function():

    # get dataframes and store them into pkl files
    o_df = outcome.run()
    o_df.to_pickle('test_outcome.pkl')

    s_df = survival.run()
    o_df.to_pickle('test_survival.pkl')

generate_model_function()


import random
import pygame

ROCK = 1
PAPER = 2
SCISSORS = 3
screen_width, screen_height= 600, 400
background_color = (200, 173, 127)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

class Button:
    def __init__(self, x, y, width, height, text, callback):
        self.rect = pygame.Rect(x, y, width, height)
        self.color = WHITE
        self.text = text
        self.callback = callback

    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            if self.rect.collidepoint(event.pos):
                self.callback()

    def draw(self):
        pygame.draw.rect(screen, self.color, self.rect)
        text_surf = font.render(self.text, True, BLACK)
        text_rect = text_surf.get_rect(center=self.rect.center)
        screen.blit(text_surf, text_rect)

def choose_winner(turn1, turn2):
    win1, win2, tie = 1, 2, 0
    if turn1 == turn2:
        text = font.render("It's a tie.", True, BLACK)
        text_rect = text.get_rect(center=(screen_width // 2, screen_height // 2))
        screen.blit(text, text_rect)
        return tie
    if (turn1 == ROCK and turn2 == SCISSORS) or (turn1 == PAPER and turn2 == ROCK) or (turn1 == SCISSORS and turn2 == PAPER):
        text = font.render("You win!!!", True, BLACK)
        text_rect = text.get_rect(center=(screen_width // 2, screen_height // 2))
        screen.blit(text, text_rect)
        return win1
    text = font.render("You lose!", True, BLACK)
    text_rect = text.get_rect(center=(screen_width // 2, screen_height // 2))
    screen.blit(text, text_rect)
    return win2

def get_image(turn):
    if turn == ROCK:
        return rock_image
    if turn == PAPER:
        return paper_image
    if turn == SCISSORS:
        return scissors_image

def choose(turn):
    global turn1, turn2
    if turn == ROCK:
        turn1 = ROCK
    elif turn == PAPER:
        turn1 = PAPER
    else:
        turn1 = SCISSORS
    turn2 = random.randint(1, 3)

# Initialize pygame
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Rock Paper Scissors")
font = pygame.font.Font(None, 36)
running = True

# import images
rock_image = pygame.image.load("rock.png")
paper_image = pygame.image.load("paper.png")
scissors_image = pygame.image.load("scissors.png")

# Choice buttons
rock_button = Button(60, screen_height - 40, 140, 30, "Rock", lambda: choose(ROCK))
paper_button = Button(230, screen_height - 40, 140, 30, "Paper", lambda: choose(PAPER))
scissors_button = Button(400, screen_height - 40, 140, 30, "Scissors", lambda: choose(SCISSORS))

turn1, turn2 = 0, 0

while running:
    # Changing surface color
    screen.fill(background_color)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                rock_button.handle_event(event)
                paper_button.handle_event(event)
                scissors_button.handle_event(event)
                if turn1 == 0:
                    continue
    if not (turn1 == 0 or turn2 == 0):
        cell_rect = pygame.Rect(50, 100, 150, 150)
        screen.blit(get_image(turn1), cell_rect)
        cell_rect = pygame.Rect(screen_width - 200, 100, 150, 150)
        screen.blit(pygame.transform.flip(get_image(turn2), True, False), cell_rect)
        choose_winner(turn1, turn2)

    rock_button.draw()
    paper_button.draw()
    scissors_button.draw()
    pygame.display.flip()

pygame.quit()


import os
import string
from inspect import stack
from itertools import combinations

import networkx as nx
from matplotlib import pyplot as plt
from networkx.drawing.nx_pydot import graphviz_layout

LIVE_DRAW_ENABLED = False  # Disable the function draw for live renderings
DRAW_ENABLED = True  # Disable the function draw, but not draw_multi

def config(enable_live_draw=False, enable_draw=True):
    global LIVE_DRAW_ENABLED, DRAW_ENABLED
    LIVE_DRAW_ENABLED = enable_live_draw
    DRAW_ENABLED = enable_draw

class PhyloNet(nx.DiGraph):
    """
    Helper class for a phylogenetic network (of instance NetworkX.DiGraph)
    """

    def __new__(cls, di_graph=nx.DiGraph()):
        """ Create a PhyloNet instance. The first argument can be used to define either a DiGraph or MultiDiGraph. """
        if not isinstance(di_graph, nx.DiGraph):
            print(di_graph, type(di_graph), isinstance(di_graph, nx.DiGraph))
        #     raise TypeError('PhyloNet should be of type DiGraph (or MultiDiGraph).')
        return di_graph.__new__(cls)

    def __init__(self, type1=None):
        super().__init__(type1)
        self._z_edges = None

    def copy(self, as_view=False):
        return super().copy(as_view)

    def get_root(self):
        """
        Get the root of the network
        :return: The unique root
        """
        for n in self.nodes:
            if self.in_degree(n) == 0:
                return n

    def get_ret_num(self):
        """
        Get the number of binary hybrid nodes/reticulations
        :return: The number of reticulations
        """
        return sum(self.in_degree(n) - 1 for n in self.nodes if self.in_degree(n) >= 2)

    def get_level(self):
        """ Get the level of the network, i.e. the maximal number of reticulations per bi-connected component. """
        g = self.to_undirected()
        level = 0
        for cc in nx.biconnected_components(g):
            if len(cc) <= 2:
                continue
            cc_ret_num = len([n for n in cc if self.in_degree(n) >= 2])
            level = max(level, cc_ret_num)
        return level

    def get_leaves(self):
        """
        For the given acyclic digraph, get the leaf nodes
        :return: A sorted list of leaves
        """
        return sorted(n for n in self.nodes() if self.out_degree(n) == 0)

    def get_dummy_leaves(self):
        """
        Get the dummy leaves
        :return: A list of names
        """
        return sorted(n for n in self.nodes() if len(n) >= 2 and n[0:2] == "d_")

    def get_hardwired_cluster(self, source: str, filter_name=False):
        """
        Get the hardwired cluster below the given source node
        :param source: A node
        :return: The hardwired cluster below the given source/node
        """
        if source not in self.nodes:
            return None
        # If the source is a leaf
        if source[0] != '_':
            return [source]
        # assert source[0] == '_':
        # The source is an internal node
        if filter_name:
            return sorted([n for n in nx.descendants(self, source) if n[0] != '_'])
        return sorted([n for n in nx.descendants(self, source) if self.out_degree(n) == 0])

    # NICE TODO: Implement a more efficient method
    def _get_soft_wired_clusters(self, source):
        """
        Get all soft-wired clusters
        :param source: A node
        :return: A set of the soft-wired clusters below the given source/node
        """

        def _select_ret_inputs(ret_ins: dict):
            """
            In the network net, select one incoming edge per reticulation to leave a tree
            :param ret_ins: A dictionary:
                Each (r: i) determines that the i-th (starting from 0) edge is selected for reticulation r.
            :return: The resulting tree
            """
            _tree = self.copy()
            for r, i in ret_ins.items():
                E_in = list(_tree.in_edges(r))
                E_in.__delitem__(i)
                _tree.remove_edges_from(E_in)
            for n in list(_tree.nodes):
                if _tree.out_degree(n) == 0 and n[0] == '_':
                    _tree.remove_node(n)
            # Remove singletons
            _tree.remove_nodes_from(list(nx.isolates(_tree)))
            return _tree

        # Find all reticulations below the source
        rets = [n for n in nx.descendants(self, source) if self.in_degree(n) >= 2]

        # If there are no reticulations below, then return the hardwired cluster
        if len(rets) == 0:
            c = self.get_hardwired_cluster(source)
            if c:
                return [c]
            return set()

        # Now find all soft-wired clusters
        c_strings = set()
        done = False
        ret_inputs = {r: 0 for r in rets}
        while not done:
            # For each reticulation, let 1 incoming edge remain
            tree = _select_ret_inputs(ret_inputs)
            c = tree.get_hardwired_cluster(source, filter_name=True)
            if c:
                c_strings.add(','.join(c))
            # Select the next tree configuration
            for r in rets:
                if ret_inputs[r] + 1 < self.in_degree(r):
                    ret_inputs[r] += 1
                    break
                elif r == rets[-1]:
                    done = True
                else:
                    ret_inputs[r] = 0
        clusters = []
        for c_s in c_strings:
            clusters.append(c_s.split(','))
        return clusters

    def get_st_sets(self, source: str):
        """
        Given that this network is a tree, return all ST-sets from a given source node and below it
        :param source: A source node
        :return: A list of lists (ST-sets)
        """
        # If the source is a leaf
        if source[0] != '_':
            return source
        sts = [self.get_hardwired_cluster(source)]
        childs = list(self.successors(source))
        # For each combination of children, add the hardwired cluster as ST-set
        for k in range(2, len(childs)):
            # Choose k children
            for combi in combinations(childs, k):
                # Get the hardwired cluster for this combination
                cl = []
                for n in combi:
                    cl.extend(self.get_hardwired_cluster(n))  # Singletons will be included
                sts.append(sorted(cl))
        # Recursively search for the ST-sets below the child split nodes
        for n in childs:
            if n[0] == '_':  # If n is a split node
                sts.extend(self.get_st_sets(n))
            else:
                sts.append([n])
        return sts

    def represents_old(self, C: set):
        """
        Check if the given network represents all clusters in C
        :param C: A set of clusters
        :param print_instr: Print instructions T/F
        :return: Does the PN represent the clusters [T/F]?
        """
        # Make a copy of the clusters C and remove each cluster that is represented. Note: This is inefficient!
        _C = C.copy()
        for node in self.nodes:
            if self.out_degree(node) == 0:
                continue
            clusters = self._get_soft_wired_clusters(node)
            for c in clusters:
                if c in _C:
                    _C.remove(c)

        return len(_C) == 0

    def represents(self, cs, x_added, lcas, print_instr=False):
        """
        Does this network represent the cluster defined in ClusterSet cs? T/F
        :param cs: A ClusterSet
        :param x_added:
        :param print_instr: Print instructions T/F
        :return: T/F
        """
        return self.represents_old(cs.C)

    def represents_cl(self, cluster, lcas):
        """ Check if the given cluster is represented """
        cl = list(cluster)
        if len(cl) == 1:
            return True
        c_s = ','.join(cluster)
        if c_s not in lcas.keys():
            # Check if the cluster is in the network
            print(f'ERROR: I do not see cluster {c_s}. Is it there?')
            self.draw(True, '?', all_labels=True)
        for ca in lcas[c_s]:  # For each common ancestor
            # Get the hardwired cluster
            hw_cl = self.get_hardwired_cluster(ca)
            if hw_cl == cluster:
                return True
        # Create a copy of the network & remove the leaves that are not in the cluster
        net = self.copy()
        # Remove each (parent) node that is not part of the cluster
        X_not = set(self.get_leaves()).difference(cluster)
        X_not_p = {list(net.predecessors(x))[0] for x in X_not if net.in_degree(x) == 1}
        net.remove_nodes_from(X_not)
        net.clean()
        while X_not_p:
            X_not = X_not_p
            X_not_1p = [x for x in X_not if
                        self.in_degree(x) == 1 and self.out_degree(x) <= 2]  # All x in X_not having 1 parent
            X_not_p = [list(self.predecessors(x))[0] for x in X_not_1p]
            net.remove_nodes_from(X_not)
            net.clean()
        for ca in lcas[c_s]:  # For each common ancestor
            # Get the hardwired cluster
            hw_cl = net.get_hardwired_cluster(ca)
            if not hw_cl:
                continue
            if hw_cl == cl:
                return True

        return False

    def get_lcas_2(self, n1: str, n2: str):
        """ Get the LCA of the 2 given nodes """
        if n1[0] != '_':
            # n1 is a leaf
            n1 = self.predecessors(n1).__next__()
        if n2[0] != '_':
            # n2 is a leaf
            n2 = self.predecessors(n2).__next__()
        if n2 in nx.ancestors(self, n1):
            lcas = set()
            lcas.add(n2)
            return lcas
        if n1 in nx.ancestors(self, n2):
            lcas = set()
            lcas.add(n1)
            return lcas
        # n1 is not an ancestor of n2, nor vice versa
        P1 = [n1]
        P2 = [n2]
        lcas = set()
        net = self.copy()
        while True:
            P1_new = set()
            for n in P1:
                if n not in lcas:
                    P1_new.update(net.predecessors(n))
            if not P1_new:
                break
            P1 = P1_new
            for p in P1:
                # If node 2 is reachable from p, then p is an LCA
                if n2 in nx.descendants(net, p):
                    lcas.add(p)
                    net.remove_node(p)
            P2_new = set()
            for n in P2:
                if n not in lcas:
                    P2_new.update(net.predecessors(n))
            if not P2_new:
                break
            P2 = P2_new
            for p in P2:
                # If node 1 is reachable from p, then p is an LCA
                if n1 in nx.descendants(net, p):
                    lcas.add(p)
                    net.remove_node(p)
        if lcas:
            return lcas
        self.draw(True, f'LCA({n1}, {n2}) = ?', all_labels=True)

    def _subdivide_edge(self, e):
        """
        Insert node v into edge e in the given network
        :param e: An edge
        :return: The added node (name)
        """
        # Let v be the new node
        v = self.new_node_name()
        # Remove the old edge
        self.remove_edge(e[0], e[1])
        # Add the new edges
        self.add_edge(e[0], v)
        self.add_edge(v, e[1])
        return v

    def new_node_name(self) -> str:
        """
        Get a name for a new (internal) node (splitting or reticulation) to insert in the given network
        :return: A new node name
        """
        node_nrs = [int(n[1:]) for n in self.nodes if n[1:].isnumeric() and n[0] == '_']
        if not node_nrs:
            return '_1'
        return '_' + str(max(node_nrs) + 1)

    def add_new_root(self):
        """
        Add a new root as parent of the current root and return the labels of the new and old root, respectively.

        :return: The labels of the new and old root, respectively
        """
        root = self.get_root()
        label = self.new_node_name()
        self.add_edge(label, root)
        return label, root

    def add_ret_below_2(self, parents: list, x: str):
        """
        Add a reticulation below two of the given parent nodes
        If no parents are given, then insert a reticulation instead of two dummy leaves, and a dummy leaf below it
        :return: A list of networks having a new reticulation
        """
        # If dummy leaves are given, then search their parents
        if parents[0][0:2] == 'd_':
            # Search for the dummy leaves & their parents
            p = {}
            for dum in parents:
                p[dum] = list(self.predecessors(dum))[0]
            parents = p
        else:
            parents = dict.fromkeys(parents, None)
        parent_combis = combinations(parents, 2)
        nets = []
        # t becomes the new reticulation/hybrid node
        t = self.new_node_name()
        for ret_parents in parent_combis:
            net = self.copy()
            # If dummy leaves are selected, then hang the reticulation below its parent & remove the dummy leaf
            p = parents[ret_parents[0]]
            if p:
                # Preserve one dummy root. Subdivide its incoming edge and make it a reticulation.
                net.remove_node(ret_parents[1])
                # If there is another dummy leaf in the network, then add a dummy leaf below the new reticulation
                if len(parents) >= 3:
                    v = net._subdivide_edge((p, ret_parents[0]))
                    net.add_edge(parents[ret_parents[1]], v)
                else:
                    # #parents = 2 => Add leaf x below reticulation t
                    net.remove_node(ret_parents[0])
                    net.add_edge(p, t)
                    net.add_edge(parents[ret_parents[1]], t)
                    net.add_edge(t, x)
            else:
                # Add the edges going to t
                net.add_edges_from((v, t) for v in ret_parents)
                # Add dummy leaves
                parents_new = list(parents)
                parents_new.remove(ret_parents[0])
                parents_new.remove(ret_parents[1])
                parents_new.append(t)
                net.add_edges_from((d, f'd_{i}') for i, d in enumerate(parents_new))
            nets.append(net)
        return nets

    def add_leaf_below_ret(self, x: str, edges, cs, stay_binary: bool = False, print_info=False):
        """
        Add a leaf below a reticulation below the given edges as follows.
        Create a reticulation t, a leaf l labeled x and an edge from t to l. Then, for each edge e_i, i = 1,2,... in the
        list, insert a node v_i into e_i and add an edge from v_i to t
        :param x: A leaf
        :param cs: A set of clusters (ClusterSet)
        :param edges: A list of edges
        :param stay_binary: Let the returned network(s) be binary
        :param print_info:
        :return: The new PN
        """
        parent_nodes = []
        E_done = []
        for e in edges:
            # Note: it can happen that an edge occurs multiple times in E
            if e in E_done:
                parent_nodes.append(parent_nodes[E_done.index(e)])
                continue
            # If e represents a node, i.e. e = (u, u)
            if e[0] == e[1]:
                parent_nodes.append(e[0])
            else:
                parent_nodes.append(self._subdivide_edge(e))
            E_done.append(e)
        # If the new leaf (or ST-set) is to be hung below exactly 1 edge (or node)
        if len(edges) == 1:
            # Hang the leaf below this edge (or node)
            self.add_edge(parent_nodes[0], x)
        else:
            # If leaf x should be hung below 3 edges or more and if the network should stay binary, then add 1 ret. now
            if len(parent_nodes) > 2 and stay_binary:
                # Print.var(parent_nodes, print_info)
                # Choose two nodes to create a reticulation below, and a dummy leaf below it
                return self.add_ret_below_2(parent_nodes, x)

            # t becomes the new reticulation/hybrid node
            t = self.new_node_name()
            # Add the edges going to t
            self.add_edges_from((v, t) for v in parent_nodes)
            # Add edge (t, x)
            self.add_edge(t, x)

        return [self]

    def get_e_out(self, cs_new, cs_in, x: str, lcas):
        """
        Get the lowest edge below which a new reticulation should be hung, such that all clusters below the edge will be
        represented in the newly formed network
        :param cs_new: The new ClusterSet
        :param cs_in: The minimal clusters containing the ST-set
        :param x: A leaf
        :return:
        """
        # If cs_in is empty, then return None
        if not cs_in:
            raise RuntimeError('cs_in should not be empty')

        # If the cluster X\{x} is in C, then E_out = the root edge
        Xm = list(cs_new.X)
        Xm.remove(x)
        if Xm in cs_new.C:
            r = self.get_root()
            return cs_new.X, list(self.out_edges(r))[0]

        # Check if the cluster X_in\st exists as (not necessarily strict) subset of any cluster in C.
        # Make a list (set) of all such edges.
        c_out = set(cs_in.get_used_taxa())
        c_out.remove(x)
        E_out = set()
        cl_out = set()
        # for c in cs_new.maximal().C:
        for c in cs_new.C:
            # If cluster c contains c_out and not leaf x
            if c_out.issubset(c) and x not in c:
                cl_out.update(c)

                # Cluster c in C contains c_out. Hence, the ST-set should be hung below an edge that is above cluster c.
                c_s = ','.join(c)
                if c_s in lcas.keys():
                    lca = lcas[c_s]
                    e = list(self.in_edges(lca))[0]
                else:
                    e = self.get_cluster_edge(c)
                if not e:
                    # Cluster c is not represented
                    print(f'ERROR: Cluster {c} is not represented')
                    return None, None
                E_out.add(e)

        # Check the length of E_out and return if possible
        _l = len(E_out)
        if _l == 1:
            return cl_out, list(E_out)[0]
        if _l == 0:
            return None, None

        # Find the highest E
        cl_nodes = {v for u, v in E_out}
        lca = self.get_comm_anc(cl_nodes)
        # if len(lcas) > 2:
        #     Print.var(cl_nodes)
        #     Print.var(lcas)
        #     self.draw(True, f'LCA({cl_nodes}) ?= {lcas}', all_labels=True)
        # lca = lcas[0]
        e_in = self.in_edges(lca)
        if len(e_in) == 0:
            # This node has no incoming edge. It must be the root
            e_out = self.out_edges(lca)
            return cl_out, list(e_out)[0]
        return cl_out, list(e_in)[0]

    def get_cluster_edge(self, c: list):
        """
        Find the lowest edge that represents the cluster
        :param c: A cluster
        :return: An edge
        """
        # Get the cluster leaves
        if len(c) == 1:
            comm_anc = c[0]
        else:
            # Quickly check if the root edge is present
            r = self.get_root()
            assert r not in c

            # Get the (lowest) common ancestor and then its incoming edge
            comm_anc = self.get_comm_anc(c)
            if not comm_anc:
                # This cluster is not represented
                return None
        e_in = self.in_edges(comm_anc)
        if len(e_in) == 0:
            # This node has no incoming edge. It must be the root
            return None, comm_anc
        return list(e_in)[0]

    def get_comm_anc(self, nodes):
        """
        Get the common ancestor of the given nodes
        :param nodes: A list of nodes
        :return: A node
        """

        assert nodes
        nodes = list(nodes)
        # Print.var(nodes, True)
        # If all leaves in the network are given, then LCA = the child of the root
        if nodes == self.get_leaves():
            r = self.get_root()
            return list(self.out_edges(r))[0]
        net = self.copy()
        # If the given nodes are leaves
        if net.out_degree(nodes[0]) == 0:
            # Remove each (parent) node that is not part of the cluster
            X_not = set(self.get_leaves()).difference(nodes)
            X_not_p = {list(net.predecessors(x))[0] for x in X_not}
            while X_not_p:
                X_not = X_not_p
                X_not_1p = [x for x in X_not if
                            self.in_degree(x) == 1 and self.out_degree(x) <= 2]  # All x in X_not having 1 parent
                X_not_p = [list(self.predecessors(x))[0] for x in X_not_1p]
                net.remove_nodes_from(X_not)
                net.clean()
            # net.draw(True, 'OK?', all_labels=True)
        comm_anc = None
        while comm_anc is None:  # TODO: will this always work well?
            # Search the common ancestor for each pair of nodes
            new_comm_ancs = set()
            for combi in combinations(nodes, 2):
                lcas = net.get_lcas_2(combi[0], combi[1])
                if not lcas:
                    print(f'ERROR: LCA({combi[0]}, {combi[1]}) = ?')
                    self.draw(True, f'LCA({combi[0]}, {combi[1]}) = ?', all_labels=True)
                # lcas = sorted(lcas)
                new_comm_ancs.update(lcas)
            if not new_comm_ancs:
                # Check if for some combination, a wrong CA is taken
                print(comm_ancs)
                self.draw(True, f'Check CA({nodes})', all_labels=True)
            comm_ancs = new_comm_ancs
            if len(comm_ancs) == 0:
                return None
            if len(comm_ancs) == 1:
                comm_anc = comm_ancs.pop()
            else:
                # If they are not all the same, then search for the common ancestor of these nodes!
                nodes = comm_ancs
        return comm_anc

    def _get_ret_ret_in_edges(self, node, incl_intermediate=True):
        """
        Suppose reticulation [node] has a reticulation v as parent, then return the union of the non-ret. parents
        of both [node] and v.
        """
        in_edges = []
        rets = [node]
        for r in rets:
            for e in self.in_edges(r):
                if self.in_degree(e[0]) > 1:
                    rets.append(e[0])
                    if incl_intermediate:
                        in_edges.append(e)
                else:
                    in_edges.append(e)
        return in_edges

    def get_hang_edges(self, cs_new, x: str, lcas, filter_edges: bool = True, guess_edges: bool = True,
                       stay_bin=False, full_cx=False, print_instr=False):
        """
        Get a list of lists of edges per cluster in Cin, to hang the ST-set below
        :param cs_new: A set of clusters (of type ClusterSet)
        :param x: A leaf to add
        :param lcas: The LCAs per cluster
        :param filter_edges: Filter the edges T/F
        :param guess_edges:
        :param stay_bin: Stay binary T/F
        :param full_cx: Use Cx as containing all clusters containing x
        :param print_instr: T/F
        :return: A list of edges
        """
        # Init
        leaves = self.get_leaves()
        if len(cs_new.X) != len(leaves) + 1:
            self.draw(True, 'Check #leaves', all_labels=True)
            return {}
        # st_str = BitString.from_bool(x in st for x in X)
        cl_edges = {}
        cl_parents = {}
        meta_candidates = {}  # Candidates for being a meta-node to be split
        cs_in = cs_new.get_cs_in(x, full_cx)

        # Find the edge e_out
        if full_cx:
            _cs_in = cs_in.minimal()
            cl_out, e_out = self.get_e_out(cs_new, _cs_in, x, lcas)
        else:
            cl_out, e_out = self.get_e_out(cs_new, cs_in, x, lcas)
        if e_out:
            E_cl = set()
            # E_cl.add(e_out)
            # Add the edges above e_out
            E_cl.update(self.out_edges(e_out[0]))
            c_s_out = ','.join(cl_out)  # Cluster as string
            cl_edges[c_s_out] = E_cl
            P = set()
            P.add(e_out[0])
            cl_parents[c_s_out] = P

        # For each cluster c in cs_in, get the lowest edge in the network that represents it.
        # Then, find all edges E_cl below which x may be hung.
        self.update_z_edges()
        for c in cs_in.C:
            if len(c) == len(cs_new.X):
                continue
            # cl = cl \ {x}
            c.remove(x)
            if filter_edges:
                E_cl, meta_candidate = self._get_cluster_ext_edges(c, cs_new, lcas, full_cx)
                # assert len(set(E_cl)) == len(E_cl)  # There should be no duplicate edges
                if not E_cl:  # If this cluster c is not represented, then return
                    return {}
            else:
                E_cl = list(self.edges)
                meta_candidate = None

            c_s = ','.join(c)  # Cluster as string
            cl_edges[c_s] = E_cl
            cl_parents[c_s] = set(e[0] for e in E_cl)
            if meta_candidate:
                meta_candidates[c_s] = meta_candidate
        # Do not use only minimal clusters in Cx, but all clusters containing x
        if full_cx:
            # Filter the edge sets: only preserve the minimal edge sets
            cl_edges_new = {}
            # Print.var(cl_edges, True)
            for c_s1, E1 in cl_edges.items():
                E1 = set(E1)
                c1 = set(c_s1.split(','))
                issupset = False
                for c_s2, E2 in reversed(cl_edges.items()):
                    if c_s2 == c_s1:
                        break
                    if E1.issuperset(E2):
                        issupset = True
                        break
                if 2 <= len(c1) < len(cs_new.X) - 1:
                    for _x in c1:
                        E1.update(self.in_edges(_x))
                if not issupset:
                    cl_edges_new[c_s1] = E1
            cl_edges = cl_edges_new
            # Update cl_parents
            for c_s in reversed(list(cl_parents.keys())):
                if c_s not in cl_edges.keys():
                    del cl_parents[c_s]
        if not guess_edges:
            return cl_edges

        for c_s1, e in meta_candidates.items():
            p = e[0]
            # Is this parent p a parent of an edge in another hang-edge group?
            # has_overlap = False
            for c_s2, P in cl_parents.items():
                if p in P:
                    E = list(filter(lambda _e: _e[0] == p, cl_edges[c_s2]))
                    assert E
                    e_div = E[0]
                    if e_div == e:
                        continue
                    # If v in e = (u, v) is not a reticulation, then continue
                    if self.in_degree(e_div[1]) == 1:
                        continue
                    # Regard p as meta node & split it
                    n_new = self._subdivide_edge(e_div)
                    # In order to minimize the number of reticulations (according to our Greedy approach, we should
                    # subdivide edge e_div and hang cluster (c_s1) below the new node n_new
                    self.remove_edge(e[0], e[1])
                    self.add_edge(n_new, e[1])
                    # Set the edges E_cl below which the new leaf should be hung
                    E_cl = set()
                    E_cl.add((e[0], n_new))
                    E_cl.add((n_new, e[1]))
                    E_cl.add((n_new, e_div[1]))
                    cl_edges[c_s1] = E_cl

                    # Add 0-edges
                    ret = e_div[1]
                    while ret:
                        succ = list(self.successors(ret))
                        if len(succ) == 0:
                            self.draw(True, f'Check ret {ret}', all_labels=True)
                            raise RuntimeError(f'Reticulation {ret} does not have any childs.')
                        desc = succ[0]
                        # If its child is a reticulation too, then add the 0-edge between the two rets.
                        if self.in_degree(desc) >= 2:
                            cl_edges[c_s1].add((ret, desc))
                            ret = desc
                        else:
                            ret = None

                    # Clean cl_edges
                    for _c_s in cl_edges.keys():
                        if e_div in cl_edges[_c_s]:
                            cl_edges[_c_s].remove(e_div)
                        if e in cl_edges[_c_s]:
                            cl_edges[_c_s].remove(e)

                    # For cluster 2, change edge e_div to (n_new, e_div[1])
                    if e_div not in cl_edges[c_s2]:
                        # In this case, there are multiple possibilities to split the metanode!
                        #  Which one to choose?
                        continue
                    cl_edges[c_s2].add((n_new, e_div[1]))
                    break
        return cl_edges

    def update_z_edges(self):
        """
        Get the zero-edges
        :return:
        """
        self._z_edges = set()
        for n in self.nodes:
            if self.in_degree(n) >= 2:  # If n is a ret.
                for e in self.in_edges(n):
                    self._z_edges.add(e)
                    p = e[0]
                    # If p has only child reticulations
                    onlyRets = True
                    for c in self.successors(p):
                        if self.in_degree(c) == 1:
                            onlyRets = False
                            break
                    if onlyRets:
                        self._z_edges.add(list(self.in_edges(p))[0])

    def get_z_edges_below(self, node: str):
        z_edges = set()
        for e in self.out_edges(node):
            if e in self._z_edges:
                z_edges.add(e)
                z_edges.update(self.get_z_edges_below(e[1]))
        return z_edges

    def _get_cluster_ext_edges(self, c: list, cs_new, lcas, full_cx=False):
        """
        Get the edges below which another cluster cl2 can be hung, such that cluster (cl1 union cl2) will be represented
        :param c: The cluster to search the 'extension edges' for
        :param cs_new: The set of clusters to be represented after hanging cluster cl back
        :return: A list of 'extension edges'
        """
        # Get the cluster edge
        if len(c) == 1:
            lca = c[0]
        else:
            c_str = ','.join(c)
            lca = lcas[c_str]
        e = list(self.in_edges(lca))[0]
        if not e:
            # Cluster c is not represented
            return [], None
        E_cl = []

        # If edge e is the root edge, then return
        if len(c) >= 2:
            # Add 0-edges connected to v
            E_cl.extend(self.get_z_edges_below(e[1]))
        # Add 0-edges connecting the subtree below the LCA with a reticulation
        for x in c:
            p = list(self.predecessors(x))[0]  # Parent of leaf x
            rest_only_z_outs = True
            for _e in self.out_edges(p):
                if _e[1] != x and _e not in self._z_edges:
                    rest_only_z_outs = False
                    break
            if rest_only_z_outs:
                for _e in self.out_edges(p):
                    if _e in self._z_edges:
                        if _e not in E_cl:
                            E_cl.append(_e)
                        for _ez in self.get_z_edges_below(_e[1]):
                            if _ez not in E_cl:
                                E_cl.append(_ez)
            else:
                E_cl.append((p, x))

        if self.out_degree(e[0]) == 1:  # If u has out-degree 1, then (u is a ret, so) add edge e
            E_cl.append(e)
            # Add all in-edges of the reticulation (and of its parent reticulations, etc.)
            E_cl.extend(self._get_ret_ret_in_edges(e[0]))
            return list(reversed(E_cl)), None
        # u of e = (u, v) is a split node.
        # If the other children of 'u' are only reticulations, then add (w, u)
        neighbors = list(self.neighbors(e[0]))

        # Consider the other children of u. Are they all reticulations?
        neighbors.remove(e[1])
        only_rets = True
        has_ret = False
        for n in neighbors:
            if self.in_degree(n) == 1:  # If node n is a splitting
                only_rets = False
                if has_ret:
                    break
            else:  # If node n is a reticulation
                has_ret = True
                if not only_rets:
                    break
        if only_rets and self.in_degree(e[0]) == 1:
            # # Add (w, u)
            E_cl.extend(self.in_edges(e[0]))
            # Add (u, u); let the new node be hung below node u!
            E_cl.append((e[0], e[0]))
            # The other children of node e[0] are all reticulations
            return E_cl, None
        # u is a splitting and there is a splitting among the neighbours of v
        if len(c) == 1:
            # If this cluster cl is a single leaf, then surely, x should be hung below this edge,
            # unless there is a ret. below e[0]
            if e not in E_cl:
                E_cl.append(e)
            if has_ret:
                # Node u has a child ret.
                return E_cl, e  # Node u of edge e=(u,v) is a meta-node candidate
            return E_cl, None
        # Otherwise, add e
        assert e[1][0] == '_', self.draw(True, f'Edge {e}')
        if e not in E_cl:
            if c in cs_new.C or full_cx:
                E_cl.append(e)
        if self.in_degree(e[1]) == 1 and not E_cl:
            E_cl.append((e[1], e[1]))
        return E_cl, None

    def clean(self, rem_dummy_root=False):
        """
        Clean the network from redundant edges, etc.
        :param rem_dummy_root: Remove the dummy root T/F
        :return: The cleaned PN
        """
        # If multiple edges go from v1 to v2, then remove redundant edges among them
        E = []
        for i, e in reversed(list(enumerate(self.edges()))):
            if not e in E:
                E.append(e)

        # If a node has both in- and out-degree 1, then remove the node and connect its ancestor with its descendant
        for node in list(self.nodes):
            if self.in_degree(node) == 1 and self.out_degree(node) == 1:
                e1 = list(self.in_edges(node))[0]
                e2 = list(self.out_edges(node))[0]
                self.remove_node(e1[1])
                self.add_edge(e1[0], e2[1])

        if rem_dummy_root:
            self.remove_dummy_root()

    def remove_dummy_root(self):
        r = self.get_root()
        if self.out_degree(r) == 1:
            self.remove_node(r)

    def merge(self, other: "PhyloNet"):
        X_other = other.get_leaves()
        # Search the node in this network to be replaced by the other network
        for n in self.nodes:
            if self.out_degree(n) <= 1:  # Only consider splittings
                continue
            # Node n is an internal node
            c = self.get_hardwired_cluster(n)
            if X_other == c:
                # Remove node n and its descendants
                nodes2rem = [n]
                nodes2rem.extend(nx.descendants(self, n))
                preds = list(self.predecessors(n))
                if len(preds) == 0:  # If n does not have a parent, then return the other tree!
                    self.remove_nodes_from(list(self.nodes))
                    self.add_edges_from(other.edges)
                    return
                p_n = preds[0]  # The parent of n
                # For each node containing 3 or more child leaves in the other network, replace it by the
                # pendant subtree in this network
                self.remove_nodes_from(nodes2rem)
                r_other = other.get_root()
                # This is the i-th merged subnetwork
                subnet_prefixes = {n[0:3] for n in self.nodes if n[0:2] == '__'}
                i = len(subnet_prefixes)
                # Add the subnetwork below the parent of node n
                prefix = f"__{string.ascii_uppercase[i]}"
                # Relabel the internal nodes to prevent duplicate labels
                mapping = {}
                for n in other.nodes:
                    if n[0] == '_':  # If node n is an internal node
                        mapping[n] = prefix + n
                other = nx.relabel_nodes(other, mapping)
                self.add_edges_from(other.edges)
                self.add_edge(p_n, prefix + r_other)
                return
            if set(X_other).issubset(c):
                # Remove the children of node n that are not reticulations
                child_leaves = sorted(_n for _n in self.successors(n) if self.in_degree(_n) == 1 and _n[0] != '_')
                if not child_leaves:
                    continue
                if set(X_other).issubset(child_leaves):
                    # Merge the networks
                    self.remove_nodes_from(X_other)
                    r_other = other.get_root()
                    # This is the i-th merged subnetwork
                    subnet_prefixes = {n[0:3] for n in self.nodes if n[0:2] == '__'}
                    i = len(subnet_prefixes)
                    # Add the subnetwork below the parent of node n
                    prefix = f"__{string.ascii_uppercase[i]}"
                    # Relabel the internal nodes to prevent duplicate labels
                    mapping = {}
                    for _n in other.nodes:
                        if _n[0] == '_':  # If node n is an internal node
                            mapping[_n] = prefix + _n
                    other = nx.relabel_nodes(other, mapping)
                    self.add_edges_from(other.edges)
                    self.add_edge(n, prefix + r_other)
                    return
        print('ERROR: How should I merge these networks?')
        # self.draw(True, 'Final Net', all_labels=True)
        # other.draw(True, 'Merge It', all_labels=True)

    def _get_newick(self, _root=None, _hybrids=None):
        """
        Convert the phylogenetic network to a string in (e-)Newick format.

        :param _root: A root
        :param _hybrids: A list of hybrid nodes
        :return: A Newick-formatted string
        """
        if _hybrids is None:
            _hybrids = {}
        if _root is None:
            _root = self.get_root()
        sub_str = []
        for child in self[_root]:
            # Is this a hybrid node?
            if self.in_degree(child) > 1:
                if child in _hybrids.keys():
                    hybrid_label = _hybrids[child]
                    sub_str.append(hybrid_label)
                else:
                    hybrid_label = '#H' + str(len(_hybrids) + 1)
                    _hybrids[child] = hybrid_label
                    sub_str.append(self._get_newick(_root=child, _hybrids=_hybrids) + hybrid_label)
            elif len(self[child]) > 0:
                sub_str.append(self._get_newick(_root=child, _hybrids=_hybrids))
            else:
                sub_str.append(child)
        return "(" + ','.join(sub_str) + ")"

    def save_net(self, filename):
        """
        Save the given network in the Newick (or e-Newick) format.

        :param filename: A file name
        """
        nwk = self._get_newick()
        # Create the path if it does not exist
        path = os.path.dirname(filename)
        if len(path) and not os.path.exists(path):
            os.makedirs(path)

        with open(filename, 'w') as f:
            f.write(nwk + ';')
        print(f'SUCCESS: The network is saved as {filename}.')

    def draw(self, hold=False, title=None, axes=None, filename=None, all_labels=False, labels=None, edge_color=None,
             node_color=None):
        """
        Draw the given NetworkX digraph
        :param hold: Show the figure until the user closes it
        :param title: A title
        :param axes: Figure axes
        :param filename: A filename to store the figure
        :param edge_color:
        :param labels:
        :param all_labels:
        :param node_color:
        """
        if not LIVE_DRAW_ENABLED and not hold:
            print('WARNING: Drawing live is disabled.')
            return
        if not DRAW_ENABLED and stack()[1].function != 'draw_multi':  # TODO: remove stack
            print('WARNING: Drawing is disabled.')
            return
        if axes is None:
            plt.clf()  # HERE the program halts often :( WHY?
            plt.cla()

        def _decrease_leaf_labels():
            ys = set(y for (x, y) in pos.values())
            y_shift = (max(ys) - min(ys)) / (2 * len(ys))
            return {_n: (x, y - y_shift * (self.out_degree(_n) == 0)) for _n, (x, y) in pos.items()}

        # If self is a digraph, then nicely color its nodes
        if isinstance(self, nx.DiGraph):
            # Color the nodes: distinguish leaf nodes, hybrid nodes/reticulations and splits
            color_leaf = 'green'
            color_ret = 'red'
            color_splt = 'black' if not all_labels else 'lightGrey'
            color_lookup = {}
            node_size = []
            for node in self.nodes():
                if ',' in node:
                    raise RuntimeError(f'A node may not include a comma (I see node {node})')
                if self.out_degree(node) == 0:  # If this is a leaf
                    color_lookup[node] = color_leaf
                    node_size.append(100)
                elif self.in_degree(node) >= 2:  # If this is a hybrid node
                    color_lookup[node] = color_ret
                    node_size.append(60)
                else:
                    color_lookup[node] = color_splt
                    node_size.append(60)
                if node_color and node in node_color.keys():
                    color_lookup[node] = node_color[node]

            # Create a nice network layout
            pos = graphviz_layout(self, prog="dot")
            # Draw the network
            nx.draw_networkx(self, pos, True, with_labels=False, node_color=list(color_lookup.values()),
                             node_size=node_size, ax=axes, edge_color=edge_color)
            pos_nodes = _decrease_leaf_labels()
            # Only show the labels of the taxa/leaves
            if labels is None:
                labels = {}
                for n in self.nodes:
                    # If n is a leaf, then show the name
                    if self.out_degree(n) == 0 or all_labels:
                        labels[n] = n
            else:
                # Filter the labels for the leaves in this network
                nodes = self.get_leaves()
                labels = {n: l for n, l in labels.items() if n in nodes}
            nx.draw_networkx_labels(self, pos=pos_nodes, labels=labels, ax=axes)
        else:
            if labels is not None:
                # Filter the labels for the leaves in this network
                nodes = list(self.nodes)
                labels = {n: l for n, l in labels.items() if n in nodes}
            # Create a nice network layout
            nx.draw_circular(self, with_labels=True, edge_color=edge_color, node_color='lightGrey', ax=axes,
                             labels=labels)
            # If there are edge labels, show them
            e_labels = nx.get_edge_attributes(self, 'weight')
            if len(e_labels) > 0:
                nx.draw_networkx_edge_labels(self, nx.circular_layout(self), e_labels, ax=axes)

        # Set the title
        if title is not None:
            plt.gcf().suptitle(title)
        plt.subplots_adjust(left=0, bottom=0, right=1)
        if filename is not None:
            plt.pause(1E-9)
            plt.savefig(f'output/{filename}', bbox_inches='tight')
        if axes is None:
            if hold:
                plt.show()
            else:
                plt.pause(1E-9)


#!/usr/bin/env python3
from busquedas.models import ProyectosExcel
import csv
from datetime import datetime
# para ejecutar este script dentro de shell ejecutar el siguiente comando en
# la terminal, dentro del proyecto.
# python manage.py shell -i python < load_db.py
# otra forma de poblar datos es mediante dumpdata y fixtures
if not ProyectosExcel.objects.all().count() > 800:
    with open('load_start_info/proyectos_normalizados.csv', 'r') as f:
        reader = csv.DictReader(f)
        items = list(reader)

    for item in items:
        sigla_id = item.get('ID'),
        tesistas = int(item.get('TESISTAS')),
        autor = item.get('AUTOR'),
        titulo = item.get('TITULO'),
        tutor = item.get('TUTOR')
        docente = item.get('DOCENTE-1040')
        mencion = item.get('MENCION')
        fecha = item.get('FECHA')
        print(f"Proyecto: {titulo[0]}")
        # print(sigla_id[0], tesistas[0], autor[0], titulo[0], tutor, docente, mencion, fecha)
        ProyectosExcel.objects.create(
            tesistas = tesistas[0],
            sigla_id = sigla_id[0],
            autor = autor[0],
            titulo = titulo[0],
            tutor = tutor,
            docente = docente,
            mencion = mencion,
            fecha_concluida = datetime.strptime(fecha, "%Y-%m-%d"),
            )
else:
    print('*** Ya se cargo los proyectos')


import os
import math
import numpy as np
from keras.models import load_model

class MultilayerClassifier:

    def __init__(self):
        self.intern_extern_model = load_model('..\\models\\Intern-Extern-Model\\150px\\binary.hdf5', compile=True)
        self.intern_model = load_model('..\\models\\Intern-Extern-Model\\150px\\intern.hdf5', compile=True)
        self.extern_model = load_model('..\\models\\Intern-Extern-Model\\150px\\extern.hdf5', compile=True)

    def __prediction_to_class(self, arg):
        options = {0: "02_ER",
                   1: "04_LN_sy",
                   2: "01_AR",
                   3: "03_LN_md",
                   4: "05_LB",
                   5: "06_AG"
                   }
        return options.get(arg, "nothing")

    def __calc_extern_prediction(self, prediction):
        return math.fabs((prediction - 0.5) / 0.5) * 100

    def __clssify_intern_extern(self, document_img):

        prediction = self.intern_extern_model.predict(document_img)
        class_and_prediction = {"prediction": prediction, 'class': int(round(prediction.item(0)))}
        return class_and_prediction

    def __classify_intern_document(self, document_img):
        prediction = self.intern_model.predict(document_img)
        class_and_prediction = {"prediction": np.max(prediction), 'class': np.argmax(prediction) + 2,
                                'class_name': self.__prediction_to_class(np.argmax(prediction) + 2)}
        return class_and_prediction

    def get_label_dir(self, folders):

        directories = [d for d in os.listdir(folders)
                       if os.path.isdir(os.path.join(folders, d))]
        print(directories)
        return directories

    def __classify_extern_document(self, document_img):

        prediction = self.extern_model.predict(document_img)
        class_and_prediction = {"prediction": self.__calc_extern_prediction(prediction),
                                'class': int(round(prediction.item(0))),
                                'class_name': self.__prediction_to_class(int(round(prediction.item(0))))}
        return class_and_prediction

    def classify_single_document(self, document_img):

        img = self.__convert_image(document_img)
        intern_extern_prediction = self.__clssify_intern_extern(img)

        if intern_extern_prediction['class'] == 1:
            return self.__classify_intern_document(img)

        elif intern_extern_prediction['class'] == 0:
            return self.__classify_extern_document(img)

    def __convert_image(self, image):
        x = image.shape[0]
        y = image.shape[0]
        img = np.array(image).astype('float32')
        reshaped_image = np.reshape(img, [1, x, y, 3])
        return reshaped_image

if __name__ == "__main__":

    classificator = MultilayerClassifier()
    data_dir = "C:\\tmp\\test2"
    directories = classificator.get_label_dir(data_dir)
    from utils.evaluate_model import Evaluator

    evaluator = Evaluator()
    classNames = evaluator.get_class_names(data_dir)
    test_X, test_Y = evaluator.get_test_data(data_dir, 150, 150)

    true_classes = evaluator.labels_to_classes(test_Y)
    predicted_classes = []

    for index, data in enumerate(test_X):
        predictions = classificator.classify_single_document(data)
        predicted_classes.append(directories.index(predictions['class_name']))

    print("macro recal=", evaluator.recall(true_classes, predicted_classes, 'weighted'))
    print("micro recal=", evaluator.recall(true_classes, predicted_classes, 'micro'))
    print("macro precision=", evaluator.presision_score(true_classes, predicted_classes, 'weighted'))
    print("micro precision=", evaluator.presision_score(true_classes, predicted_classes, 'micro'))
    print("macro f1=", evaluator.f1_score(true_classes, predicted_classes, 'weighted'))
    print("micro f1=", evaluator.f1_score(true_classes, predicted_classes, 'micro'))
    print('report: ', evaluator.classification_report(true_classes, predicted_classes, classNames))
    print('accurary:', evaluator.accuracy_score(true_classes, predicted_classes))

    from utils.confusionmatrix import Confusionmatrix

    cnf = Confusionmatrix(directories)
    cnf_matriy = cnf.compute_confusion_matrix(true_classes, predicted_classes)
    cnf.plot_confusion_matrix(cnf_matriy, False, title='150px')


import easyocr
import cv2
from PIL import Image
import pytesseract

pytesseract.pytesseract.tesseract_cmd = 'D:\\Progarms\\tesseract\\tesseract'

#-------------------------

def draw_blocks(img, blocks):    #отрисовка блок распознаного текста от easyocr
    for i in blocks:
        ax = int(i[0][0][0])
        ay = int(i[0][0][1])
        bx = int(i[0][2][0])
        by = int(i[0][2][1])
        img = cv2.rectangle(img, (ax, ay), (bx, by), (0, 0, 255), 1)
    return img

def draw_paragraphs(img, paragr):    #отрисовка блок распознаного текста от easyocr
    for i in paragr:
        ax = int(i[0][0][0])
        ay = int(i[0][0][1])
        bx = int(i[0][1][0])
        by = int(i[0][1][1])
        img = cv2.rectangle(img, (ax, ay), (bx, by), (255, 0, 0), 1)
    return img

#-------------------------

def recognize_block_of_text(start_file):      #распознавание текста
    reader = easyocr.Reader(["en"], gpu = True)
    return reader.readtext(start_file, paragraph = False)

#-------------------------

def recognize_paragraphes(blocks):
    paragraphs = []                    #структура: [[[XверхЛ, YверхЛ], [XнизП,YнизП]], "текст"]
    for i in blocks:
        hasElement = False             #флаг наличия параграфа
        for j in paragraphs:
            if i[0][0][1] <= j[0][1][1] and (
                ((i[0][0][0] <= j[0][0][0]) and (i[0][2][0] >= j[0][0][0])) or
                (i[0][0][0]<= j[0][1][0] and i[0][2][0] >= j[0][1][0]) or
                (i[0][0][0] >= j[0][0][0] and i[0][2][0] <= j[0][1][0])):

                j[0][1][1] = i[0][2][1]
                if i[0][0][0] < j[0][0][0]:
                    j[0][0][0] = i[0][0][0]
                if i[0][2][0] > j[0][1][0]:
                    j[0][1][0] = i[0][2][0]
                j[1] += i[1] + " "
                hasElement = True
                break
        if not hasElement:          #создаем новый параграф
            paragraphs.append([[ [i[0][0][0], i[0][0][1]] , [i[0][2][0], i[0][2][1]] ], i[1]])
    return paragraphs

#-------------------------

def recognize_text(img_file, drBlocks = False, drParagraphs=False):
    blc = recognize_block_of_text(img_file)
    prg = recognize_paragraphes(blc)
    if drBlocks:
        img = cv2.imread(img_file)
        img = draw_blocks(img, blc)
        cv2.imwrite(img_file, img)
    if drParagraphs:
        img = cv2.imread(img_file)
        img = draw_paragraphs(img, prg)
        cv2.imwrite(img_file, img)
    return prg

#-------------------------

def recognize_text_tesseract(img_file, paragrapth):
    im = Image.open(img_file)
    rs = im.crop((paragrapth[0][0][0], paragrapth[0][0][1], paragrapth[0][1][0], paragrapth[0][1][1]))
    rs.save("textRec.jpg")
    return pytesseract.image_to_string('textRec.jpg')

def just_tesseract(img_file):
    return pytesseract.image_to_string(img_file)


"""

A script to batch test ORFs by int random fetching method

Example script:
python -i example_by_trans_shuffling.py -s human -olf list/golub/xaa -sn 1 -o1 output/golub/xaa.example.fa -o2 output/golub/xaa.example.pep_len

Parameter description:
s  = species
olf = orf_list_file
sn = simulation_num
"""

__version__ = "1.0.1"
__email__ = "haiwang.yang@northwestern.edu"

from timeit import default_timer as timer
from common_functions import Orf, Transcript2, get_elements, get_A2B, object_to_pickle, pickle_to_object
from simulate_by_trans_shuffling import example_shu
import subprocess
import argparse
import random
import os
import re
import pickle
from Bio.Seq import Seq
from pyfaidx import Fasta

if __name__ == "__main__":
    # fetching parameters
    parser = argparse.ArgumentParser(description='please provide information')
    parser.add_argument('-s', '--species', type=str) # human
    parser.add_argument('-olf', '--orf_list_file', type=str) # orf list file path
    parser.add_argument('-sn', '--simulation_num', type=int) # simulation num
    parser.add_argument('-o1', '--output1', type=str) # example orf cds seq
    parser.add_argument('-o2', '--output2', type=str) # example orf pep len

    args = parser.parse_args()
    species = args.species
    orf_list_file = args.orf_list_file
    simulation_num = args.simulation_num
    output1 = args.output1
    output2 = args.output2

    transcript_fasta = Fasta("annotation/" + species + ".transcript.fa")
    cds_range_file = "annotation/" + species + ".transcript.cds_range"
    transcript_id_to_transcript_len = get_A2B(cds_range_file, 1, 3)
    transcript_id_to_canonical_start = get_A2B(cds_range_file, 1, 4)
    transcript_id_to_UTR3_len = get_A2B(cds_range_file, 1, 6)

    with open(output1, "w") as w1, open(output2, "w") as w2:
        for orf_id in get_elements(orf_list_file):
            print(orf_id)
            orf = Orf(species, orf_id)
            transcript_id = orf.transcript_id
            transcript_seq = str(transcript_fasta[transcript_id])

            orf_type = orf.orf_type
            if orf_type != "noncoding":
                transcript_len = int(transcript_id_to_transcript_len[transcript_id])
                canonical_start = int(transcript_id_to_canonical_start[transcript_id])
                UTR3_len = int(transcript_id_to_UTR3_len[transcript_id])
                canonical_end = transcript_len - UTR3_len
                lst_sim_all_orf_cds, lst_sim_main_orf_cds, lst_sim_uorf_cds, lst_sim_overlapping_uorf_cds, lst_sim_all_orf_pep_len, lst_sim_main_orf_pep_len, lst_sim_uorf_pep_len, lst_sim_overlapping_uorf_pep_len = example_shu(species, orf_id, transcript_seq, canonical_start, canonical_end, simulation_num)
            else:
                canonical_start, canonical_end = 0, 0
                lst_sim_all_orf_cds, lst_sim_main_orf_cds, lst_sim_uorf_cds, lst_sim_overlapping_uorf_cds, lst_sim_all_orf_pep_len, lst_sim_main_orf_pep_len, lst_sim_uorf_pep_len, lst_sim_overlapping_uorf_pep_len = example_shu(species, orf_id, transcript_seq, canonical_start, canonical_end, simulation_num)

            i = 0
            for _ in lst_sim_all_orf_cds:
                w1.write(">" + orf_id + "__all" + str(i)  + "\n" + _ + "\n")
                i += 1

            i = 0
            for _ in lst_sim_main_orf_cds:
                w1.write(">" + orf_id + "__main" + str(i)  + "\n" + _ + "\n")
                i += 1

            i = 0
            for _ in lst_sim_uorf_cds:
                w1.write(">" + orf_id + "__uORF" + str(i)  + "\n" + _ + "\n")
                i += 1

            i = 0
            for _ in lst_sim_overlapping_uorf_cds:
                w1.write(">" + orf_id + "__ouORF" + str(i)  + "\n" + _ + "\n")
                i += 1

            w2.write(orf_id + "\tall"  + "\t" + ",".join([str(_) for _ in lst_sim_all_orf_pep_len]) + "\n")
            w2.write(orf_id + "\tmain"  + "\t" + ",".join([str(_) for _ in lst_sim_main_orf_pep_len]) + "\n")
            w2.write(orf_id + "\tuORF"  + "\t" + ",".join([str(_) for _ in lst_sim_uorf_pep_len]) + "\n")
            w2.write(orf_id + "\touORF"  + "\t" + ",".join([str(_) for _ in lst_sim_overlapping_uorf_pep_len]) + "\n")