import pygame
import random
import numpy as np

class Explosion(pygame.sprite.Sprite):
    def __init__(self, frames, xcoord, ycoord, scale=1.5, update_n=1):
        pygame.sprite.Sprite.__init__(self)  # call Sprite initializer
        self.frame = 0
        self.frames = frames
        self.image = self.frames[self.frame]
        self.rect = self.image.get_rect()
        self.x = xcoord
        self.y = ycoord
        self.scale = scale
        self.update_n = update_n
        self.update_counter = self.update_n

    def update(self):
        self.update_counter -= 1
        if self.frame >= len(self.frames) - 1:
            self.kill()
        self.image = self.frames[self.frame]
        self.rect = self.image.get_rect()
        self.image = pygame.transform.scale(self.image, (int(self.rect.size[0] * self.scale),
                                                         int(self.rect.size[1] * self.scale)))
        self.rect = self.image.get_rect()
        self.rect.x = self.x
        self.rect.y = self.y
        if self.update_counter == 0:
            self.frame += 1
            self.update_counter = self.update_n

        gameDisplay.blit(self.image, self.rect)

    def update_moving(self, xspeedboss, yspeedboss):
        if self.frame >= len(self.frames) - 1:
            self.kill()
        self.image = self.frames[self.frame]
        self.rect = self.image.get_rect()
        self.image = pygame.transform.scale(self.image, (int(self.rect.size[0] * 1.5), int(self.rect.size[1] * 1.5)))
        self.rect = self.image.get_rect()
        self.x += xspeedboss
        self.y += yspeedboss
        self.rect.x = self.x
        self.rect.y = self.y
        self.frame += 1
        gameDisplay.blit(self.image, self.rect)

class Explosion2(pygame.sprite.Sprite):
    def __init__(self, frames, xcoord, ycoord):
        pygame.sprite.Sprite.__init__(self)  # call Sprite initializer
        self.frame = 0
        self.frames = frames
        self.image = self.frames[self.frame]
        self.rect = self.image.get_rect()
        self.x = xcoord
        self.y = ycoord
        self.expansion = 0.8
        self.update_counter = 3

    def update(self):
        self.update_counter -= 1
        if self.frame >= len(self.frames) - 1:
            self.kill()
        self.image = self.frames[self.frame]
        self.rect = self.image.get_rect()
        self.image = pygame.transform.scale(self.image, (int(self.rect.size[0] * self.expansion),
                                                         int(self.rect.size[1] * self.expansion)))
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.rect.centerx = self.x
        self.rect.centery = self.y
        if self.update_counter == 0:
            self.expansion += 0.045
            self.frame += 1
            self.update_counter = 4
        gameDisplay.blit(self.image, self.rect)

class Ship(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)  # call Sprite initializer
        self.image = player_ship
        self.rect = self.image.get_rect()
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = (display_width - self.width) * 0.5
        self.y = display_height - self.height * 1.2
        self.speed = 0  # This variable changes with key presses
        self.endspeed = 1
        self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        # Update variables of the game for next update
        self.x += self.speed
        if self.x > display_width - self.width:
            self.x = display_width - self.width  # boundaries for ship
        elif self.x < 0:
            self.x = 0  # boundaries for ship

        self.rect.x = self.x
        self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
        gameDisplay.blit(self.image, self.rect)

    def to_end_position(self, xcoord):
        statement = False
        self.speed = 0
        if self.x < xcoord - 1:
            self.x += self.endspeed
        elif self.x > xcoord + 1:
            self.x -= self.endspeed
        else:
            statement = True

        self.rect.x = self.x
        self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
        gameDisplay.blit(self.image, self.rect)

        return statement

class Meteor(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)  # call Sprite initializer
        meteor_choices = [meteor1, meteor2, meteor3, meteor4]
        self.image = meteor_choices[random.randrange(0, 3)]
        self.rect = self.image.get_rect()
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = random.randrange(0, display_width - self.width)
        self.y = -200
        self.mask = pygame.mask.from_surface(self.image)
        self.speed = 7

    def update(self):
        self.y += self.speed
        self.rect.x = self.x
        self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
        gameDisplay.blit(self.image, self.rect)

class Laser(pygame.sprite.Sprite):
    def __init__(self, xcoord, ycoord):
        pygame.sprite.Sprite.__init__(self)
        self.image = laser_blue
        self.rect = self.image.get_rect()
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = xcoord - 0.5 * self.width  # depends on ship location
        self.y = ycoord  # These will be set at spawn because it depends on ship location
        self.speed = -20
        self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        self.y += self.speed
        if self.y < 0 - self.height:
            self.kill()
        else:
            self.rect.x = self.x
            self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
            gameDisplay.blit(self.image, self.rect)

class EnemyGoon(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)  # call Sprite initializer
        enemy_choices = [enemy1, enemy2, enemy3]
        self.image = enemy_choices[random.randrange(0, 2)]
        self.rect = self.image.get_rect()
        self.image = pygame.transform.scale(self.image, (int(self.rect.size[0] / 1.5), int(self.rect.size[1] / 1.5)))
        self.rect = self.image.get_rect()  # after transforming need to acquire new rect
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = random.choice(np.linspace(0, display_width - self.width, 10))
        self.y = 100 + self.height
        self.mask = pygame.mask.from_surface(self.image)
        self.update_timer = fps * 2  # update every 60 frames
        self.x_speed = random.choice([-3, 3])

    def update(self):
        self.update_timer -= 1
        if self.update_timer == 0:
            self.fire()
            self.update_timer = fps * 2
        self.y = 100 * np.sin(timer / 500) + 100

        self.x += self.x_speed
        if self.x > display_width - self.width:
            self.x = display_width - self.width  # boundaries for enemy
            self.x_speed = -self.x_speed  # flip speed so that enemy moves into opposite direction
        elif self.x < 0:
            self.x = 0  # boundaries for ship
            self.x_speed = -self.x_speed  # flip speed so that enemy moves into opposite direction
        self.rect.x = self.x
        self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
        gameDisplay.blit(self.image, self.rect)

    def fire(self):
        enemy_lasers.add(EnemyLaser(self.x + 0.5 * self.width, self.y))
        pygame.mixer.Channel(2).play(enemy_laser_sound)

class EnemyLaser(pygame.sprite.Sprite):
    def __init__(self, xcoord, ycoord):
        pygame.sprite.Sprite.__init__(self)
        self.image = laser_red
        self.image = pygame.transform.flip(self.image, 0, 1)
        self.rect = self.image.get_rect()
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = xcoord - 0.5 * self.width  # depends on ship location
        self.y = ycoord  # These will be set at spawn because it depends on ship location
        self.speed = 7
        self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        self.y += self.speed
        if self.y > display_height:
            self.kill()
        else:
            self.rect.x = self.x
            self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
            gameDisplay.blit(self.image, self.rect)

class ChooseFont(object):
    def __init__(self, fonttype, fontsize, color):
        self.font = pygame.font.Font(fonttype, fontsize)
        self.color = color

    def message(self, text, xcoord, ycoord, centered=False):
        text_surface = self.font.render(text, True, self.color)
        text_rect = text_surface.get_rect()
        if centered is True:
            text_rect.center = (xcoord, ycoord)
        elif centered is False:
            text_rect.x = xcoord
            text_rect.y = ycoord
        gameDisplay.blit(text_surface, text_rect)

class Boss(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)  # call Sprite initializer
        self.image = boss_image
        self.rect = self.image.get_rect()
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = display_width * 0.5 - self.width * 0.5
        self.y = 50
        self.y_speed = 0
        self.mask = pygame.mask.from_surface(self.image)
        self.laser_timer_max = fps * 2  # update every 120 frames
        self.laser_timer = fps * 2  # update every 120 frames
        self.laser_list = [5, 10, 15, 20, 25]
        self.bomb_timer_max = fps * 4
        self.bomb_timer = self.bomb_timer_max
        self.x_speed = 3
        self.hp = 100
        self.maxhp = self.hp
        self.dead_timer = 170
        self.add_explosion_timer = 10
        self.randx = None
        self.randy = None
        self.hp_50 = False
        self.hp_25 = False

    def update(self):
        if self.hp > 0:
            self.laser_timer -= 1
            self.bomb_timer -= 1
            if self.laser_timer in self.laser_list:  # frames at which ship fires
                self.fire_laser()
            if self.laser_timer == 0:
                self.laser_timer = self.laser_timer_max
            if self.bomb_timer == 0:
                self.bomb_timer = self.bomb_timer_max
                self.fire_bomb()

            if self.hp < self.maxhp * 0.5 and self.hp_50 is False:
                if self.x_speed > 0:
                    self.x_speed = 5
                elif self.x_speed < 0:
                    self.x_speed = -5
                self.laser_timer_max = fps * 1.7
                self.bomb_timer_max = fps * 3.5
                self.hp_50 = True

            if self.hp < self.maxhp * 0.25 and self.hp_25 is False:
                if self.x_speed > 0:
                    self.x_speed = 7
                elif self.x_speed < 0:
                    self.x_speed = -7
                self.laser_timer_max = fps * 1.5
                self.bomb_timer_max = fps * 3
                self.hp_25 = True

        elif self.dead_timer > 0:
            self.x_speed = 1
            self.add_explosion_timer -= 1
            self.dead_timer -= 1
            if self.add_explosion_timer == 0:
                self.add_explosions()

                self.add_explosion_timer = 10

            for explosion in explosions_boss:
                explosion.update_moving(self.x_speed, self.y_speed)

        self.x += self.x_speed
        if self.x > display_width - self.width:
            self.x = display_width - self.width  # boundaries for enemy
            self.x_speed = -self.x_speed  # flip speed so that enemy moves into opposite direction
        elif self.x < 0:
            self.x = 0  # boundaries for ship
            self.x_speed = -self.x_speed  # flip speed so that enemy moves into opposite direction
        self.rect.x = self.x
        self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
        gameDisplay.blit(self.image, self.rect)

        self.draw_health()

    def fire_laser(self):
        enemy_lasers.add(EnemyLaser(self.x + 0.35 * self.width, self.y + 0.8 * self.height))
        enemy_lasers.add(EnemyLaser(self.x + 0.65 * self.width, self.y + 0.8 * self.height))
        pygame.mixer.Channel(2).play(enemy_laser_sound)

    def fire_bomb(self):
        boss_bomb.add(BossBomb(self.x, self.y))
        pygame.mixer.Channel(4).play(bomb_release_sound)

    def draw_health(self):
        color = red
        width_hp = self.width * (self.hp / self.maxhp)
        healthbar = pygame.Rect((self.x, self.y - 10, width_hp, 10))
        pygame.draw.rect(gameDisplay, color, healthbar)

    def add_explosions(self):
        for i in range(2):
            self.randx = random.randint(np.round(self.x) + 10 - 32, np.round(self.x) + self.width - 10 - 32)
            self.randy = random.randint(np.round(self.y) + 10 - 64, np.round(self.y) + self.height - 10 - 64)
            explosions_boss.add(Explosion(explosion1, self.randx, self.randy))
        pygame.mixer.Channel(3).play(explosion_sound)

class BossBomb(pygame.sprite.Sprite):
    def __init__(self, xcoord, ycoord):
        pygame.sprite.Sprite.__init__(self)
        self.image = missile
        self.image = pygame.transform.flip(self.image, 0, 1)
        self.rect = self.image.get_rect()
        self.width = self.rect.size[0]
        self.height = self.rect.size[1]
        self.x = xcoord - 0.5 * self.width  # depends on ship location
        self.y = ycoord  # These will be set at spawn because it depends on ship location
        self.xspeed = 0
        self.xspeedincr = 0.3
        self.xspeedmax = 5
        self.yspeed = 3
        self.mask = pygame.mask.from_surface(self.image)

    def update(self, xship, yship):
        if xship > self.x:
            if self.xspeed < self.xspeedmax:
                self.xspeed += self.xspeedincr
        elif xship < self.x:
            if self.xspeed > -self.xspeedmax:
                self.xspeed -= self.xspeedincr
        self.x += self.xspeed

        self.y += self.yspeed

        if self.y >= display_height - 200:
            self.kill()
            explosions.add(Explosion2(explosion2, self.x, self.y))
            pygame.mixer.Channel(5).play(bomb_explosion_sound)
        else:
            self.rect.x = self.x
            self.rect.y = self.y  # set the rect (not just blit) or collision won't work!
            gameDisplay.blit(self.image, self.rect)

def main_menu():
    button_width = start_button.get_rect().size[0]
    scheme_width = controlscheme.get_rect().size[0]
    button_x_center = (display_width - button_width) * 0.5
    scheme_x_center = (display_width - scheme_width) * 0.5

    # End game when this becomes true
    in_main_menu = True
    # Play the soundtrack
    pygame.mixer.Channel(0).play(game_music, loops=-1)

    # This is the game loop where all game logic happens
    while in_main_menu:

        # This checks all events that happen (which are located in pygame.event.get()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                pos = pygame.mouse.get_pos()
                if startbutton.collidepoint(pos):
                    pygame.mixer.Channel(1).play(button_sound)
                    global time_since_startbutton
                    time_since_startbutton = pygame.time.get_ticks()
                    game_loop()
                elif creditbutton.collidepoint(pos):
                    credit_loop()
                elif quitbutton.collidepoint(pos):
                    pygame.mixer.Channel(1).play(button_sound)
                    pygame.quit()
                    quit()

        # Update main menu
        gameDisplay.blit(background, (0, 0))
        startbutton = gameDisplay.blit(start_button, (button_x_center, display_height * 0.4))
        creditbutton = gameDisplay.blit(credit_button, (button_x_center, display_height * 0.5))
        quitbutton = gameDisplay.blit(quit_button, (button_x_center, display_height * 0.6))
        gameDisplay.blit(controlscheme, (scheme_x_center, display_height * 0.7))
        pygame.display.update()

def credit_loop():
    credits = True
    while credits:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    credits = False
        # Update
        gameDisplay.blit(credit_background, (0, 0))

        pygame.display.update()

def game_loop():

    # Instantiate Ship & Meteor and create a group for lasersprites
    global ship, ship_group, meteors, lasers, score_count, enemies, fps, timer, enemy_lasers, score_count
    global boss_bomb, explosions, explosions_boss
    ship_group = pygame.sprite.GroupSingle()
    boss_group = pygame.sprite.GroupSingle()
    boss_bomb = pygame.sprite.Group()
    enemies = pygame.sprite.Group()
    meteors = pygame.sprite.Group()
    lasers = pygame.sprite.Group()
    enemy_lasers = pygame.sprite.Group()
    explosions = pygame.sprite.Group()
    explosions_boss = pygame.sprite.Group()

    # Set variables and events needed for meteor shower
    add_meteor_event = pygame.USEREVENT + 1
    add_meteor_timer = 300  # add new meteor evert 300 ms
    ms_event = pygame.USEREVENT + 2
    ms_start = 60000  # ms after which meteor shower arrives
    ms_duration = 20000
    pygame.time.set_timer(add_meteor_event, add_meteor_timer)
    pygame.time.set_timer(ms_event, ms_start)
    ms_passed = False
    ms_announcement = False

    # Set variables needed to spawn enemies
    add_enemies_event = pygame.USEREVENT + 3
    add_enemies_timer = 5000  # add new enemies every 5000 ms
    pygame.time.set_timer(add_enemies_event, add_enemies_timer)
    num_enemies = 3
    enemies_meteors_spawning = True

    # Set variables for boss battle
    boss_battle = False
    won = False
    ship_centered = False
    boss_announcement = False

    # Instatiate other variables
    score_count = 0  # score
    meteors_dodged = 0
    enemies_killed = 0
    bosses_killed = 0
    fps = 60
    ship = Ship()
    ship_group.add(ship)  # Add ship once before playing loop starts

    # This is the game loop where all game logic happens
    playing = True
    while playing:
        timer = pygame.time.get_ticks() - time_since_startbutton  # ms that have passed since start

        if 30000 < timer <= 40000:
            num_enemies = 4
        elif 40000 < timer <= 50000:
            num_enemies = 5
        elif 50000 < timer <= 60000:
            num_enemies = 6

        # Check for global events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if not won:
                # Check for user-inputted event
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        ship.speed = -10
                    elif event.key == pygame.K_RIGHT:
                        ship.speed = 10
                    elif event.key == pygame.K_SPACE:
                        lasers.add(Laser(ship.x + 0.5*ship.width, ship.y))
                        pygame.mixer.Channel(1).play(laser_sound)

                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                        ship.speed = 0

            if ship_centered is True:
                # Button to return to main menu after defeating boss
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_RETURN:
                        playing = False

            # Check for events that only happen within certain time range
            if event.type == ms_event and ms_passed is False:  # This only occurs once
                ms_announcement_timer = timer
                ms_announcement = True
                enemies_meteors_spawning = False

            if event.type == add_enemies_event and enemies_meteors_spawning:
                for i in range(num_enemies):
                    enemies.add(EnemyGoon())

            try:
                if timer - ms_announcement_timer < 2000 and ms_announcement is True:  # display message 2000 ms
                    continue
                elif ms_announcement is True:
                    ms_announcement = False  # This makes sure announcement doesn't return anymore
                    ms_start_timer = timer  # Timestamp start of meteor shower
            except UnboundLocalError:
                continue
            try:
                if timer - ms_start_timer < ms_duration and ms_passed is False:
                    if event.type == add_meteor_event:  # add a meteor every time event is in event queue
                        meteors.add(Meteor())
                elif ms_passed is False:
                    ms_passed = True  # This makes sure ms doesn't return after it passed event is queued again
                    boss_announcement_timer = timer
                    boss_announcement = True
            except UnboundLocalError:
                continue

            try:
                if timer - boss_announcement_timer < 2000 and boss_announcement is True:
                    continue
                elif boss_announcement is True:
                    boss_announcement = False
                    boss_battle = True
                    boss = Boss()
                    boss_group.add(boss)
            except UnboundLocalError:
                continue

        # Update display and sprites
        gameDisplay.blit(background, (0, 0))
        ship.update()

        if len(meteors) < 1 and enemies_meteors_spawning:
            meteors.add(Meteor())
        for meteor in meteors:
            meteor.update()
            if meteor.y > display_height:
                meteor.kill()
                meteors_dodged += 1
                score_count += 10

        for laser in lasers:
            laser.update()

        for enemy in enemies:
            enemy.update()

        for laser in enemy_lasers:
            laser.update()

        if boss_battle is True:
            boss.update()
            for bomb in boss_bomb:
                bomb.update(ship.x + 0.5 * ship.width, ship.y + 0.5 * ship.height)

            boss_hit = pygame.sprite.groupcollide(lasers, boss_group, 1, 0, pygame.sprite.collide_mask)
            for sprite in boss_hit:
                if boss_hit[sprite]:
                    explosions_boss.add(Explosion(explosion1, sprite.x - 32, sprite.y - 64))  # 64 is w/l of explosion
                    pygame.mixer.Channel(3).play(explosion_sound)
                    boss.hp -= 1

            for explosion in explosions_boss:
                explosion.update_moving(boss.x_speed, boss.y_speed)

            if boss.dead_timer <= 0:
                explosions.add(Explosion(explosion3, boss.x - boss.width*0.5, boss.y - boss.height*0.5, 3, 5))
                del boss
                boss_battle = False
                won = True
                score_count += 1000
                bosses_killed += 1

        for explosion in explosions:
            explosion.update()

            if boss_battle is True:
                burned = pygame.sprite.groupcollide(ship_group, explosions, 0, 0, pygame.sprite.collide_mask)
                if burned:
                    explosions.add(Explosion(explosion3, ship.x - ship.width * 0.5, ship.y - ship.height * 0.5, 2, 5))
                    crashed_text.message('you died. BUT DO NOT PANIC!',
                                         display_width * 0.5, display_height * 0.5, centered=True)
                    pygame.display.update()
                    waiting = True
                    while waiting:
                        for event in pygame.event.get():
                            if event.type == pygame.QUIT:
                                pygame.quit()
                                quit()
                            if event.type == pygame.KEYDOWN:
                                if event.key == pygame.K_RETURN:
                                    waiting = False
                                    playing = False
                        for explosion in explosions:
                            explosion.update()
                        performance_text.message('Return to main menu by pressing Enter and try again.',
                                                 display_width * 0.5, 500, centered=True)
                        pygame.display.update()

        if boss_battle is False and won is True:
            if ship_centered is False:
                ship_centered = ship.to_end_position(display_width*0.5 - ship.width * 0.5)

        # Check for collisions after new display if updated
        crashed = pygame.sprite.groupcollide(ship_group, meteors, 0, 0, pygame.sprite.collide_mask)
        hit = pygame.sprite.groupcollide(enemy_lasers, ship_group, 1, 0, pygame.sprite.collide_mask)
        if crashed or hit:
            explosions.add(Explosion(explosion3, ship.x - ship.width * 0.5, ship.y - ship.height * 0.5, 2, 5))
            crashed_text.message('you died. BUT DO NOT PANIC!',
                                 display_width * 0.5, display_height * 0.5, centered=True)
            pygame.display.update()
            waiting = True
            while waiting:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        quit()

                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_RETURN:
                            waiting = False
                            playing = False

                for explosion in explosions:
                    explosion.update()
                performance_text.message('Return to main menu by pressing Enter and try again.',
                                         display_width * 0.5, 500, centered=True)
                pygame.display.update()

        # Kill sprites after collision
        pygame.sprite.groupcollide(lasers, meteors, 1, 0, pygame.sprite.collide_mask)
        pygame.sprite.groupcollide(enemy_lasers, meteors, 1, 0, pygame.sprite.collide_mask)

        enemy_hit = pygame.sprite.groupcollide(enemies, lasers, 1, 1, pygame.sprite.collide_mask)
        for sprite in enemy_hit:
            if enemy_hit[sprite]:
                explosions.add(Explosion(explosion1, sprite.x, sprite.y))
                pygame.mixer.Channel(3).play(explosion_sound)
                score_count += 100
                enemies_killed += 1

        # Lastly, show text
        performance_text.message('score: ' + str(score_count), 5, 0)
        performance_text.message('%i' % (timer/1000), display_width - 45, 0)

        if ms_announcement:
            shower_text.message('METEOR SHOWER INCOMING', display_width * 0.5, display_height * 0.5, centered=True)

        if boss_announcement:
            shower_text.message('FINAL BOSS INCOMING', display_width * 0.5, display_height * 0.5, centered=True)

        if ship_centered is True:
            performance_text.message('meteors dodged: %i' % meteors_dodged, display_width * 0.5, 360, centered=True)
            performance_text.message('enemies destroyed: %i:' % enemies_killed, display_width * 0.5, 380, centered=True)
            performance_text.message('bosses destroyed: %i' % bosses_killed, display_width * 0.5, 400, centered=True)
            endgame_score_text.message('Final score: %i' % score_count, display_width * 0.5, 430, centered=True)
            performance_text.message('press enter to return to main menu', display_width * 0.5, 500, centered=True)

        pygame.display.update()

        # Set FPS
        clock.tick(fps)

# Here we initialize pygame, set variables and start the actual game
pygame.init()
# pygame.mouse.set_cursor(*pygame.cursors.diamond)
pygame.mouse.set_cursor(*pygame.cursors.broken_x)

# Define some colors
black = (0, 0, 0)  # (R,G,B)
red = (255, 0, 0)
green = (0, 255, 0)

# Setup a window for the game
display_width = 800
display_height = 800
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption('MyFirstGame')  # Window Title

# -- Load sprites from spritesheets
spritesheet_explosion1 = pygame.image.load('Textures/explosions.png')
explosion1 = []
x_all = [628, 628, 628, 628, 576, 566, 562, 562, 562, 562, 924, 858, 792, 726, 660, 594, 924, 858, 792, 726, 660, 594,
         924, 764]
y_all = [772, 706, 640, 574, 938, 872, 772, 706, 640, 574, 502, 496, 496, 496, 496, 496, 436, 430, 430, 430, 430, 430,
         370, 826]
height = 64
width = 64

for i in range(24):
    frame = str(i)
    if len(frame) is 1:
        frame = '0' + frame
    x = x_all[i]
    y = y_all[i]
    explosion1.append(spritesheet_explosion1.subsurface(pygame.Rect(x, y, width, height)))

explosion3 = []
x_all = [100, 100, 100, 100, 888, 790, 692, 594, 496, 398, 300, 202, 104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 100]
y_all = [398, 300, 202, 104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 884, 786, 688, 590, 492, 394, 296, 198, 100, 2, 496]
h = 96
w = 96

for i in range(24):
    frame = str(i)
    if len(frame) is 1:
        frame = '0' + frame
    x = x_all[i]
    y = y_all[i]

    explosion3.append(spritesheet_explosion1.subsurface(pygame.Rect(x, y, h, w)))

spritesheet_explosion2 = pygame.image.load('Textures/particlefx_06.png')
height_exp = 128
width_exp = 128
explosion2 = []
for i in range(8):
    for j in range(8):
        explosion2.append(spritesheet_explosion2.subsurface(pygame.Rect(
            i*height_exp, j*width_exp, height_exp, width_exp)))

spritesheetspace = pygame.image.load('Textures/spritesheet_space.png')
start_button = spritesheetspace.subsurface(pygame.Rect(0, 117, 222, 39))
credit_button = spritesheetspace.subsurface(pygame.Rect(0, 78, 222, 39))
quit_button = spritesheetspace.subsurface(pygame.Rect(0, 0, 222, 39))
enemy1 = spritesheetspace.subsurface(pygame.Rect(423, 728, 93, 84))
enemy2 = spritesheetspace.subsurface(pygame.Rect(120, 604, 104, 84))
enemy3 = spritesheetspace.subsurface(pygame.Rect(144, 156, 103, 84))
laser_blue = spritesheetspace.subsurface(pygame.Rect(856, 421, 9, 54))
laser_red = spritesheetspace.subsurface(pygame.Rect(858, 230, 9, 54))
meteor1 = spritesheetspace.subsurface(pygame.Rect(224, 664, 101, 84))
meteor2 = spritesheetspace.subsurface(pygame.Rect(0, 520, 120, 98))
meteor3 = spritesheetspace.subsurface(pygame.Rect(518, 810, 89, 82))
meteor4 = spritesheetspace.subsurface(pygame.Rect(327, 452, 98, 96))
player_ship = spritesheetspace.subsurface(pygame.Rect(224, 832, 99, 75))
spritesheetspace2 = pygame.image.load('Textures/spritesheet_space2.png')
missile = spritesheetspace2.subsurface(pygame.Rect(1093, 711, 19, 40))
boss_image = spritesheetspace2.subsurface(pygame.Rect(276, 0, 172, 151))
controlscheme = pygame.image.load('Textures/controlscheme.png')
background = pygame.image.load('Textures/space_background.png').convert()
credit_background = pygame.image.load('Textures/credits.png').convert()

# Load files used in the game
game_music = pygame.mixer.Sound('Sounds/desert-travel.ogg')  # Channel 0
game_music.set_volume(0.5)
button_sound = pygame.mixer.Sound('Sounds/click_menu_sound.wav')  # Channel 1
laser_sound = pygame.mixer.Sound('Sounds/laser5.wav')  # Channel 1
enemy_laser_sound = pygame.mixer.Sound('Sounds/laser8.wav')  # Channel 2
enemy_laser_sound.set_volume(0.5)
explosion_sound = pygame.mixer.Sound('Sounds/explodemini.wav')  # Channel 3
bomb_release_sound = pygame.mixer.Sound('Sounds/weaponfire4.wav')  # Channel 4
bomb_explosion_sound = pygame.mixer.Sound('Sounds/explosion2.wav')  # Channel 5

# Load fonts to use in the game
performance_text = ChooseFont('Fonts/xirod.ttf', 15, green)
endgame_score_text = ChooseFont('Fonts/xirod.ttf', 30, green)
crashed_text = ChooseFont('Fonts/xirod.ttf', 30, red)
shower_text = ChooseFont('Fonts/xirod.ttf', 30, red)

# Define game clock to time things
clock = pygame.time.Clock()

main_menu()


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pandas_datareader as data
from sklearn.preprocessing import MinMaxScaler
# noinspection PyUnresolvedReferences
import silence_tensorflow.auto      # for ignoring tensorflow info and warnings
from keras.layers import Dense, Dropout, LSTM
from keras.models import Sequential
from datetime import date

# starting and ending of data frame
start = '2010-01-01'
end = date.today().strftime('%Y-%m-%d')

# data frame
df = data.DataReader('SBI', 'yahoo', start, end)
df = df.reset_index()
df = df.drop(['Date', 'Adj Close'], axis=1)

# splitting data into Training and Testing
data_training = pd.DataFrame(df['Close'][0:int(len(df) * 0.70)])
data_testing = pd.DataFrame(df['Close'][int(len(df) * 0.70): int(len(df))])

# scaling down the training data and converting it into an array
scale = MinMaxScaler(feature_range=(0, 1))
data_training_array = scale.fit_transform(data_training)

# splitting data into x_train and y_train
# x_train is taken as fist 100 values and y_train as 101 value
# then first value of x_train is dropped and y_train is inserted into x_train
# next y_train is taken as 102 value and same continues till last value
x_train = []
y_train = []

for i in range(100, data_training_array.shape[0]):
    x_train.append(data_training_array[i - 100: i])
    y_train.append(data_training_array[i, 0])

x_train, y_train = np.array(x_train), np.array(y_train)

# Simple LSTM Model
model = Sequential()

# layer 1
model.add(LSTM(units=50, activation='relu', return_sequences=True, input_shape=(x_train.shape[1], 1)))
model.add(Dropout(0.2))

# layer 2
model.add(LSTM(units=60, activation='relu', return_sequences=True))
model.add(Dropout(0.3))

# layer 3
model.add(LSTM(units=80, activation='relu', return_sequences=True))
model.add(Dropout(0.4))

# layer 4
model.add(LSTM(units=120, activation='relu'))
model.add(Dropout(0.5))

# dense layer
model.add(Dense(units=1))

# compile model with adam optimizer
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x_train, y_train, epochs=50)

# saving model
model.save('keras_model.h5')

# predicting values for testing data
past_100_days = data_training.tail(100)
final_df = past_100_days.append(data_testing, ignore_index=True)

# scaling down the testing data and converting it into an array
input_data = scale.fit_transform(final_df)

# splitting data into x_test and y_test
x_test = []
y_test = []

for i in range(100, input_data.shape[0]):
    x_test.append(input_data[i - 100: i])
    y_test.append(input_data[i, 0])

x_test, y_test = np.array(x_test), np.array(y_test)

# Making Prediction
y_predicted = model.predict(x_test)

# scaling up the predicted data
scale_factor = 1/scale.scale_[0]

y_predicted = y_predicted * scale_factor
y_test = y_test * scale_factor

# plotting original vs predicted data
plt.figure(figsize=(12, 6))
plt.plot(y_test, 'b', label='Original Price')
plt.plot(y_predicted, 'r', label='Predicted Price')
plt.xlabel('Time')
plt.ylabel('Price')
plt.legend()
plt.show()


# Network Delay Time - https://leetcode.com/problems/network-delay-time/
'''There are N network nodes, labelled 1 to N.

Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node,
v is the target node, and w is the time it takes for a signal to travel from source to target.

Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1.

Example 1:

Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2
Output: 2'''

# Dijkstra's Algorithm - 0(n2)
from collections import defaultdict
class Solution:
    def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:

        def getMinDistance(distance, seen):
            minNode = float('inf')
            minIndex = -1
            for i in range(1, N+1):
                if distance[i] < minNode and not seen[i]:
                    minNode = distance[i]
                    minIndex = i
            return minIndex

        graph = defaultdict(list)
        for u, v, w in times:
            graph[u].append((v,w))

        seen = [False] * (N+1)
        distance = {node: float('inf') for node in range(1, N+1)}
        distance[K] = 0

        while True:
            u = getMinDistance(distance, seen)
            if u < 0:
                break
            seen[u] = True
            for neighbour, time in graph[u]:
                if distance[neighbour] > distance[u] + time:
                    distance[neighbour] = distance[u] + time

        output = max(distance.values())
        return output if output != float('inf') else -1

# Dijkstra's Algorithm - 0(nlogn)
from collections import defaultdict
import heapq
class Solution:
    def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:

        graph = defaultdict(list)
        for u, v, w in times:
            graph[u].append((v,w))

        distance = {}
        q = [(0, K)]

        while q:
            time, node = heapq.heappop(q)
            if node in distance:
                continue
            distance[node] = time
            for neighbour, timeTravelled in graph[node]:
                if neighbour not in distance:
                    heapq.heappush(q, (time+timeTravelled, neighbour))
        return max(distance.values()) if len(distance) == N else -1


import time
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torch.optim.lr_scheduler import ExponentialLR
import argparse
import os
# pylint: disable=E1101, W0612

"""
# GPU CLUSTER
source = '/vol/gpudata/rar2417/src/model1'  #path to code location
data_path = '/vol/gpudata/rar2417/Data' #path to the parent directory of 'audio'
output_path = '/vol/gpudata/rar2417/results/model1' #path to output the results
model_path = output_path + '/models/resnet_bgru_1.ckpt' #path to find pre-trained model
"""

# HOME SETUP
source = '/home/r2d9/Desktop/SpeechRecognitionProject'  #path to code location
data_path = '/home/r2d9/Desktop/Data/train' #path to the parent directory of 'audio'
output_path = '/home/r2d9/Desktop/results' #path to output the results
model_path = output_path + '/models/resnet_bgru_1.ckpt' #path to find pre-trained model

parser = argparse.ArgumentParser()
parser.add_argument('-key', '--filekey', type = str, help='key for multiple trainings')
parser.add_argument('-lr', '--learning_rate', type = float, help='LEARNING_RATE')
parser.add_argument('-md', '--mode', type = int, help='1, 2 or 3')
args = parser.parse_args()

KEY = '' #provided for convenience, easy way to differenciate experiments
if args.filekey is not None:
    KEY = args.filekey
MODE = 4 #3-step training procedure
if args.mode is not None:
    MODE = args.mode

os.chdir(source)
from dataset import dataset
from model_resnet_bgru import Network, accuracy

# Configuration
start = time.time()
torch.set_default_tensor_type('torch.FloatTensor')
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Hyperparams
NUM_EPOCHS = 50
BATCH_SIZE = 20
LAMBDA = 0.87
LEARNING_RATE = 0.0003
if args.learning_rate is not None:
    LEARNING_RATE = args.learning_rate

# Model & Dataset
data = dataset(data_path + '/training_list.txt', data_path + '/audio')
valset = dataset(data_path + '/validation_list.txt', data_path + '/audio')
testset = dataset(data_path + '/testing_list.txt', data_path + '/audio')

if MODE == 1:   #training only the resnet
    model = Network(mode=1).to(device)
if MODE == 2:   #training only the bgru
    model = Network().to(device)
    model.load_state_dict(torch.load(model_path))
    for name, param in model.named_parameters():
        if 'gru' in name:
            param.requires_grad = True
        if 'resnet' in name:
            param.requires_grad = False
if MODE == 3:   #training resnet and bgru from pre-trained model
    model = Network().to(device)
    model.load_state_dict(torch.load(model_path))
    for params in model.parameters():
        params.requires_grad = True
if MODE == 4:   #training everything in one go from scratch
    model = Network().to(device)
    for params in model.parameters():
        params.requires_grad = True

# Loss and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=LEARNING_RATE)
scheduler = ExponentialLR(optimizer, LAMBDA)    #learning rate decay, halved every 5 epochs
epoch, estop, maxval, maxind = 0, False, 0, 0

while epoch < NUM_EPOCHS and not estop: #early stopping
    dataloader = DataLoader(data, batch_size=BATCH_SIZE, shuffle=True, drop_last=False)
    if epoch > 4:   #fixed learning rate for first 5 epochs
        scheduler.step()

    for i_batch, batch in enumerate(dataloader):
        # Forward
        optimizer.zero_grad()
        outputs = model(batch['audio'])
        loss = criterion(outputs, batch['label'].to(device))

        # Backward and optimize
        loss.backward()
        optimizer.step()

        # Save loss
        with open(output_path +'/loss_'+KEY+'.txt', 'a') as myfile:
            myfile.write(str(loss.item())+'\n')

    # Save model, accuracy at each epoch
    newval = accuracy(model, valset, output_path + '/val_'+KEY+'.txt', 4) #accuracy on validation set for early-stopping
    accuracy(model, dataset, output_path + '/train_'+KEY+'.txt', 4) #accuracy on training set to monitor overfitting
    accuracy(model, testset, output_path + '/test_'+KEY+'.txt', 4) #accuracy on testing set

    # Early stopping
    if newval > maxval:
        maxval = newval
        maxind = epoch
        if MODE == 1:
            torch.save(model.state_dict(), output_path + '/models/resnet_'+KEY+'.ckpt')
        if MODE == 2:
            torch.save(model.state_dict(), output_path +'/models/bgru_'+KEY+'.ckpt')
        if MODE == 3:
            torch.save(model.state_dict(), output_path +'/models/resnet_bgru_3_'+KEY+'.ckpt')
        if MODE == 4:
            torch.save(model.state_dict(), output_path +'/models/resnet_bgru_'+KEY+'.ckpt')

    if epoch > maxind + 4:
        estop = True
    epoch += 1
    data.resample_unknown_class()

print('key  ', KEY)
print('time  ', time.time()-start)
print('epochs  ', epoch)


import cv2
import numpy as np
from dlclive import DLCLive, Processor
from skimage.transform import (hough_line, hough_line_peaks)

folder = 'model/'
dlc_proc = Processor()
dlc_live = DLCLive(folder, processor=dlc_proc)
dlc_live.init_inference()

i = 0

while True:
    # Load frame
    i += 1
    frame = cv2.imread('frames/ (' + str(i) + ').jpg')
    frame = cv2.resize(frame, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

    # Get poses
    pose = dlc_live.get_pose(frame)
    nose = (int(pose[0, 0]), int(pose[0, 1]))
    head = (int(pose[1, 0]), int(pose[1, 1]))
    body = (int(pose[2, 0]), int(pose[2, 1]))

    # Draw lines on Stage for angle measurement
    stage = np.zeros((frame.shape[0], frame.shape[1]), np.uint8)  # Clear frame
    stage = cv2.line(stage, nose, head, 255, 1)

    # Perform Hough Transformation to detect lines
    hspace, angles, distances = hough_line(stage)

    # Find angle
    angle = []
    for _, a, distances in zip(*hough_line_peaks(hspace, angles, distances)):
        angle.append(a)

    # Obtain angle for each line
    angles = [a * 180 / np.pi for a in angle]

    # Get length of radius for angle visualization
    radius = cv2.norm(head, nose)
    axes = (int(radius), int(radius))

    # Get 360 degree readout
    degree = int(angles[0])
    if nose[0] > head[0] and degree < 0:
        degree = 180 + degree
    elif nose[0] < head[0] and degree < 0:
        degree = 360 + degree
    elif nose[0] < head[0] and degree > 0:
        degree = 180 + degree

    # Draw lines
    frame = cv2.line(frame, nose, head, (255, 255, 0), 1, lineType=cv2.LINE_AA)
    frame = cv2.line(frame, (head[0], int(head[1] - radius)), head, (255, 255, 0), 1, lineType=cv2.LINE_AA)
    frame = cv2.putText(frame, str(degree), (head[0] - 50, head[1] - 50), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 0, 255),
                        lineType=cv2.LINE_AA)

    # Draw arc of angle
    if nose[0] >= head[0]:
        frame = cv2.ellipse(frame, head, axes, -90, degree, 0, (255, 255, 0), lineType=cv2.LINE_AA)
    else:
        frame = cv2.ellipse(frame, head, axes, -90, 0, degree, (255, 255, 0), lineType=cv2.LINE_AA)

    # Show video
    cv2.imshow('Pose', frame)
    cv2.imwrite("output/head_angle/" + str(i) + ".png", frame)
    cv2.waitKey(1)

    # Reset loop
    if i == 969:
        i = 0


from collections import namedtuple, defaultdict
import numpy as np
import bmesh
import bpy

from ..math import get_dist_sq
from ..log import log, logd
from ..helpers import get_context, get_modifier_mask

# shape_key_apply_modifiers TODO:
# - Specialcase more merging modifiers, solidify for example
# - Transfer vertex order. Is it still necessary if all merging modifiers are covered?
#   Is it possible to identify which face went where without guessing?

class ShapeKeyInfo(namedtuple('ShapeKeyInfo', ['coords', 'interpolation', 'mute', 'name',
    'slider_max', 'slider_min', 'value', 'vertex_group'])):
    __slots__ = ()

    """Helper to preserve shape key information."""

    @classmethod
    def from_shape_key_with_empty_data(cls, shape_key):
        return cls(
            coords=np.empty(0, dtype=np.single),
            interpolation=shape_key.interpolation,
            mute=shape_key.mute,
            name=shape_key.name,
            # relative_key=shape_key.relative_key.name,
            slider_max=shape_key.slider_max,
            slider_min=shape_key.slider_min,
            value=shape_key.value,
            vertex_group=shape_key.vertex_group,
        )

    @classmethod
    def from_shape_key(cls, shape_key):
        info = cls.from_shape_key_with_empty_data(shape_key)
        info.get_coords_from(shape_key.data)
        return info

    def get_coords_from(self, vertices):
        self.coords.resize(len(vertices) * 3, refcheck=False)
        vertices.foreach_get('co', self.coords)

    def put_coords_into(self, vertices):
        vertices.foreach_set('co', self.coords)

def weld_mesh(mesh, weld_map):
    """Welds mesh vertices according to a source index to destination index weld map."""

    bm = bmesh.new()
    bm.from_mesh(mesh)
    bm.verts.ensure_lookup_table()
    targetmap = {bm.verts[src_idx]: bm.verts[dst_idx] for src_idx, dst_idx in weld_map.items()}
    bmesh.ops.weld_verts(bm, targetmap=targetmap)
    bm.to_mesh(mesh)
    bm.free()

def apply_modifier(modifier):
    try:
        bpy.ops.object.modifier_apply(get_context(modifier.id_data), modifier=modifier.name)
    except RuntimeError:
        logd(f"Couldn't apply {modifier.type} modifier {modifier.name}")

class ModifierHandler:
    """Subclass this to define special behavior when applying different modifiers."""

    modifier_type = None
    modifier_name = None

    def __init__(self, modifier):
        self.modifier_name = modifier.name

    @classmethod
    def poll(cls, modifier):
        return cls.modifier_type is None or modifier.type == cls.modifier_type

    def apply(self, obj):
        apply_modifier(obj.modifiers[self.modifier_name])

class MirrorModifierHandler(ModifierHandler):
    modifier_type = 'MIRROR'
    weld_map = None  # Specifies vertex pairs to be welded

    def __init__(self, modifier):
        super().__init__(modifier)
        self.merge_dist = modifier.merge_threshold
        self.num_mirrors = sum(modifier.use_axis)

    @classmethod
    def poll(cls, modifier):
        return super().poll(modifier) and modifier.use_mirror_merge and any(modifier.use_axis)

    def apply(self, obj):
        modifier = obj.modifiers[self.modifier_name]

        modifier.use_mirror_merge = False
        bpy.ops.object.modifier_apply(get_context(obj), modifier=modifier.name)

        if not self.weld_map:
            self.fill_weld_map(obj)
        weld_mesh(obj.data, self.weld_map)

    def fill_weld_map(self, obj):
        mesh = obj.data
        num_verts = len(mesh.vertices) // (2 ** self.num_mirrors)  # Num of verts before mirroring
        merge_dist_sq = self.merge_dist ** 2

        # Only consider pairs of mirrored vertices for merging. Probably breaks if flip is enabled
        welds = []
        for n in range(self.num_mirrors):
            num_part_verts = num_verts * (2 ** n)

            new_welds = []
            for src_idx, dst_idx in welds:
                new_welds.append((src_idx + num_part_verts, dst_idx + num_part_verts))
            welds.extend(new_welds)

            for vert_idx in range(num_part_verts):
                vert = mesh.vertices[vert_idx]
                other_vert_idx = vert_idx + num_part_verts
                other_vert = mesh.vertices[other_vert_idx]
                if get_dist_sq(vert.co, other_vert.co) <= merge_dist_sq:
                    welds.append((other_vert_idx, vert_idx))

        # Resolve the welds into a single dict. Not too robust but weld_verts doesn't complain
        self.weld_map = weld_map = {}
        weld_map_reverse = defaultdict(list)
        for src_idx, dst_idx in welds:
            dst_idx = weld_map.get(dst_idx, dst_idx)
            weld_map[src_idx] = dst_idx
            old_idxs = weld_map_reverse.get(src_idx, [])
            for old_idx in old_idxs:
                weld_map[old_idx] = dst_idx
                weld_map_reverse[dst_idx].append(old_idx)
            weld_map_reverse[dst_idx].append(src_idx)

class WeldModifierHandler(ModifierHandler):
    modifier_type = 'WELD'
    weld_map = None  # Specifies vertex pairs to be welded

    def __init__(self, modifier):
        super().__init__(modifier)
        self.merge_dist = modifier.merge_threshold
        self.vertex_group = modifier.vertex_group
        self.invert_vertex_group = modifier.invert_vertex_group

    @classmethod
    def poll(cls, modifier):
        return super().poll(modifier) and modifier.mode == 'ALL'

    def apply(self, obj):
        modifier = obj.modifiers[self.modifier_name]

        bpy.ops.object.modifier_remove(get_context(obj), modifier=modifier.name)

        if not self.weld_map:
            self.fill_weld_map(obj)
        weld_mesh(obj.data, self.weld_map)

    def fill_weld_map(self, obj):
        mesh = obj.data
        vg = obj.vertex_groups.get(self.vertex_group)
        invert = self.invert_vertex_group

        bm = bmesh.new()
        bm.from_mesh(mesh)
        bm.verts.ensure_lookup_table()
        deform_layer = bm.verts.layers.deform.active
        if deform_layer and vg:
            # Handle vertex group filtering
            verts = [v for v in bm.verts if bool(v[deform_layer].get(vg.index, 0.0)) != invert]
        else:
            verts = bm.verts
        targetmap = bmesh.ops.find_doubles(bm, verts=verts, dist=self.merge_dist)['targetmap']
        self.weld_map = {src.index: dst.index for src, dst in targetmap.items()}
        bm.free()

modifier_handler_classes = (
    MirrorModifierHandler,
    WeldModifierHandler,
    ModifierHandler,
)

# Incomplete map of modifier type to icon
modifier_icons = {
    'DATA_TRANSFER': 'MOD_DATA_TRANSFER',
    'MESH_CACHE': 'MOD_MESHDEFORM',
    'MESH_SEQUENCE_CACHE': 'MOD_MESHDEFORM',
    'NORMAL_EDIT': 'MOD_NORMALEDIT',
    'WEIGHTED_NORMAL': 'MOD_NORMALEDIT',
    'UV_PROJECT': 'MOD_UVPROJECT',
    'UV_WARP': 'MOD_UVPROJECT',
    'VERTEX_WEIGHT_EDIT': 'MOD_VERTEX_WEIGHT',
    'VERTEX_WEIGHT_MIX': 'MOD_VERTEX_WEIGHT',
    'VERTEX_WEIGHT_PROXIMITY': 'MOD_VERTEX_WEIGHT',

    'ARRAY': 'MOD_ARRAY',
    'BEVEL': 'MOD_BEVEL',
    'BOOLEAN': 'MOD_BOOLEAN',
    'BUILD': 'MOD_BUILD',
    'DECIMATE': 'MOD_DECIM',
    'EDGE_SPLIT': 'MOD_EDGESPLIT',
    'NODES': 'NODETREE',
    'MASK': 'MOD_MASK',
    'MIRROR': 'MOD_MIRROR',
    'MULTIRES': 'MOD_MULTIRES',
    'REMESH': 'MOD_REMESH',
    'SCREW': 'MOD_SCREW',
    'SKIN': 'MOD_SKIN',
    'SOLIDIFY': 'MOD_SOLIDIFY',
    'SUBSURF': 'MOD_SUBSURF',
    'TRIANGULATE': 'MOD_TRIANGULATE',
    'VOLUME_TO_MESH': 'VOLUME_DATA',
    'WELD': 'AUTOMERGE_OFF',
    'WIREFRAME': 'MOD_WIREFRAME',

    'ARMATURE': 'MOD_ARMATURE',
    'CAST': 'MOD_CAST',
    'CURVE': 'MOD_CURVE',
    'DISPLACE': 'MOD_DISPLACE',
    'HOOK': 'HOOK',
    'LAPLACIANDEFORM': 'MOD_MESHDEFORM',
    'LATTICE': 'MOD_LATTICE',
    'MESH_DEFORM': 'MOD_MESHDEFORM',
    'SHRINKWRAP': 'MOD_SHRINKWRAP',
    'SIMPLE_DEFORM': 'MOD_SIMPLEDEFORM',
    'SMOOTH': 'MOD_SMOOTH',
    'CORRECTIVE_SMOOTH': 'MOD_SMOOTH',
    'LAPLACIANSMOOTH': 'MOD_SMOOTH',
    'SURFACE_DEFORM': 'MOD_MESHDEFORM',
    'WARP': 'MOD_WARP',
    'WAVE': 'MOD_WAVE',
}

ignored_modifier_types = frozenset((
    'CLOTH',
    'COLLISION',
    'DYNAMIC_PAINT',
    'EXPLODE',
    'FLUID',
    'OCEAN',
    'PARTICLE_INSTANCE',
    'PARTICLE_SYSTEM',
    'SOFT_BODY',
))

class GRET_OT_shape_key_apply_modifiers(bpy.types.Operator):
    """Applies viewport modifiers while preserving shape keys"""

    bl_idname = "gret.shape_key_apply_modifiers"
    bl_label = "Apply Modifiers with Shape Keys"
    bl_context = "objectmode"
    bl_options = {'REGISTER', 'UNDO'}

    modifier_mask: bpy.props.BoolVectorProperty(
        name="Apply Modifier",
        description="Whether this modifier should be applied",
        size=32,  # Maximum allowed by Blender, will need some hack if more are required
        default=[True] * 32,
    )

    modifier_info = []  # Only used to draw buttons when operator is invoked

    @classmethod
    def poll(cls, context):
        return context.mode == 'OBJECT' and context.object and context.object.type == 'MESH'

    def draw(self, context):
        layout = self.layout
        layout.ui_units_x = 10.0
        obj = context.object

        layout.label(text="Select modifiers to apply:")

        col = layout.column(align=True)
        for modifier_index, (modifier_type, modifier_name) in enumerate(self.modifier_info):
            if modifier_type in ignored_modifier_types:
                continue

            icon = modifier_icons.get(modifier_type, 'BLANK1')
            col.prop(self, 'modifier_mask', index=modifier_index, icon=icon, text=modifier_name)

    def invoke(self, context, event):
        obj = context.object

        # Cache modifier info to be shown on panel. Otherwise redo_last won't work correctly
        # Side note: the displayed icon for show_viewport is hardcoded to change when toggled on
        def should_apply_modifier(mod):
            return (mod.show_viewport
                and mod.type not in ignored_modifier_types
                and mod.type != 'ARMATURE')  # Don't apply armatures by default
        self.modifier_info = [(mod.type, mod.name) for mod in obj.modifiers]
        self.modifier_mask = get_modifier_mask(obj, should_apply_modifier)

        return context.window_manager.invoke_props_dialog(self)

    def execute(self, context):
        obj = context.active_object

        if not any(self.modifier_mask[:len(obj.modifiers)]):
            # There are no modifiers to apply
            return {'FINISHED'}

        if obj.data.users > 1:
            # Make single user copy
            obj.data = obj.data.copy()

        num_shape_keys = len(obj.data.shape_keys.key_blocks) if obj.data.shape_keys else 0
        if not num_shape_keys:
            # No shape keys, just apply the modifiers
            for modifier, mask in zip(obj.modifiers[:], self.modifier_mask):
                if mask:
                    apply_modifier(modifier)
            return {'FINISHED'}

        print(f"Applying modifiers with {num_shape_keys} shape keys")
        mesh_copy = obj.data.copy()  # Copy for convenience, to be able to call from_existing(fcurve)
        shape_keys = obj.data.shape_keys.key_blocks if obj.data.shape_keys else []
        shape_key_infos = []
        saved_active_shape_key_index = obj.active_shape_key_index
        saved_show_only_shape_key = obj.show_only_shape_key

        # Start by separating each shape key so modifiers can be applied one by one
        shape_key_objs = []
        for shape_key in shape_keys:
            shape_key_info = ShapeKeyInfo.from_shape_key(shape_key)
            shape_key_infos.append(shape_key_info)

            new_obj = obj.copy()
            new_obj.name = f"{obj.name}_{shape_key.name}"
            new_obj.data = obj.data.copy()
            shape_key_objs.append(new_obj)

        # Handle modifiers accordingly. This means recording welded vertex pairs for mirrors and such
        obj.shape_key_clear()
        modifier_handlers = []
        for modifier, mask in zip(obj.modifiers[:], self.modifier_mask):
            if mask:
                for modifier_handler_cls in modifier_handler_classes:
                    if modifier_handler_cls.poll(modifier):
                        modifier_handler = modifier_handler_cls(modifier)
                        modifier_handler.apply(obj)
                        modifier_handlers.append(modifier_handler)
                        break

        # Store vertex coordinates of each shape key with modifiers applied
        for sk_info, sk_obj in zip(shape_key_infos, shape_key_objs):
            sk_mesh = sk_obj.data
            sk_obj.shape_key_clear()
            sk_info.put_coords_into(sk_mesh.vertices)
            for modifier_handler in modifier_handlers:
                modifier_handler.apply(sk_obj)
            sk_info.get_coords_from(sk_mesh.vertices)

            bpy.data.objects.remove(sk_obj)
            bpy.data.meshes.remove(sk_mesh)

        # Add the shape keys back
        for shape_key_info in shape_key_infos:
            shape_key = obj.shape_key_add()
            shape_key.interpolation = shape_key_info.interpolation
            shape_key.mute = shape_key_info.mute
            shape_key.name = shape_key_info.name
            shape_key.slider_max = shape_key_info.slider_max
            shape_key.slider_min = shape_key_info.slider_min
            shape_key.value = shape_key_info.value
            shape_key.vertex_group = shape_key_info.vertex_group
            if len(shape_key.data) * 3 != len(shape_key_info.coords):
                self.report({'ERROR'}, f"Vertex count for {shape_key.name} did not match, "
                    "the shape key will be lost.")
                continue
            shape_key_info.put_coords_into(shape_key.data)

        # Recreate drivers
        if mesh_copy.shape_keys and mesh_copy.shape_keys.animation_data:
            for fcurve in mesh_copy.shape_keys.animation_data.drivers:
                if obj.data.shape_keys.animation_data is None:
                    obj.data.shape_keys.animation_data_create()
                obj.data.shape_keys.animation_data.drivers.from_existing(src_driver=fcurve)

        # Clean up
        obj.show_only_shape_key = saved_show_only_shape_key
        obj.active_shape_key_index = saved_active_shape_key_index
        bpy.data.meshes.remove(mesh_copy)

        return {'FINISHED'}

def draw_menu(self, context):
    self.layout.operator(GRET_OT_shape_key_apply_modifiers.bl_idname, icon='CHECKMARK')

def register(settings, prefs):
    bpy.utils.register_class(GRET_OT_shape_key_apply_modifiers)
    bpy.types.MESH_MT_shape_key_context_menu.append(draw_menu)

def unregister():
    bpy.types.MESH_MT_shape_key_context_menu.remove(draw_menu)
    bpy.utils.unregister_class(GRET_OT_shape_key_apply_modifiers)


"""
Artifact module.
"""
from __future__ import annotations

import typing
from typing import Self

from sdk.entities.artifact.metadata import build_metadata
from sdk.entities.artifact.spec import build_spec
from sdk.entities.base.entity import Entity
from sdk.entities.utils.utils import get_uiid
from sdk.utils.api import DTO_ARTF, api_ctx_create, api_ctx_update
from sdk.utils.exceptions import EntityError
from sdk.utils.factories import get_context, get_default_store
from sdk.utils.file_utils import check_file, get_dir
from sdk.utils.uri_utils import get_name_from_uri, get_uri_scheme, rebuild_uri

if typing.TYPE_CHECKING:
    from sdk.entities.artifact.metadata import ArtifactMetadata
    from sdk.entities.artifact.spec import ArtifactSpec

class Artifact(Entity):
    """
    A class representing a artifact.
    """

    def __init__(
        self,
        project: str,
        name: str,
        kind: str = None,
        metadata: ArtifactMetadata = None,
        spec: ArtifactSpec = None,
        local: bool = False,
        embedded: bool = False,
        uuid: str = None,
        **kwargs,
    ) -> None:
        """
        Initialize the Artifact instance.

        Parameters
        ----------
        project : str
            Name of the project.
        name : str
            Name of the artifact.
        kind : str
            Kind of the artifact
        metadata : ArtifactMetadata
            Metadata of the object.
        spec : ArtifactSpec
            Specification of the object.
        local: bool
            If True, run locally.
        embedded: bool
            If True embed object in backend.
        **kwargs
            Keyword arguments.
        """
        super().__init__()
        self.project = project
        self.name = name
        self.kind = kind if kind is not None else "artifact"
        self.metadata = metadata if metadata is not None else build_metadata(name=name)
        self.spec = spec if spec is not None else build_spec(self.kind, **{})
        self.embedded = embedded
        self.id = uuid if uuid is not None else get_uiid()

        self._local = local

        # Temporary local artifact path (see as_file())
        self._temp_path = None

        # Set new attributes
        self._any_setter(**kwargs)

        # Set context
        self._context = get_context(self.project)

        # Set key in spec store://<project>/artifacts/<kind>/<name>:<uuid>
        self.spec.key = (
            f"store://{self.project}/artifacts/{self.kind}/{self.name}:{self.id}"
        )

    #############################
    #  Save / Export
    #############################

    def save(self, uuid: str = None) -> dict:
        """
        Save artifact into backend.

        Parameters
        ----------
        uuid : str
            UUID.

        Returns
        -------
        dict
            Mapping representation of Artifact from backend.
        """
        if self._local:
            raise EntityError("Use .export() for local execution.")

        obj = self.to_dict()

        if uuid is None:
            api = api_ctx_create(self.project, DTO_ARTF)
            return self._context.create_object(obj, api)

        self.id = uuid
        api = api_ctx_update(self.project, DTO_ARTF, self.name, uuid)
        return self._context.update_object(obj, api)

    def export(self, filename: str = None) -> None:
        """
        Export object as a YAML file.

        Parameters
        ----------
        filename : str
            Name of the export YAML file. If not specified, the default value is used.

        Returns
        -------
        None
        """
        obj = self.to_dict()
        filename = (
            filename
            if filename is not None
            else f"artifact_{self.project}_{self.name}.yaml"
        )
        self._export_object(filename, obj)

    #############################
    #  Artifacts Methods
    #############################

    def as_file(self, target: str = None) -> str:
        """
        Get artifact as file. In the case of a local store, the store returns the current
        path of the artifact. In the case of a remote store, the artifact is downloaded in
        a temporary directory.

        Parameters
        ----------
        target : str
            Target path is the remote path of the artifact where it is stored

        Returns
        -------
        str
            Temporary path of the artifact.
        """
        # Get store
        store = get_default_store()

        # If local store, return local artifact path
        if store.is_local():
            self._check_src()
            return self.spec.src_path

        # Check if target path is specified
        self._check_target(target)

        # Check if target path is remote
        self._check_remote()

        # Download artifact and return path
        self._temp_path = store.download(self.spec.target_path)
        return self._temp_path

    def download(
        self, target: str = None, dst: str = None, overwrite: bool = False
    ) -> str:
        """
        Download artifact from backend.

        Parameters
        ----------
        target : str
            Target path is the remote path of the artifact
        dst : str
            Destination path as filename
        overwrite : bool
            Specify if overwrite an existing file

        Returns
        -------
        str
            Path of the downloaded artifact.
        """

        # Check if target path is specified
        self._check_target(target)

        # Check if target path is remote
        self._check_remote()

        # Check if download destination path is specified and rebuild it if necessary
        dst = self._rebuild_dst(dst)

        # Check if destination path exists for overwrite
        self._check_overwrite(dst, overwrite)

        # Get store
        store = get_default_store()

        # Download artifact and return path
        return store.download(self.spec.target_path, dst)

    def upload(self, source: str = None, target: str = None) -> str:
        """
        Upload artifact to backend.

        Parameters
        ----------
        source : str
            Source path is the local path of the artifact
        target : str
            Target path is the remote path of the artifact

        Returns
        -------
        str
            Path of the uploaded artifact.
        """
        # Check if source path is provided.
        self._check_src(source)

        # Check if source path is local
        self._check_local()

        # Check if target path is provided.
        self._check_target(target, upload=True)

        # Check if target path is remote
        self._check_remote()

        # Get store
        store = get_default_store()

        # Upload artifact and return remote path
        return store.upload(self.spec.src_path, self.spec.target_path)

    #############################
    #  Private Helpers
    #############################

    def _check_target(self, target: str = None, upload: bool = False) -> None:
        """
        Check if target path is specified.

        Parameters
        ----------
        target : str
            Target path is the remote path of the artifact

        upload : bool
            Specify if target path is for upload

        Returns
        -------
        None
        """
        if self.spec.target_path is None:
            if target is None:
                if not upload:
                    raise EntityError("Target path is not specified.")
                path = get_dir(self.spec.src_path)
                filename = get_name_from_uri(self.spec.src_path)
                target_path = rebuild_uri(f"{path}/{filename}")
                self.spec.target_path = target_path
                return
            self.spec.target_path = target
            return

    def _check_src(self, src: str = None) -> None:
        """
        Check if source path is specified.

        Parameters
        ----------
        src : str
            Source path is the local path of the artifact

        Returns
        -------
        None

        Raises
        ------
        Exception
            If source path is not specified.
        """
        if self.spec.src_path is None:
            if src is None:
                raise EntityError("Source path is not specified.")
            self.spec.src_path = src

    def _check_remote(self) -> None:
        """
        Check if target path is remote.

        Parameters
        ----------
        ignore_raise : bool
            Specify if raise an exception if target path is not remote

        Returns
        -------
        None

        Raises
        ------
        Exception
            If target path is not remote.
        """
        if self.spec.target_path is None:
            return
        if get_uri_scheme(self.spec.target_path) in ["", "file"]:
            raise EntityError("Only remote source URIs are supported for target paths")

    def _check_local(self) -> None:
        """
        Check if source path is local.

        Returns
        -------
        None

        Raises
        ------
        Exception
            If source path is not local.
        """
        if get_uri_scheme(self.spec.src_path) not in ["", "file"]:
            raise EntityError("Only local paths are supported for source paths.")

    def _rebuild_dst(self, dst: str = None) -> None:
        """
        Check if destination path is specified.

        Parameters
        ----------
        dst : str
            Destination path as filename

        Returns
        -------
        str
            Destination path as filename.
        """
        if dst is None:
            dst = f"./{get_name_from_uri(self.spec.target_path)}"
        return dst

    @staticmethod
    def _check_overwrite(dst: str, overwrite: bool) -> None:
        """
        Check if destination path exists for overwrite.

        Parameters
        ----------
        dst : str
            Destination path as filename.
        overwrite : bool
            Specify if overwrite an existing file.

        Raises
        ------
        Exception
            If destination path exists and overwrite is False.
        """
        if check_file(dst) and not overwrite:
            raise EntityError(f"File {dst} already exists.")

    #############################
    #  Getters and Setters
    #############################

    @property
    def local(self) -> bool:
        """
        Get local flag.
        """
        return self._local

    @property
    def temp_path(self) -> str:
        """
        Get temporary path.
        """
        return self._temp_path

    #############################
    #  Generic Methods
    #############################

    @classmethod
    def from_dict(cls, obj: dict) -> Self:
        """
        Create object instance from a dictionary.

        Parameters
        ----------
        obj : dict
            Dictionary to create object from.

        Returns
        -------
        Self
            Self instance.
        """
        parsed_dict = cls._parse_dict(obj)
        obj_ = cls(**parsed_dict)
        obj_._local = obj_._context.local
        return obj_

    @staticmethod
    def _parse_dict(obj: dict) -> dict:
        """
        Parse dictionary.

        Parameters
        ----------
        obj : dict
            Dictionary to parse.

        Returns
        -------
        dict
            Parsed dictionary.
        """

        # Mandatory fields
        project = obj.get("project")
        name = obj.get("name")
        if project is None or name is None:
            raise EntityError("Project or name are not specified.")

        # Optional fields
        uuid = obj.get("id")
        kind = obj.get("kind")
        embedded = obj.get("embedded")

        # Build metadata and spec
        spec = obj.get("spec")
        spec = spec if spec is not None else {}
        spec = build_spec(kind=kind, **spec)
        metadata = obj.get("metadata", {"name": name})
        metadata = build_metadata(**metadata)

        return {
            "project": project,
            "name": name,
            "kind": kind,
            "uuid": uuid,
            "metadata": metadata,
            "spec": spec,
            "embedded": embedded,
        }

def artifact_from_parameters(
    project: str,
    name: str,
    description: str = "",
    kind: str = "artifact",
    key: str = None,
    src_path: str = None,
    target_path: str = None,
    local: bool = False,
    embedded: bool = False,
    uuid: str = None,
) -> Artifact:
    """
    Create artifact.

    Parameters
    ----------
    project : str
        Name of the project.
    name : str
        Identifier of the artifact.
    description : str
        Description of the artifact.
    kind : str
        The type of the artifact.
    key : str
        Representation of artfact like store://etc..
    src_path : str
        Path to the artifact on local file system or remote storage.
    targeth_path : str
        Destination path of the artifact.
    local : bool
        Flag to determine if object has local execution.
    embedded : bool
        Flag to determine if object must be embedded in project.
    uuid : str
        UUID.

    Returns
    -------
    Artifact
        Artifact object.
    """
    meta = build_metadata(name=name, description=description)
    spec = build_spec(kind, key=key, src_path=src_path, target_path=target_path)
    return Artifact(
        project=project,
        name=name,
        kind=kind,
        metadata=meta,
        spec=spec,
        local=local,
        embedded=embedded,
        uuid=uuid,
    )

def artifact_from_dict(obj: dict) -> Artifact:
    """
    Create artifact from dictionary.

    Parameters
    ----------
    obj : dict
        Dictionary to create artifact from.

    Returns
    -------
    Artifact
        Artifact object.
    """
    return Artifact.from_dict(obj)


import numpy as np
import matplotlib
import matplotlib.pyplot as plt

# random data
A = [2,5,7,9,11,16,19,23,22,29,29,35,37,40,46]
b = [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

# Visualize data
plt.plot(A,b,'ro')

# array to [[   ]]
# change row vector to column vector
A = np.array([A]).T
b = np.array([b]).T

# Create vector 1
ones = np.ones_like(A, dtype = np.int8)
A = np.concatenate((A,ones),axis = 1)

# Use formula
x = np.linalg.inv(A.transpose().dot(A)).dot(A.transpose()).dot(b)

x0 = np.array([1,46]).T
y0 = x[0][0]*x0 + x[1][0]
# ko co phep toan matrix cong mot so
# nhung trong numpy cong mot so voi tat ca cac phan tu cua matrix

# Test predict data
x_test = 12
y_test = x[0][0]*x_test + x[1][0]
print(y_test)

# Visualize x0,y0
plt.plot(x0,y0)
plt.show()


from collections import deque

vowels = deque(x for x in input().split())
consonants = [x for x in input().split()]

flowers = {
    "rose": [],
    "tulip": [],
    "lotus": [],
    "daffodil": []
}

def check_for_a_match():
    for word, found in flowers.items():
        if len(found) == len(word):
            return word

while vowels and consonants:
    current_vowel = vowels.popleft()
    current_consonant = consonants.pop()

    for flower in flowers.keys():
        if current_vowel in flower and current_vowel not in flowers[flower]:
            flowers[flower].extend(current_vowel * (flower.count(current_vowel)))
        if current_consonant in flower and current_consonant not in flowers[flower]:
            flowers[flower].extend(current_consonant * (flower.count(current_consonant)))

    result = check_for_a_match()
    if result:
        print(f"Word found: {result}")
        break

else:
    print("Cannot find any word!")

if vowels:
    print(f"Vowels left: {' '.join(vowels)}")
if consonants:
    print(f"Consonants left: {' '.join(consonants)}")


import os
import json
import random as rd
from copy import deepcopy

from matplotlib.pylab import *

import math
import torch
import torchvision.datasets as dsets
import torch.nn as nn
import torch.nn.functional as F
# import torch_xla
# import torch_xla.core.xla_model as xm

device = torch.device("cuda")

def encode(lstm, wemb_l, l, return_hidden=False, hc0=None, last_only=False):
    """ [batch_size, max token length, dim_emb]
    """
    bS, mL, eS = wemb_l.shape

    # sort before packking
    l = array(l)
    perm_idx = argsort(-l)
    perm_idx_inv = generate_perm_inv(perm_idx)

    # pack sequence

    packed_wemb_l = nn.utils.rnn.pack_padded_sequence(wemb_l[perm_idx, :, :],
                                                      l[perm_idx],
                                                      batch_first=True)

    # Time to encode
    if hc0 is not None:
        hc0 = (hc0[0][:, perm_idx], hc0[1][:, perm_idx])

    # ipdb.set_trace()
    packed_wemb_l = packed_wemb_l.float()  # I don't know why..
    packed_wenc, hc_out = lstm(packed_wemb_l, hc0)
    hout, cout = hc_out

    # unpack
    wenc, _l = nn.utils.rnn.pad_packed_sequence(packed_wenc, batch_first=True)

    if last_only:
        # Take only final outputs for each columns.
        wenc = wenc[tuple(range(bS)), l[perm_idx] - 1]  # [batch_size, dim_emb]
        wenc.unsqueeze_(1)  # [batch_size, 1, dim_emb]

    wenc = wenc[perm_idx_inv]

    if return_hidden:
        # hout.shape = [number_of_directoin * num_of_layer, seq_len(=batch size), dim * number_of_direction ] w/ batch_first.. w/o batch_first? I need to see.
        hout = hout[:, perm_idx_inv].to(device)
        cout = cout[:, perm_idx_inv].to(device)  # Is this correct operation?

        return wenc, hout, cout
    else:
        return wenc

def encode_hpu(lstm, wemb_hpu, l_hpu, l_hs):
    wenc_hpu, hout, cout = encode(lstm,
                                  wemb_hpu,
                                  l_hpu,
                                  return_hidden=True,
                                  hc0=None,
                                  last_only=True)

    wenc_hpu = wenc_hpu.squeeze(1)
    bS_hpu, mL_hpu, eS = wemb_hpu.shape
    hS = wenc_hpu.size(-1)

    wenc_hs = wenc_hpu.new_zeros(len(l_hs), max(l_hs), hS)
    wenc_hs = wenc_hs.to(device)

    # Re-pack according to batch.
    # ret = [B_NLq, max_len_headers_all, dim_lstm]
    st = 0
    for i, l_hs1 in enumerate(l_hs):
        wenc_hs[i, :l_hs1] = wenc_hpu[st:(st + l_hs1)]
        st += l_hs1

    return wenc_hs

def generate_perm_inv(perm):
    # Definitly correct.
    perm_inv = zeros(len(perm), dtype=int)  # Was an undefine int32 variable
    for i, p in enumerate(perm):
        perm_inv[int(p)] = i

    return perm_inv

def pred_sc(s_sc):
    """
    return: [ pr_wc1_i, pr_wc2_i, ...]
    """
    # get g_num
    pr_sc = []
    for s_sc1 in s_sc:
        pr_sc.append(s_sc1.argmax().item())

    return pr_sc

def pred_sc_beam(s_sc, beam_size):
    """
    return: [ pr_wc1_i, pr_wc2_i, ...]
    """
    # get g_num
    pr_sc_beam = []

    for s_sc1 in s_sc:
        val, idxes = s_sc1.topk(k=beam_size)
        pr_sc_beam.append(idxes.tolist())

    return pr_sc_beam

def pred_sa(s_sa):
    """
    return: [ pr_wc1_i, pr_wc2_i, ...]
    """
    # get g_num
    pr_sa = []
    for s_sa1 in s_sa:
        pr_sa.append(s_sa1.argmax().item())

    return pr_sa

def pred_wn(s_wn):
    """
    return: [ pr_wc1_i, pr_wc2_i, ...]
    """
    # get g_num
    pr_wn = []
    for s_wn1 in s_wn:
        pr_wn.append(s_wn1.argmax().item())
        # print(pr_wn, s_wn1)
        # if s_wn1.argmax().item() == 3:
        #     input('')

    return pr_wn

def pred_wc(wn, s_wc):
    """
    return: [ pr_wc1_i, pr_wc2_i, ...]
    ! Returned index is sorted!
    """
    # get g_num
    pr_wc = []
    for b, wn1 in enumerate(wn):
        s_wc1 = s_wc[b]

        pr_wc1 = argsort(-s_wc1.data.cpu().numpy())[:wn1]
        pr_wc1.sort()

        pr_wc.append(list(pr_wc1))
    return pr_wc

def pred_wo(wn, s_wo):
    """
    return: [ pr_wc1_i, pr_wc2_i, ...]
    """
    # s_wo = [B, 4, n_op]
    pr_wo_a = s_wo.argmax(dim=2)  # [B, 4]
    # get g_num
    pr_wo = []
    for b, pr_wo_a1 in enumerate(pr_wo_a):
        wn1 = wn[b]
        pr_wo.append(list(pr_wo_a1.data.cpu().numpy()[:wn1]))

    return pr_wo

def topk_multi_dim(tensor, n_topk=1, batch_exist=True):

    if batch_exist:
        idxs = []
        for b, tensor1 in enumerate(tensor):
            idxs1 = []
            tensor1_1d = tensor1.reshape(-1)
            values_1d, idxs_1d = tensor1_1d.topk(k=n_topk)
            idxs_list = unravel_index(idxs_1d.cpu().numpy(), tensor1.shape)
            # (dim0, dim1, dim2, ...)

            # reconstruct
            for i_beam in range(n_topk):
                idxs11 = []
                for idxs_list1 in idxs_list:
                    idxs11.append(idxs_list1[i_beam])
                idxs1.append(idxs11)
            idxs.append(idxs1)

    else:
        tensor1 = tensor
        idxs1 = []
        tensor1_1d = tensor1.reshape(-1)
        values_1d, idxs_1d = tensor1_1d.topk(k=n_topk)
        idxs_list = unravel_index(idxs_1d.numpy(), tensor1.shape)
        # (dim0, dim1, dim2, ...)

        # reconstruct
        for i_beam in range(n_topk):
            idxs11 = []
            for idxs_list1 in idxs_list:
                idxs11.append(idxs_list1[i_beam])
            idxs1.append(idxs11)
        idxs = idxs1
    return idxs

def remap_sc_idx(idxs, pr_sc_beam):
    for b, idxs1 in enumerate(idxs):
        for i_beam, idxs11 in enumerate(idxs1):
            sc_beam_idx = idxs[b][i_beam][0]
            sc_idx = pr_sc_beam[b][sc_beam_idx]
            idxs[b][i_beam][0] = sc_idx

    return idxs

def check_sc_sa_pairs(tb, pr_sc, pr_sa, ):
    """
    Check whether pr_sc, pr_sa are allowed pairs or not.
    agg_ops = ['', 'MAX', 'MIN', 'COUNT', 'SUM', 'AVG']
    """
    bS = len(pr_sc)
    check = [False] * bS
    for b, pr_sc1 in enumerate(pr_sc):
        pr_sa1 = pr_sa[b]
        hd_types1 = tb[b]['types']
        hd_types11 = hd_types1[pr_sc1]
        if hd_types11 == 'text':
            if pr_sa1 == 0 or pr_sa1 == 3:  # ''
                check[b] = True
            else:
                check[b] = False

        elif hd_types11 == 'real':
            check[b] = True
        else:
            raise Exception("New TYPE!!")

    return check

def pred_wvi_se_beam(max_wn, s_wv, beam_size):
    """
    s_wv: [B, 4, mL, 2]
    - predict best st-idx & ed-idx
    output:
    pr_wvi_beam = [B, max_wn, n_pairs, 2]. 2 means [st, ed].
    prob_wvi_beam = [B, max_wn, n_pairs]
    """
    bS = s_wv.shape[0]

    # [B, 4, mL, 2] -> [B, 4, mL, 1], [B, 4, mL, 1]
    s_wv_st, s_wv_ed = s_wv.split(1, dim=3)

    s_wv_st = s_wv_st.squeeze(3)  # [B, 4, mL, 1] -> [B, 4, mL]
    s_wv_ed = s_wv_ed.squeeze(3)

    prob_wv_st = F.softmax(s_wv_st, dim=-1).detach().to('cpu').numpy()
    prob_wv_ed = F.softmax(s_wv_ed, dim=-1).detach().to('cpu').numpy()

    k_logit = int(ceil(sqrt(beam_size)))
    n_pairs = k_logit**2
    assert n_pairs >= beam_size
    values_st, idxs_st = s_wv_st.topk(k_logit)  # [B, 4, mL] -> [B, 4, k_logit]
    values_ed, idxs_ed = s_wv_ed.topk(k_logit)  # [B, 4, mL] -> [B, 4, k_logit]

    # idxs = [B, k_logit, 2]
    # Generate all possible combination of st, ed indices & prob
    pr_wvi_beam = []  # [B, max_wn, k_logit**2 [st, ed] paris]
    prob_wvi_beam = zeros([bS, max_wn, n_pairs])
    for b in range(bS):
        pr_wvi_beam1 = []

        idxs_st1 = idxs_st[b]
        idxs_ed1 = idxs_ed[b]
        for i_wn in range(max_wn):
            idxs_st11 = idxs_st1[i_wn]
            idxs_ed11 = idxs_ed1[i_wn]

            pr_wvi_beam11 = []
            pair_idx = -1
            for i_k in range(k_logit):
                for j_k in range(k_logit):
                    pair_idx += 1
                    st = idxs_st11[i_k].item()
                    ed = idxs_ed11[j_k].item()
                    pr_wvi_beam11.append([st, ed])

                    p1 = prob_wv_st[b, i_wn, st]
                    p2 = prob_wv_ed[b, i_wn, ed]
                    prob_wvi_beam[b, i_wn, pair_idx] = p1*p2
            pr_wvi_beam1.append(pr_wvi_beam11)
        pr_wvi_beam.append(pr_wvi_beam1)

    # prob

    return pr_wvi_beam, prob_wvi_beam

def convert_pr_wvi_to_string(pr_wvi, nlu_t, nlu_wp_t, wp_to_wh_index, nlu):
    """
    - Convert to the string in whilte-space-separated tokens
    - Add-hoc addition.
    """
    pr_wv_str_wp = []  # word-piece version
    pr_wv_str = []
    for b, pr_wvi1 in enumerate(pr_wvi):
        pr_wv_str_wp1 = []
        pr_wv_str1 = []
        wp_to_wh_index1 = wp_to_wh_index[b]
        nlu_wp_t1 = nlu_wp_t[b]
        nlu_t1 = nlu_t[b]

        for i_wn, pr_wvi11 in enumerate(pr_wvi1):
            st_idx, ed_idx = pr_wvi11

            # Ad-hoc modification of ed_idx to deal with wp-tokenization effect.
            # e.g.) to convert "butler cc (" ->"butler cc (ks)" (dev set 1st question).
            pr_wv_str_wp11 = nlu_wp_t1[st_idx:ed_idx+1]
            pr_wv_str_wp1.append(pr_wv_str_wp11)

            st_wh_idx = wp_to_wh_index1[st_idx]
            ed_wh_idx = wp_to_wh_index1[ed_idx]
            pr_wv_str11 = nlu_t1[st_wh_idx:ed_wh_idx+1]

            pr_wv_str1.append(pr_wv_str11)

        pr_wv_str_wp.append(pr_wv_str_wp1)
        pr_wv_str.append(pr_wv_str1)

    return pr_wv_str, pr_wv_str_wp

def merge_wv_t1_eng(where_str_tokens, NLq):
    """
    Almost copied of SQLNet.
    The main purpose is pad blank line while combining tokens.
    """
    nlq = NLq.lower()
    where_str_tokens = [tok.lower() for tok in where_str_tokens]
    alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789$'
    special = {'-LRB-': '(',
               '-RRB-': ')',
               '-LSB-': '[',
               '-RSB-': ']',
               '``': '"',
               '\'\'': '"',
               }
    # '--': '\u2013'} # this generate error for test 5661 case.
    ret = ''
    double_quote_appear = 0
    for raw_w_token in where_str_tokens:
        # if '' (empty string) of None, continue
        if not raw_w_token:
            continue

        # Change the special characters
        # maybe necessary for some case?
        w_token = special.get(raw_w_token, raw_w_token)

        # check the double quote
        if w_token == '"':
            double_quote_appear = 1 - double_quote_appear

        # Check whether ret is empty. ret is selected where condition.
        if len(ret) == 0:
            pass
        # Check blank character.
        elif len(ret) > 0 and ret + ' ' + w_token in nlq:
            # Pad ' ' if ret + ' ' is part of nlq.
            ret = ret + ' '

        elif len(ret) > 0 and ret + w_token in nlq:
            pass  # already in good form. Later, ret + w_token will performed.

        # Below for unnatural question I guess. Is it likely to appear?
        elif w_token == '"':
            if double_quote_appear:
                ret = ret + ' '  # pad blank line between next token when " because in this case, it is of closing apperas
                # for the case of opening, no blank line.

        elif w_token[0] not in alphabet:
            pass  # non alphabet one does not pad blank line.

        # when previous character is the special case.
        elif (ret[-1] not in ['(', '/', '\u2013', '#', '$', '&']) and (ret[-1] != '"' or not double_quote_appear):
            ret = ret + ' '
        ret = ret + w_token

    return ret.strip()


#LIBRERIAS A UTILIZAR
import pandas as pd
from sklearn.impute import SimpleImputer
import numpy as np
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
import seaborn as seabornInstance
from sklearn.model_selection import train_test_split #libreria para poder separar los datos entre entrenamiento y test
from sklearn.linear_model import LinearRegression #libreria para poder generar la regresion lineal
from sklearn import metrics
#ANALISIS PREVIO
"""
En el analisis previo con solo mirar el archivo, puedo deducir que es un archivo que contiene
edad de una persona, sexo de la persona solicitante del seguro,bmi(segun lo que googlie es un reembolso que se le puede dar),
cuantos hijos tiene, si es fumador o no, y el precio del seguro a abonar.
De esto puedo inferir que mi prediccion va a ser con una regresion lineal multiple ya que la variable dependiete es el cargo del seguro, y es afectada
por todas las demas columnas, es decir, un seguro aumentara por todas las condiciones anteriores(edad, bmi, hijos, si es fumador o no y de que zona es la persona solicitante)
"""

#-------------GUARDO MI ARCHIVO A USAR -------------------------------
ruta =r'Unsam.Clase.12.4.6.2021\Unsam.Clase.12.4.6.2021\cvs_para_el_TP\seguro.csv'
dataSet = pd.read_csv(ruta) #archivo a utilizar

#-----------------ANALISIS DE MIS DATOS ---------------------
print(dataSet)
#print("CANTIDAD DE FILAS Y COLUMNAS")
#print(dataSet.shape)
#no hace falta hacer el shape ya que al imprimir el dataSet, me dice cuantas filas y columnas tengo
print(dataSet.describe())

#compruebo si hay valores nan en mi codigo
print("Valores null o nan en el dataSet: ")
print(dataSet.isna().values.any())
print(dataSet.isnull().values.any())
#devuelve falso por ende no tengo valores NaN o null

#-------------SEPARO LAS VARIABLES QUE VOY A USAR------------
X = dataSet[['age','sex','bmi','children','smoker','region' ]].values
Y = dataSet['charges'].values

print("X:\n")
print(X) #matriz     /variables independientes
print("Y:\n")
print(Y) #vector    /variables dependientes

#----------------COMIENZO A PREPARAR LOS DATOS-------------
"""
La edad y el bmi, son valores numericos ya manipulables , por ende , los dejo como estan para poder realizar mi analisis
"""

#MODIFICO LA COLUMNA DEL SEXO PARA MANEJARME CON 1 Y 0
labelencoder_X=LabelEncoder()
X[:,1]=labelencoder_X.fit_transform(X[:,1])

#MODIFICO LA COMLUNMA DE SI ES FUMADOR CON 1 Y 0
labelencoder_X=LabelEncoder()
X[:,4]=labelencoder_X.fit_transform(X[:,4])

#MODIFICO LA COLUMNNA DE SUR Y NORTE (CON ORIENTACION ESTE Y OESTE)
labelencoder_X=LabelEncoder()
X[:,-1]=labelencoder_X.fit_transform(X[:,-1])
print("Matriz X luego de preparacion de datos : \n")
print(X)

#PREGUNTAR SI HAY QUE ESCALAR LA EDAD?

#-------------GRAFICO MI VARIABLE DEPENDIENTE PARA SACAR ALGUNA CONCLUSION -----------

plt.figure(figsize=(10,5))
plt.tight_layout()
plt.title('Densidad de mi variable Dependiente')
seabornInstance.distplot(dataSet['charges'], color = 'lightblue')
plt.show()
"""
De este Grafico podemos concluir que mi variable dependiente 'charges' que seria el precio a pagar del seguro,
varia entre 0 y 70000 , y la mayor densidad(mayor cantidad de match) las hace desde el 0 hasta el 10000 aproximadamente.
"""

#------------------DIVIDIR LOS DATOS EN ENTRENAMIENTO Y TEST -----------------
#80 porciento de mis datos en entrenamiento y 20 en test
X_train , X_test, Y_train, Y_test = train_test_split(X , Y , test_size = 0.2, random_state=0)
#comienzo a entrenar mi modelo
regressor = LinearRegression()
regressor.fit(X_train, Y_train)

#------------------ELIMINO MI COLUMNA QUE ES LA DE CARGOS(VARIABLE DEPENDIENTE)-----------
dataFrame= dataSet.drop(['charges'], axis = 1) #dropeo(elimino) mi variale dependiente de mi data frame
dataFrame = dataFrame.T #transpongo filas por columnas PODRIA SER TRANSPOSE()
dataFrame = dataFrame.index #me guardo en mi dataframe solo las etiquetas
print(dataFrame) #imprimo las etiquetas para verificar que estan bien guardadas

#-----------------ENCONTRAR LOS COEFICIENTES MAS OPTIMOS PARA MIS ATRIBUTOS----------------

coeficiente_dataFrame = pd.DataFrame(regressor.coef_, dataFrame, columns= ['Coeficiente'])
print(coeficiente_dataFrame)
 #luego de imprimir concluyo que transpuso filas por columnas y tiene sus coeficentes correspondientes

#---------------PREDICCION--------------------

y_prediccion = regressor.predict(X_test)
#agarro mi dataFrame donde estan mis indices
dataFrame = pd.DataFrame({'Actual': Y_test, 'Prediccion': y_prediccion})
dataFrame_prediccion = dataFrame.head(30)
print(dataFrame_prediccion)

#-------------------GRAFICO MI PREDICCION -----------------

dataFrame_prediccion.plot(kind='bar',figsize=(10,8)) #LE DIGO QUE ES UN GRAFICO DE BARRAS Y EL TAMAÑO
plt.title('Actual vs Prediccion')
plt.grid(which='major', linestyle='-', linewidth='0.5', color='black')
plt.grid(which='minor', linestyle=':', linewidth='0.5', color='black')
plt.show()

#----------------ME FIJO EL RENDIMIENTO DE MI ALGORITMO ---------------

print('Promedio de Error Absoluto:', metrics.mean_absolute_error(Y_test, y_prediccion))
print('Promedio de Error de Raiz:', metrics.mean_squared_error(Y_test, y_prediccion))
print('Error cuadratico medio de la raiz:', np.sqrt(metrics.mean_squared_error(Y_test, y_prediccion)))

#CONCLUSION FINAL
"""
Por el analisis hecho, podemos deducir que la prediccion no es nada buena, ya que los promedios de error
superan ampliamente el 10 porciento, es aproximadamente, un 15 porciento (siendo generoso), no es un analisis muy exacto, pero para nuestro caso
es aceptable(porque use regresion lineal multiple, entonces entre mas datos afecten a mi variable dependiente , menos me voy a acercar a una prediccion acertada),
ya que las columnas en las que supera ese porcentaje son la 14 , la 20 y la 23, que siendo generoso tendrian aproximadamente un 25 porciento extra.
"""

"""
PD: Perdon por mi manera de hablar o escribir pero la verdad que en las conclusiones tarde mas que en el codigo jajaja.
    Si hay algo que deba cambiar o mejorar porfavor diganmelo
Muchas gracias por TODO
Atte : Alex Sosa :)
"""


import streamlit as st
import pandas as pd
import plotly.express as px
import seaborn as sns

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

st.set_option('deprecation.showPyplotGlobalUse', False)
# Loading dataset
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data',
                names=['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class'])

st.title('Exploratory Data Analysis of the Iris Dataset')
st.header('This app allows you to explore the Iris dataset and visualize the data using various plots.')

st.subheader("DataFrame")
st.dataframe(df)
selected_column = st.sidebar.selectbox('Select a column to visualize', df.columns)

st.write("Histogram Plots")
sns.histplot(df[selected_column])
st.pyplot()

st.write("Scatter plot")
x_axis = st.sidebar.selectbox('Select the x-axis', df.columns)
y_axis = st.sidebar.selectbox('Select the y-axis', df.columns)

fig = px.scatter(df, x=x_axis, y=y_axis)
st.plotly_chart(fig)

st.write("Pair Plot")
sns.pairplot(df, hue='class')
st.pyplot()
st.write("Description of the data")
st.table(df.describe())

st.header('Correlation Matrix')

corr = df.corr()
sns.heatmap(corr, annot=True)
st.pyplot()

st.header('Boxplot')

fig = px.box(df, y=selected_column)
st.plotly_chart(fig)

selected_class = st.sidebar.selectbox('Select a class to visualize', df['class'].unique())

if st.sidebar.button('Show Violin Plot'):
    fig = px.violin(df[df['class'] == selected_class], y=selected_column)
    st.plotly_chart(fig)


#!/usr/bin/python
# coding: utf-8

import numpy as np
import cv2
import csv
import os
import shutil
import shutil
import logging

def to_image_string(image_filepath):
    return open(image_filepath, "rb").read().encode("base64")

def from_base64(base64_data):
    nparr = np.fromstring(base64_data.decode("base64"), np.uint8)
    return cv2.imdecode(nparr, cv2.IMREAD_ANYCOLOR)

# clean all non-alphanumberic characters
def strip(string):
    words = string.split()
    words = [word for word in words if "#" not in word]
    string = " ".join(words)
    clean = ""
    for c in string:
        if str.isalnum(c) or (c in [" ", ".", ","]):
            clean += c
    return clean

# creating CSV header
def create_csv(filename):
    with open(filename, "w+", newline="", encoding="utf-8") as save_file:
        writer = csv.writer(save_file)
        writer.writerow(["Author", "uTime", "Text"])

def write_to_csv(filename, data):
    with open(filename, "a+", newline="", encoding="utf-8") as save_file:
        writer = csv.writer(save_file)
        writer.writerow(data)

def empty_folder(folder):
    for filename in os.listdir(folder):
        file_path = os.path.join(folder, filename)
        try:
            if os.path.isfile(file_path) or os.path.islink(file_path):
                os.unlink(file_path)
            elif os.path.isdir(file_path):
                empty_folder(file_path)
        except Exception as e:
            logging.info("Failed to delete %s. Reason: %s" % (file_path, e))

def move_file(source, dest):
    shutil.move(source, dest)


# -*- coding: utf-8 -*-
import scrapy

from collections import OrderedDict

class BriefingEarningsSpider(scrapy.Spider):
    name = 'briefing_earnings'
    allowed_domains = ['www.briefing.com']
    start_urls = ['https://www.briefing.com/Inv/content/Auth/Calendar/Earnings/week1.htm']  # Current week (week1)

    def parse(self, response):
        dates_lst = response.xpath('//*[@class="calDATE"]/text()').getall()    # Get list of days (dates)
        dates = {dates_lst[day]: day for day in range(len(dates_lst))}
        dates_sort = OrderedDict(sorted(dates.items(), key=lambda x: x[1]))     # Ordered dict to save que

        for i, day in enumerate(dates_sort):
            block = response.xpath('//*[@class="calDATE"]/following-sibling::ul')[i]    # Block for day
            events_lst = block.xpath('.//div[contains(@class,"calEVENT")]')             # Block for ticket

            tickets = OrderedDict()

            for event in events_lst:
                ticket = event.xpath('.//span/@data-ticker-search').get()
                name = event.xpath('.//strong/text()').get()
                surprise_value = event.xpath(
                    './/span[contains(text(), "Surprise:")]/following-sibling::span/text()').get()
                act_value = event.xpath('.//span[contains(text(), "Act:")]/following-sibling::span/text()').get()
                cons_value = event.xpath('.//span[contains(text(), "Cons:")]/following-sibling::span/text()').get()

                tickets.update({ticket: {'name': name,
                                         'surprise_value': surprise_value,
                                         'actual_value': act_value,
                                         'consensus_value': cons_value}})

            dates_sort.update({day: tickets})  # Add all tickets with values for day

        yield dates_sort