import arcade

import enemyController
from component import Component
from eventManager import EventManager
from soundManager import SoundManager
from spriteRenderer import SpriteRenderer
from transform import Transform
from entity import Entity
from levelSectionLoader import LevelSectionLoader

# A component that handles input for a player
from gameManager import GameManager

# Amount the player is knocked back when they attack
KNOCK_BACK_ATTACK = 350

# Amount the player is knocked back when they take damage
KNOCK_BACK_DAMAGE = 2000

# Timer for attack animation taking precedence over walking animation
ATTACK_TIME = 0.1
ATTACK_COOL_DOWN = 0.2
ATTACK_ANIMATION = 0.25

# Constants for animation
RIGHT_FACING = 1
LEFT_FACING = 0
IDLE_TIME = 3

# DEFAULT SPEED VALUES
GRAVITY = -2000
MAX_SPEED = 500
MAX_JUMP = 2500

class PlayerController(Component):
    # Called when a key is pressed
    def on_key_press(self, key, modifiers):
        self.__keys_pressed[key] = True
        self.__idle = False
        self.__idle_timer = IDLE_TIME

        # Jump
        if key == arcade.key.W:
            self.__jump_requested = True
            self.__jump_timer = self.__jump_buffer_time
            self.__touching_floor = False
            self.current_texture = 0

        # Toggle debug
        if key == arcade.key.F:
            print("Toggling debug mode")
            GameManager.debug = not GameManager.debug

        # Pause/quit
        if key == arcade.key.ESCAPE:
            arcade.exit()

        # Attacking
        if key == arcade.key.SPACE:
            self.__is_attacking = True
            self.current_texture = 0

    # Change the value of the key_pressed dictionary when a key is released
    def on_key_release(self, key, modifiers):
        self.__keys_pressed[key] = False
        self.__idle_timer = IDLE_TIME
        self.__idle = False

    # Called every time physics get updated (currently every frame)
    # Deals with all player movement and collision
    def on_physics_update(self, dt):
        # Round velocity to 0 if we're close enough
        epsilon = 0.001
        if abs(self.__velocity[0]) < epsilon:
            self.__velocity = (0, self.__velocity[1])
        if abs(self.__velocity[1]) < epsilon:
            self.__velocity = (self.__velocity[0], 0)

        # Update timers
        self.__coyote_timer -= dt
        self.__jump_timer -= dt
        self.__invincibility_timer -= dt
        self.__attack_time -= dt
        self.__attack_timer -= dt
        self.__health_up_timer -= dt
        self.__jumping_timer -= dt
        self.__speed_timer -= dt
        self.__mad_timer -= dt
        self.__attack_animation_timer -= dt
        self.__idle_timer -= dt

        # If the player is in the air, determine if they've landed on the ground
        # If the player is on the ground, determine if they've jumped (or moved up/down a slope)
        # In both cases, determine if they've collided with a wall

        # On-the-ground case
        # Deal with whether the player should fall (they've stepped off the platform) and jumps
        if self.__touching_ground:
            # Give player slight downward movement so that the downward case below is run
            self.__velocity = (self.__velocity[0], -0.1)
            self.__touching_ground = False
            self.__coyote_timer = self.__coyote_time  # Start coyote timer

        if self.__transform.position[1] == 0:
            self.__touching_floor = True

        # Get some stuff we'll need
        all_colliders = GameManager.get_colliders()  # List of all colliders in scene
        player_collision_polygon = self.__collider.polygon  # Polygon of the player
        # CAN'T GET THIS OBJECT IN on_created() (CIRCULAR REFERENCE) (Need to create player first in screenView.setup())
        self.__sword_sprite_polygon = GameManager.get_entities_by_name("Sword")[0].get_component_by_name(
            "Collider").polygon

        # Disallow the player from moving past the edge of the screen
        if not self.__freeze_camera:
            if self.__transform.position[0] <= self.__camera_min + 50:
                self.__transform.position = (self.__camera_min + 51, self.__transform.position[1])
        else:
            if self.__transform.position[0] <= 100:
                self.__transform.position = (101, self.__transform.position[1])
            if self.__transform.position[0] >= 1820:
                self.__transform.position = (1819, self.__transform.position[1])

        # Player dies if they fall off the level edge
        if self.__transform.position[1] < -5:
            self.__health = self.__health - 1
            self.__invincibility_timer = 1.0
            SoundManager.play_sound("player", "damage")
            self.__taking_damage = True
            self.__velocity = (self.__velocity[0] * 490 / 500, self.__velocity[1])
            self.__is_falling = True
            self.__transform.position = (self.__camera_min + 50, 1500)

        # For when the player is attacking
        if self.__is_attacking and self.__attack_timer < 0:
            SoundManager.play_sound("player", "attack")
            # SoundManager.test_play_sound("assets/sounds/music/main_stage_music.mp3") """Used to test if sounds will stop playing on death"""
            self.__attack_time = ATTACK_TIME
            self.__attack_timer = ATTACK_COOL_DOWN
            self.__attack_animation_timer = ATTACK_ANIMATION
            self.__attack_animation = True
            self.__is_attacking = False
            # implement attacking knock back
            if self.__velocity[1] == 0:
                if (self.__velocity[0] >= KNOCK_BACK_ATTACK):
                    self.__velocity = (self.__velocity[0] - KNOCK_BACK_ATTACK, self.__velocity[1])
                elif self.__velocity[0] >= 0:
                    self.__velocity = (
                    self.__velocity[0] - (KNOCK_BACK_ATTACK - (self.__velocity[0] / 2)), self.__velocity[1])
                elif (self.__velocity[0] <= (-1 * KNOCK_BACK_ATTACK)):
                    self.__velocity = (self.__velocity[0] + KNOCK_BACK_ATTACK, self.__velocity[1])
                elif self.__velocity[0] < 0:
                    self.__velocity = (
                    self.__velocity[0] + (KNOCK_BACK_ATTACK - self.__velocity[0] / 2), self.__velocity[1])

            else:
                if (self.__velocity[0] >= KNOCK_BACK_ATTACK):
                    self.__velocity = (self.__velocity[0] - KNOCK_BACK_ATTACK / 2, self.__velocity[1])
                elif self.__velocity[0] >= 0:
                    self.__velocity = (
                    self.__velocity[0] - (KNOCK_BACK_ATTACK / 2 - (self.__velocity[0] / 2)), self.__velocity[1])
                elif (self.__velocity[0] <= (-1 * KNOCK_BACK_ATTACK)):
                    self.__velocity = (self.__velocity[0] + KNOCK_BACK_ATTACK / 2, self.__velocity[1])
                elif self.__velocity[0] < 0:
                    self.__velocity = (
                    self.__velocity[0] + (KNOCK_BACK_ATTACK / 2 - self.__velocity[0] / 2), self.__velocity[1])

            for collider in all_colliders:
                if collider.parent is self:
                    continue
                if "Enemy" in collider.parent.tags:
                    if arcade.are_polygons_intersecting(self.__sword_sprite_polygon, collider.polygon):
                        collider.parent.get_component_by_name("EnemyController").take_damage(self.__attack_power)
                elif "Boss" in collider.parent.tags:
                    if arcade.are_polygons_intersecting(self.__sword_sprite_polygon, collider.polygon):
                        collider.parent.get_component_by_name("BossController").take_damage(self.__attack_power)

        if self.__taking_damage and not self.__is_falling:
            # implement damage knock back
            if self.__velocity[0] >= 0:
                self.__velocity = (self.__velocity[0] - KNOCK_BACK_DAMAGE, self.__velocity[1])
            else:
                self.__velocity = (self.__velocity[0] + KNOCK_BACK_DAMAGE, self.__velocity[1])

        # For when the player is taking damage
        self.__taking_damage = False
        for collider in all_colliders:
            # Ignore the player's own collider
            if collider.parent is self:
                continue
            if "Enemy" in collider.parent.tags or "Boss" in collider.parent.tags:
                if arcade.are_polygons_intersecting(player_collision_polygon, collider.polygon):
                    if self.__invincibility_timer < 0:
                        if "Enemy" in collider.parent.tags:
                            self.__invincibility_timer = 1.0
                            self.__health = self.__health - 1
                            SoundManager.play_sound("player", "damage")
                            self.__taking_damage = True
                            self.__velocity = (self.__velocity[0] * 490 / 500, self.__velocity[1])
                        else:
                            if not collider.parent.get_component_by_name("BossController").dead:
                                damage_taken = collider.parent.get_component_by_name(
                                    "BossController").get_attack_power()
                                self.__invincibility_timer = 1.0
                                self.__health = self.__health - damage_taken
                                SoundManager.play_sound("player", "damage")
                                self.__taking_damage = True
                                self.__velocity = (self.__velocity[0] * 490 / 500, self.__velocity[1])

        if self.__invincibility_timer > 0:
            self.__sprite_renderer.sprite.color = (255, 100, 100)
        else:
            self.__is_falling = False

        if self.__invincibility_timer <= 0 and not self.__health_up and not self.__speed and not self.__jump and not self.__mad:
            self.__sprite_renderer.sprite.color = (255, 255, 255)

        if self.__attack_animation_timer < 0:
            self.__attack_animation = False

        # Two cases here: moving up and moving down
        # Both have different physics and check for different things
        # Deal with vertical movement first, same for both cases (collision checking comes later)
        self.__velocity = (arcade.clamp(self.__velocity[0], -self.__max_velocity[0], self.__max_velocity[0]),
                           arcade.clamp(self.__velocity[1], -self.__max_velocity[1], self.__max_velocity[1]))
        self.__transform.move((0, self.__velocity[1] * dt))

        # First case: moving up
        # Check for ground collision (with ceilings)
        # Deal with vertical deceleration
        if self.__velocity[1] > 0:
            # Ground collision (ceilings)
            largest_difference = float("inf")
            for collider in all_colliders:
                # Ignore the player's own collider
                if collider.parent is self:
                    continue
                # Ignore colliders that we're above (avoid colliding with floor we just left)
                # TODO: THIS IS A BODGE
                if self.__transform.position[1] > collider.transform.position[1]:
                    continue
                # Only checking for objects tagged with "Ground" (solid objects, not platforms)
                if "Ground" in collider.parent.tags:
                    if arcade.are_polygons_intersecting(player_collision_polygon, collider.polygon):
                        # Get how far we would have to move the player to get them out of the ceiling
                        difference = (collider.transform.position[1] - collider.height / 2) - (
                                self.__transform.position[1] + self.__collider.height / 2)
                        if difference < largest_difference:  # Largest negative amount
                            largest_difference = difference
                        # Stop the player's vertical movement
                        self.__velocity = (self.__velocity[0], 0)
            # Move the player out of the ceiling if they're in it
            if largest_difference != float("inf"):
                self.__transform.move((0, largest_difference))

            # Deal with vertical deceleration
            self.__velocity = (self.__velocity[0], self.__velocity[1] + (self.__gravity * dt))

        # Second case: moving down
        # Check for collision with ground and platforms
        # Deal with vertical acceleration
        else:
            # Ground collision (floors)
            largest_difference = float("-inf")
            for collider in all_colliders:
                # Ignore the player's own collider
                if collider.parent is self:
                    continue

                if "PowerUp" in collider.parent.tags:
                    if arcade.are_polygons_intersecting(player_collision_polygon, collider.polygon):
                        temp = collider.parent.on_collection()
                        if temp == "Health-Up":
                            self.__health_up_timer = 0.1
                            self.__sprite_renderer.sprite.color = arcade.color.PINK
                            self.__health_up = True

                        if temp == "Speed":
                            self.__sprite_renderer.sprite.color = (100, 100, 255)
                            self.__speed = True
                            self.__speed_timer = 5
                            # CHANGE
                            self.__max_velocity = (self.__max_velocity[0] + 350, self.__max_velocity[1] + 350)
                            self.__velocity = (self.__velocity[0] + 100, self.__velocity[1] + 100)

                            # Make player speed

                        if temp == "Jump":
                            self.__jumping_timer = 7
                            self.__jump = True
                            self.__jumping_timer = 7
                            self.__sprite_renderer.sprite.color = (100, 255, 100)
                            # CHANGE
                            self.__gravity = -1000
                            self.__max_velocity = (self.__max_velocity[0], self.__max_velocity[1] + 100)
                            self.__velocity = (self.__velocity[0], self.__velocity[1] + 100)
                            # make player jump high

                        if temp == "Attack":
                            self.__mad = True
                            self.__mad_timer = 5
                            self.__attack_power = 10

                # Only checking for objects tagged with "Ground" or "Platform" (solid objects and platforms)
                if "Ground" in collider.parent.tags or (
                        "Platform" in collider.parent.tags and self.__transform.position[1] > (
                        collider.transform.position[1] + collider.height / 4)):
                    if arcade.are_polygons_intersecting(player_collision_polygon, collider.polygon):
                        # Get how far we would have to move the player to get them out of the floor
                        difference = (collider.transform.position[1] + collider.height / 2) - \
                                     (self.__transform.position[1] - self.__collider.height / 2)

                        if difference > largest_difference:
                            largest_difference = difference
                        # Stop the player's vertical movement
                        self.__velocity = (self.__velocity[0], 0)
                        # Set the touching_ground flag
                        self.__touching_ground = True
                        self.__touching_floor = True

            # Move the player out of the floor if they're in it
            if largest_difference != float("-inf"):
                self.__transform.move((0, largest_difference))

            # Deal with vertical acceleration
            # Multiply vertical acceleration by a multiplier to get faster falling (less "floaty")
            self.__velocity = (
                self.__velocity[0], self.__velocity[1] + (self.__gravity * self.__falling_speed_multiplier * dt))

        # If we're on the ground (or in coyote time) and the player has pressed the jump button, let them jump
        if (self.__jump_requested or self.__jump_timer > 0) and (self.__touching_ground or self.__coyote_timer > 0):
            self.__velocity = (self.__velocity[0], self.__jump_speed)
            SoundManager.play_sound("player", "jump")
            # print(self.__velocity)
            self.__jump_requested = False
            self.__jump_timer = 0
            self.__touching_ground = False
            self.__touching_floor = False

        # Move the character based on which keys are pressed
        # Deal with wall collision
        # First, move the character horizontally

        self.__transform.move((self.__velocity[0] * dt, 0))
        if self.__keys_pressed[arcade.key.D]:
            self.__moving_left = False
            self.__holding_left = False
            self.__holding_right = True
            if self.__velocity[0] > 0:  # Moving right (accelerating)
                self.__velocity = (self.__velocity[0] + self.__horizontal_acceleration * dt, self.__velocity[1])
            else:  # Moving left (decelerating)
                self.__velocity = (
                self.__velocity[0] + self.__horizontal_turnaround_acceleration * dt, self.__velocity[1])
        elif self.__keys_pressed[arcade.key.A]:
            self.__moving_left = True
            self.__holding_left = True
            self.__holding_right = False
            if self.__velocity[0] > 0:  # Moving right (decelerating)
                self.__velocity = (
                self.__velocity[0] - self.__horizontal_turnaround_acceleration * dt, self.__velocity[1])
            else:  # Moving left (accelerating)
                self.__velocity = (self.__velocity[0] - self.__horizontal_acceleration * dt, self.__velocity[1])
        else:  # no keys pressed, default deceleration
            self.__velocity = (
            self.__velocity[0] * (1 - self.__horizontal_deceleration_multiplier * dt), self.__velocity[1])

        self.__jump_requested = False

        if self.__idle_timer < 0:
            self.__idle = True

        self.power_up_effects()

    # Gets called every frame
    # dt is the time taken since the last frame
    def on_update(self, dt):
        # Camera Movement Control:
        # 1) Camera does not move vertically
        # 2) Once the character reaches halfway across the screen, the camera moves forward with them again
        # 3) Camera never moves backwards

        # Can't just get them here again because that crashes for some reason
        width, height = self.__screen_width, self.__screen_height

        if self.__transform.position[0] > (self.__camera_min + width / 2):
            loader = self.get_level_section_loader()
            if loader.sections_gone_through < loader.sections_before_tower or \
                    self.get_transform_x() < (loader.current_offset + loader.tower_section[1] - width / 2):
                # if self.__transform.position[0] < 1920 * 9 - width / 2:
                self.__camera_min = self.__camera_min + (self.__transform.position[0] - (self.__camera_min + width / 2))

        if not self.__freeze_camera:
            GameManager.main_camera.move_to((self.__camera_min, 0), 5 * dt)

        # Animation update
        self.update_animation()

        # Health
        self.set_gui()

    def __init__(self):
        super().__init__("PlayerController")
        # Initialize the keys_pressed dictionary
        self.__keys_pressed = {
            arcade.key.A: False,
            arcade.key.D: False
        }

        # Get the screen width and height just once when the game starts
        self.__screen_width, self.__screen_height = arcade.window_commands.get_display_size()
        print("Screen resolution: " + str(self.__screen_width) + "x" + str(self.__screen_height))

        # Store some components here (set when the program starts), so we don't have to look for them each time
        self.__transform = None
        self.__collider = None
        self.__sprite_renderer = None
        self.__sword_sprite_polygon = None
        self.__level_section_loader = None

        # Private variables for player health
        self.__health = 6
        self.__max_health = 6
        self.__taking_damage = False
        self.__invincibility_timer = 0.0

        # Private variable for player attacking
        self.__is_attacking = False
        self.__attack_time = ATTACK_TIME
        self.__attack_timer = ATTACK_COOL_DOWN
        self.__attack_power = 1

        # Variable used for camera movement, stores the left most edge of the visable screen
        self.__camera_min = 0
        self.__freeze_camera = False

        # Variable used for sword positioning, stores the direction the player is facing
        self.__moving_left = False

        # Private variables for player movement
        self.__touching_ground = False
        self.__touching_floor = False  # this is a variable just for animation
        self.__jump_requested = False
        self.__velocity = (0, 0)
        self.__gravity = -2000
        self.__jump_speed = 1300
        self.__max_velocity = (500, 2500)
        self.__falling_speed_multiplier = 1.75  # Fall faster than you go up (makes jumps feel better)
        self.__coyote_time = 0.1  # Period after walking off a platform where you can still jump (another QOL feature)
        self.__coyote_timer = 0  # Temporary variable to keep track of the coyote time
        self.__is_falling = False  # variable changes to true once you fall off an edge and false once you respawn

        # Pressing jump within this amount of time before touching the ground
        # will make you jump when you land, avoiding missed inputs.
        self.__jump_buffer_time = 0.1
        self.__jump_timer = 0

        # Variables for the powerups
        self.__health_up = False
        self.__speed = False
        self.__jump = False
        self.__mad = False

        self.__power_up_type = None
        self.__health_up_timer = 0.5
        self.__speed_timer = 7
        self.__jumping_timer = 7
        self.__mad_timer = 5

        self.__flash_count = 0

        self.__horizontal_acceleration = 700  # How quickly you accelerate when moving sideways
        self.__horizontal_deceleration_multiplier = 10  # How quickly you decelerate when no button is pressed
        self.__horizontal_turnaround_acceleration = 4000  # How quickly you decelerate when changing direction

        self.__exit_sprite = arcade.Sprite("assets/sprites/exit.png", 1.0)

        # Animation
        # I USED THE FOLLOWING LINK AS REFERENCE: https://api.arcade.academy/en/latest/examples/platform_tutorial/step_12.html#platformer-part-twelve
        self.character_face_direction = RIGHT_FACING
        self.current_texture = 0

        self.__holding_left = False
        self.__holding_right = False

        self.__attack_animation_timer = 0
        self.__attack_animation = False

        self.__idle = True
        self.__idle_timer = IDLE_TIME

        idle_1 = (arcade.load_texture("assets/sprites/player/player_idle_1.png"),
                  arcade.load_texture("assets/sprites/player/player_idle_1.png"))
        idle_2 = (arcade.load_texture("assets/sprites/player/player_idle_2.png"),
                  arcade.load_texture("assets/sprites/player/player_idle_2.png"))
        self.idle_texture_pair = (idle_1, idle_2)

        idle_side_1 = (arcade.load_texture("assets/sprites/player/player_idle_L_1.png"),
                       arcade.load_texture("assets/sprites/player/player_idle_R_1.png"))
        idle_side_2 = (arcade.load_texture("assets/sprites/player/player_idle_L_2.png"),
                       arcade.load_texture("assets/sprites/player/player_idle_R_2.png"))
        self.idle_side_texture_pair = (idle_side_1, idle_side_2)

        attack_1 = (arcade.load_texture("assets/sprites/player/player_attack_L_1.png"),
                    arcade.load_texture("assets/sprites/player/player_attack_R_1.png"))
        attack_2 = (arcade.load_texture("assets/sprites/player/player_attack_L_2.png"),
                    arcade.load_texture("assets/sprites/player/player_attack_R_2.png"))
        self.attack_texture_pair = (attack_1, attack_2)

        jump_attack_1 = (arcade.load_texture("assets/sprites/player/player_jump_attack_L_1.png"),
                         arcade.load_texture("assets/sprites/player/player_jump_attack_R_1.png"))
        jump_attack_2 = (arcade.load_texture("assets/sprites/player/player_jump_attack_L_2.png"),
                         arcade.load_texture("assets/sprites/player/player_jump_attack_R_2.png"))
        self.jump_attack_texture_pair = (jump_attack_1, jump_attack_2)

        jump_1 = (arcade.load_texture("assets/sprites/player/player_jump_L.png"),
                  arcade.load_texture("assets/sprites/player/player_jump_R.png"))
        self.jump_texture_pair = (jump_1)

        walk_1 = (arcade.load_texture("assets/sprites/player/player_walk_L_1.png"),
                  arcade.load_texture("assets/sprites/player/player_walk_R_1.png"))
        walk_2 = (arcade.load_texture("assets/sprites/player/player_walk_L_2.png"),
                  arcade.load_texture("assets/sprites/player/player_walk_R_2.png"))
        walk_3 = (arcade.load_texture("assets/sprites/player/player_walk_L_3.png"),
                  arcade.load_texture("assets/sprites/player/player_walk_R_3.png"))
        walk_4 = (arcade.load_texture("assets/sprites/player/player_walk_L_4.png"),
                  arcade.load_texture("assets/sprites/player/player_walk_R_4.png"))
        self.walk_texture_pair = (walk_1, walk_2, walk_3, walk_4)

        self.texture = self.idle_texture_pair

        # Add listeners for all the events
        EventManager.add_listener("Update", self.on_update)  # calls on_update every frame
        EventManager.add_listener("KeyPress", self.on_key_press)  # calls on_key_press every time a key is pressed
        EventManager.add_listener("KeyRelease",
                                  self.on_key_release)  # calls on_key_release every time a key is released
        EventManager.add_listener("PhysicsUpdate", self.on_physics_update)  # calls physics_update every frame

        # UI entities
        self.__exit_entity = None
        self.__heart_entities = []
        print("Creating heart entities")
        for i in range(3):
            heart_sprite = arcade.Sprite("assets/sprites/heart_full.png")
            heart_sprite_renderer = SpriteRenderer(heart_sprite)
            heart_transform = Transform((i * (heart_sprite.width + 10) + 70, 980 + heart_sprite.height / 2), 0,
                                        1.0)
            heart_entity = Entity("Heart", ["HeartTag"], [heart_sprite_renderer, heart_transform])
            self.__heart_entities.append(heart_entity)
            GameManager.add_gui_entity(heart_entity)

        self.sprite_full = arcade.Sprite("assets/sprites/heart_full.png", 1.0)
        self.sprite_half = arcade.Sprite("assets/sprites/heart_half.png", 1.0)
        self.sprite_empty = arcade.Sprite("assets/sprites/heart_empty.png", 1.0)

        # Exit Button
        exit_sprite_renderer = SpriteRenderer(self.__exit_sprite)
        exit_transform = Transform((1860, 1020), 0, 1.0)
        exit_entity = Entity("Exit", ["ExitTag"], [exit_sprite_renderer, exit_transform])
        GameManager.add_gui_entity(exit_entity)

    # Called when the entity is removed
    # Remove listeners here
    def on_remove(self):
        print("Removing listeners for player controller")
        EventManager.remove_listener("Update", self.on_update)
        EventManager.remove_listener("KeyPress", self.on_key_press)
        EventManager.remove_listener("KeyRelease", self.on_key_release)
        EventManager.remove_listener("PhysicsUpdate", self.on_physics_update)

    def set_transform(self, pos):
        self.__transform.position = pos

    def get_transform_x(self):
        return self.__transform.position[0]

    def set_health(self, health):
        self.__health = health

    def set_power_up(self, power_up):
        self.__power_up_type = power_up

    def get_level_section_loader(self):
        return self.__level_section_loader

    # Called when parent entity is created
    def on_created(self):
        self.__collider = self.parent.get_component_by_name("Collider")
        self.__transform = self.parent.get_component_by_name("Transform")
        self.__sprite_renderer = self.parent.get_component_by_name("SpriteRenderer")
        self.__level_section_loader = self.parent.get_component_by_name("LevelSectionLoader")

    def set_gui(self):
        # GameManager.clear_gui_sprite()
        if self.__health <= 2:
            if self.__health == 1:
                self.make_heart_entity(0, "half")
            else:
                self.make_heart_entity(0, "full")
            self.make_heart_entity(1, "empty")
            self.make_heart_entity(2, "empty")
        elif self.__health <= 4:
            if self.__health == 3:
                self.make_heart_entity(1, "half")
            else:
                self.make_heart_entity(1, "full")
            self.make_heart_entity(0, "full")
            self.make_heart_entity(2, "empty")
        else:
            if self.__health == 5:
                self.make_heart_entity(2, "half")
            else:
                self.make_heart_entity(2, "full")
            self.make_heart_entity(0, "full")
            self.make_heart_entity(1, "full")

    def make_heart_entity(self, num_heart, state):
        if state == "full":
            heart_sprite = self.sprite_full
        elif state == "half":
            heart_sprite = self.sprite_half
        else:
            heart_sprite = self.sprite_empty
        self.__heart_entities[num_heart].get_component_by_name("SpriteRenderer").switch_sprite(heart_sprite)

    def get_exit_sprite(self):
        return self.__exit_sprite

    def set_exit_sprite(self, new_sprite):
        self.__exit_sprite = new_sprite

    def power_up_effects(self):
        # FLASHES PINK when you get HEALTH-UP
        if self.__health_up_timer < 0 and self.__flash_count == 0 and self.__health_up:
            self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__health_up_timer = 0.1
            self.__flash_count += 1

        if self.__health_up_timer < 0 and self.__flash_count == 1 and self.__health_up:
            self.__sprite_renderer.sprite.color = arcade.color.PINK
            self.__health_up_timer = 0.1
            self.__flash_count += 1

        if self.__health_up_timer < 0 and self.__flash_count == 2 and self.__health_up:
            self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__flash_count = 0
            self.__health_up = False

        # Undoes the effect of the speed-boost power-up
        if self.__speed_timer < 0 and self.__speed:
            self.__speed = False
            self.__flash_count = 0
            self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__max_velocity = (MAX_SPEED, MAX_JUMP)

        # Undoes the effect of the jump power-up
        if self.__jumping_timer < 0 and self.__jump:
            self.__jump = False
            self.__flash_count = 0
            self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__gravity = -1700
            self.__max_velocity = (self.__max_velocity[0], MAX_JUMP)

        # Undoes the effect of the attack power-up
        if self.__mad_timer < 0 and self.__mad:
            self.__mad = False
            self.__flash_count = 0
            self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__attack_power = 1

        # FLASHES BLUE when you get SPEED-BOOST
        if self.__speed:
            if self.__flash_count % 16 == 0:
                self.__sprite_renderer.sprite.color = (100, 100, 255)
            elif self.__flash_count % 8 == 0:
                self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__flash_count = (self.__flash_count + 1) % 80

        # FLASHES GREEN when you get JUMP-BOOST
        if self.__jump:
            if self.__flash_count % 16 == 0:
                self.__sprite_renderer.sprite.color = (100, 255, 100)
            elif self.__flash_count % 8 == 0:
                self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__flash_count = (self.__flash_count + 1) % 8e0

        # FLASHES PURPLE when you get ATTACK-BOOST
        if self.__mad:
            if self.__flash_count % 16 == 0:
                self.__sprite_renderer.sprite.color = (255, 100, 255)
            elif self.__flash_count % 8 == 0:
                self.__sprite_renderer.sprite.color = (255, 255, 255)
            self.__flash_count = (self.__flash_count + 1) % 80

    def toggle_camera_movement(self):
        self.__freeze_camera = not self.__freeze_camera

    # Update the player's animation
    # THE FOLLOWING LINK WAS USED AS REFERENCE: https://api.arcade.academy/en/latest/examples/platform_tutorial/step_12.html#platformer-part-twelve
    def update_animation(self, delta_time: float = 1 / 30):
        speed = 18
        if self.__velocity[0] > 100 or self.__velocity[0] < -100:
            speed = 13
        if self.__velocity[0] > 300 or self.__velocity[0] < -300:
            speed = 9
        if self.__velocity[0] > 500 or self.__velocity[0] < -500:
            speed = 7
        if self.__velocity[0] > 600 or self.__velocity[0] < -600:
            speed = 3

        # Determine if the character is facing right or left
        if self.__moving_left:
            self.character_face_direction = LEFT_FACING
        else:
            self.character_face_direction = RIGHT_FACING

        # If player is currently attacking
        if self.__attack_animation:
            # Update frame
            self.current_texture += 1
            if self.current_texture > (2 * 9) - 1:
                self.current_texture = 0
            frame = self.current_texture // 9
            if not self.__touching_floor:  # If you are attacking in air
                self.__sprite_renderer.set_texture(self.jump_attack_texture_pair[frame][self.character_face_direction])
                return
            else:
                self.__sprite_renderer.set_texture(self.attack_texture_pair[frame][self.character_face_direction])
                return

        # If the player is jumping
        if not self.__touching_floor:
            self.__sprite_renderer.set_texture(self.jump_texture_pair[self.character_face_direction])
            return

        # Idle animation:
        # Update frame
        if 0.1 > self.__velocity[0] > -0.1:
            if not self.__idle:
                # Side idle animation
                # Update frame
                self.current_texture += 1
                if self.current_texture > (2 * speed) - 1:
                    self.current_texture = 0
                frame = self.current_texture // speed
                self.__sprite_renderer.set_texture(self.idle_side_texture_pair[frame][self.character_face_direction])
                return

            self.current_texture += 1
            if self.current_texture > (2 * speed) - 1:
                self.current_texture = 0
            frame = self.current_texture // speed
            self.__sprite_renderer.set_texture(self.idle_texture_pair[frame][self.character_face_direction])
            return

        # Walking animation:
        # Update frame
        self.current_texture += 1
        if self.current_texture > (4 * speed) - 1:
            self.current_texture = 0
        frame = self.current_texture // speed
        self.__sprite_renderer.set_texture(self.walk_texture_pair[frame][self.character_face_direction])
        return

    @property
    def touching_ground(self):
        return self.__touching_ground

    @property
    def velocity(self):
        return self.__velocity

    @property
    def health(self):
        return self.__health

    @health.setter
    def health(self, new):
        self.__health = new

    @property
    def max_health(self):
        return self.__max_health

    @property
    def taking_damage(self):
        return self.__taking_damage

    @property
    def is_attacking(self):
        return self.__is_attacking

    @property
    def is_moving_left(self):
        return self.__moving_left

    @property
    def attack_power(self):
        return self.__attack_power


#!/usr/bin/env python3
# Work on apt.dat from FlightGear/XPlane
# HanishKVC, 2021
# GPL

import sys

import odb

gbDebug = False

def cleanup_line(sL):
    la = []
    prev = ''
    for cur in sL:
        if cur == ' ':
            if prev == '':
                continue
            else:
                la.append(prev)
                prev = ''
        else:
            prev += cur
    if prev != '':
        la.append(prev)
    return la

def parse_aptdat(sFName, db):
    f = open(sFName, errors='replace')
    state = '0'
    lCnt = 0
    apt = {}
    for l in f:
        lCnt += 1
        l = l.strip()
        la = cleanup_line(l)
        if len(la) == 0:
            state = '0'
            if apt.get('lat'):
                odb.set(db, apt['lat'], apt['lon'], apt)
            apt = {}
            continue
        else:
            if gbDebug:
                print("DBUG:ParseAptDat:{}:{}".format(lCnt,la))
        if la[0] == "1":
            if state != '0':
                print("ERRR:ParseAptDat:{}:New Airport Line before ending of prev airport data?".format(lCnt))
                continue
            state = 'A'
            apt['icao'] = la[4]
            if gbDebug:
                print("INFO:A:", la[4], la[5:])
        elif (la[0] == "16") or (la[0] == "17"):
            state = 'B'
            continue
        elif la[0] == "100":
            if state == 'B':
                continue
            if state != 'A':
                print("ERRR:ParseAptDat:{}:Runway data in wrong place?".format(lCnt))
                continue
            if gbDebug:
                print("INFO:R:", la[9], la[10])
            if not apt.get('lat'):
                apt['lat'] = la[9]
                apt['lon'] = la[10]
        else:
            continue

gDB = odb.initdb()
parse_aptdat(sys.argv[1], gDB)
print(gDB)
odb.store(gDB, "data/odb.pickle")


import pygame
from settings import *
from entity import Entity
from support import *

class Enemy(Entity):
    def __init__(self, groups):
        super().__init__(groups)
        self.can_attack = None

    def __int__(self, enemy_name, pos, groups, obstacle_sprites, damage_player):
        super().__init__(groups)
        self.sprite_type = 'enemy'

        # graphics
        self.import_graphics(enemy_name)
        self.status = 'still'
        self.image = self.animations[self.status][self.frame_index]

        self.rect = self.image.get_rect(topleft=pos)
        self.hitbox = self.rect.inflate(0, -10)
        self.obstacle_sprites = obstacle_sprites

        # stats
        self.enemy_name = enemy_name
        enemy_info = enemy_data[self.enemy_name]
        self.health = enemy_info['health']
        self.speed = enemy_info['speed']
        self.attack_radius = enemy_info['attack_radius']
        self.notice_radius = enemy_info['notice_radius']
        self.attack_type = enemy_info['attack_type']
        self.attack_damage = enemy_info['damage']
        self.resistance = enemy_info['resistance']

        self.damage_player = damage_player
        #self.can_attack = True
        #self.attack_time = None

        #self.vulnerable = True
        #self.hit_time = None

    def import_graphics(self):
        self.animations = {'still': [], 'flap': [], 'attack': []}
        main_path = f'../venv/monsters/'
        for animation in self.animations.keys():
            self.animations[animation] = import_folder(main_path + animation)

    def get_player_distance_direction(self, player):
        enemy_vec = pygame.math.Vector2(self.rect.center)
        player_vec = pygame.math.Vector2(player.rect.center)
        distance = (player_vec - enemy_vec).magnitude()

        if distance > 0:
            direction = (player_vec - enemy_vec).normalize()
        else:
            direction = pygame.math.Vector2()

        return (distance, direction)

    def get_status(self, player):
        distance = self.get_player_distance_direction(player)[0]

        if distance <= self.attack_radius and self.can_attack:
            if self.status != 'attack':
                self.frame_index = 0
            self.status = 'attack'
        elif distance <= self.notice_radius:
            self.status = 'flap'
        else:
            self.status = 'still'

    # def actions(self, player):
    #     if self.status == 'attack':
    #         self.attack_time = pygame.time.get_ticks()
    #         self.damage_player(self.attack_damage, self.attack_type)
    #     elif self.status == 'flap':
    #         self.direction = self.get_player_distance_direction(player)[1]
    #     else:
    #         self.direction = pygame.math.Vector2()

    def animate(self):
        animation = self.animations[self.status]

        self.frame_index += self.animation_speed
        if self.frame_index >= len(animation):
            if self.status == 'attack':
                self.can_attack = False
            self.frame_index = 0

        self.image = animation[int(self.frame_index)]
        self.rect = self.image.get_rect(center=self.hitbox.center)

    # def get_damage(self, player, attack_type):
    #     if self.vulnerable:
    #         self.direction = self.get_player_distance_direction(player)[1]
    #         if attack_type == 'rush':
    #             self.health -= player.get_full_weapon_damage()
    #         else:
    #             pass
    #
    #         self.hit_time = pygame.time.get_ticks()
    #         self.vulnerable = False

    def check_death(self):
        if self.health <= 0:
            self.kill()

    # def hit_reaction(self):
    #     if not self.vunerable:
    #         self.direction *= -self.resistance

    def update(self):
       # self.hit_reaction()
        self.move(self.speed)
        self.animate()
        self.check_death()

    def enemy_update(self, player):
        self.get_status(player)
        #self.actions(player)


#!/usr/bin/env python
import os
import sys
from setuptools import setup, find_packages

with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as f:
    long_description = f.read()

requirements = [
    'prompt_toolkit>=2.0.0,<2.1.0',
    'pyte>=0.5.1',
    'six>=1.9.0',
]

# Install yawinpty on Windows only.
if sys.platform.startswith('win'):
    requirements.append('yawinpty')

setup(
    name='ptterm',
    author='Jonathan Slenders',
    version='0.1',
    license='LICENSE',
    url='https://github.com/jonathanslenders/ptterm',
    description='Terminal emulator for prompt_toolkit.',
    long_description=long_description,
    packages=find_packages('.'),
    install_requires = requirements,
)


import datetime

from typing import Any, Optional, Union

from deker.ABC.base_dimension import BaseDimension
from deker.errors import DekerValidationError
from deker.log import SelfLoggerMixin
from deker.types import IndexLabels, Labels, Scale

class Dimension(SelfLoggerMixin, BaseDimension):
    """``Dimension`` of a grid axes or any other series except time (for time series use ``TimeDimension``).

    May be used for defining the majority of parameters which may be stored in an array.
    You can use ``labels`` or ``scale`` parameter to create a mapping of some names or values to the dimension indexes.

    :param name: dimension unique name (e.g. "length")
    :param size: dimension cells quantity
    :param labels: ordered mapping of dimension values names to their indexes

      ::

        ["name1", "name2", ..., "nameN"]
        ("float1", "float2", ..., "floatN")

    :param scale: a description of dimension regular scale; default None
    """

    __slots__ = ("__name", "__size", "__step", "__labels", "__scale")

    def _validate(  # noqa: C901,RUF100
        self,
        name: str,
        size: int,
        labels: Optional[Labels] = None,
        scale: Optional[Union[dict, Scale]] = None,
        **kwargs: Any,  # noqa[ARG002]
    ) -> None:
        super()._validate(name, size)

        if all(param is None for param in (labels, scale)):
            return

        if all(param is not None for param in (labels, scale)):
            raise DekerValidationError(
                f"Invalid Dimension {name} arguments: either `labels` or `scale` or none of them should be passed, "
                f"not both"
            )

        if scale is not None:
            if not isinstance(scale, (dict, Scale)):
                raise DekerValidationError(
                    f"Scale parameter value shall be an instance of deker.types.classes.Scale, not {type(self.scale)}"
                )

            if isinstance(scale, dict):
                try:
                    scale: Scale = Scale(**scale)
                except TypeError as e:
                    raise DekerValidationError(e)

            for attr in scale.__annotations__:
                value = getattr(scale, attr)
                if attr == "name":
                    if value is not None and (
                        not isinstance(value, str) or not value or value.isspace()
                    ):
                        raise DekerValidationError(
                            f"Scale attribute '{attr}' value shall be non-empty string"
                        )

                elif not isinstance(value, float):
                    raise DekerValidationError(f"Scale attribute '{attr}' value shall be float")

            scale_end_non_inclusive = scale.start_value
            scale_end_non_inclusive += scale.step * size
            scale_size = int(round(abs((scale_end_non_inclusive - scale.start_value) / scale.step)))
            if scale_size != size:
                raise DekerValidationError(
                    f"Dimension {name} wrong scale: does not match dimension size {size}"
                )

        if labels is not None:
            common_labels_exc = (
                "Labels shall be an ordered sequence or mapping of unique elements to their "
                "indexes in the dimension axis"
            )

            if not isinstance(labels, (tuple, list, dict)):
                raise DekerValidationError(common_labels_exc)

            if isinstance(labels, (list, tuple, dict)) and not labels:
                raise DekerValidationError("Label cannot be empty, use None instead")

            if len(labels) != size:
                raise DekerValidationError("Labels quantity do not match dimension size")

            if isinstance(labels, dict):
                for key, val in labels.items():
                    if not isinstance(key, str) or not isinstance(val, int):
                        raise DekerValidationError(common_labels_exc)

            if isinstance(labels, (list, tuple)):
                if all(isinstance(el, (list, tuple)) for el in labels):
                    keys = {el[0] for el in labels}
                    vals = {el[1] for el in labels}
                    if len(keys) < len(labels) or len(vals) < len(labels):
                        raise DekerValidationError(common_labels_exc)
                    if not all(isinstance(key, str) for key in keys) or not all(
                        isinstance(val, int) for val in vals
                    ):
                        raise DekerValidationError(common_labels_exc)
                elif len(set(labels)) < len(labels) or all(
                    (
                        all(not isinstance(el, str) for el in labels),
                        all(not isinstance(el, int) for el in labels),
                        all(not isinstance(el, float) for el in labels),
                    )
                ):
                    raise DekerValidationError(common_labels_exc)

    def __init__(
        self,
        name: str,
        size: int,
        labels: Optional[Labels] = None,
        scale: Optional[dict] = None,
        **kwargs: Any,
    ) -> None:
        """Dimension constructor.

        Validates parameters, sets them to default and converts labels and scale to a corresponding type.

        :param name: dimension name (e.g. "time")
        :param size: dimension cells quantity
        :param labels: ordered mapping of dimension values names to their indexes; default None
            ["name1", "name2", ..., "nameN"] |
            ("name1", "name2", ..., "nameN") |
            [("name1", 0), ("name2", 1), ..., ("nameN", N)] |
            (("name1", 0), ("name2", 1), ..., ("nameN", N)) |
            {"name1": 0, "name2": 1, ..., "nameN": N}
        :param scale: a description of dimension regular scale; default None
        :param kwargs: any keyword arguments
        """
        super().__init__(name, size, **kwargs)
        self._validate(name, size, labels, scale)
        self.__name: str = name
        self.__size: int = size
        self.__step: int = 1  # step attribute kept for further usage unification
        self.__labels: Optional[IndexLabels] = labels if not labels else IndexLabels(labels)
        self.__scale: Optional[Scale] = scale if not scale else Scale(**scale)
        self.logger.debug(f"Dimension {name} instantiated")

    @property
    def step(self) -> int:
        """Get ``Dimension`` step."""
        return self.__step

    @property
    def name(self) -> str:
        """Get ``Dimension`` name."""
        return self.__name

    @property
    def size(self) -> int:
        """Get ``Dimension`` size."""
        return self.__size

    @property
    def labels(self) -> Optional[IndexLabels]:
        """Get ``Dimension`` labels."""
        return self.__labels

    @property
    def scale(self) -> Optional[Scale]:
        """Get ``Dimension`` regular scale."""
        return self.__scale

    @property
    def as_dict(self) -> dict:
        """Serialize ``Dimension`` into dict."""
        dic = {"name": self.name, "size": self.size, "step": self.step}
        if self.labels:
            dic["labels"] = self.labels
        if self.scale:
            dic["scale"] = self.scale._asdict() if self.scale else self.scale
        return dic

    def __len__(self) -> int:
        """Get ``Dimension`` size."""
        return self.size

    def __repr__(self) -> str:
        s = (
            f"{self.__class__.__name__}("
            f"name={self.name!r}, "
            f"size={self.size!r}, "
            f"step={self.step!r}"
            f")"
        )
        if self.labels:
            s = s[:-1] + f", labels={self.labels})"
        if self.scale:
            s = s[:-1] + f", scale={self.scale})"
        return s

    def __str__(self) -> str:
        return str(self.size)

class TimeDimension(SelfLoggerMixin, BaseDimension):
    """Special dimension for time series.

    :param name: dimension name (e.g. "time")
    :param size: dimension cells quantity

    :param start_value: time mark of the first cell (index 0) in dimension
    :param step: a ``datetime.timedelta`` step of ``TimeDimension`` series within the grid

    .. note::

      ``start_value`` shall be:
        - either ``datetime.datetime`` object with explicit ``timezone``
        - or ``datetime.isoformat()`` with explicit ``timezone``
        - or a reference to an array ``attribute`` name which shall start with ``$`` *(attribute name shall be defined
          without prefix "$")*::

            custom_attributes(
                {"my_start_value": datetime.datetime(2023, 1, 1, 0, tzinfo=timezone.utc),
                }

            TimeDimension(start_value: "$my_start_value", ...)
    """

    __slots__ = ("__name", "__size", "__start_value", "__step_label", "__step")

    def __init__(
        self,
        name: str,
        size: int,
        start_value: datetime.datetime,
        step: datetime.timedelta,
    ) -> None:
        super().__init__(name, size, **{"start_value": start_value, "step": step})  # type: ignore[arg-type]
        self.__name: str = name
        self.__size: int = size
        self.__step: datetime.timedelta = step
        self.__start_value: datetime.datetime = start_value
        self.logger.debug(f"dimension {name} instantiated")

    def _validate(
        self,
        name: str,
        size: int,
        start_value: Union[datetime.datetime, str],
        step: datetime.timedelta,
        **kwargs: Any,  # noqa[ARG002]
    ) -> None:
        super()._validate(name, size)
        if not isinstance(step, datetime.timedelta) or not step:
            raise DekerValidationError(
                'TimeDimension "step" shall be a none-zero datetime.timedelta'
            )

        if isinstance(start_value, str):
            if not start_value.startswith("$"):
                incorrect_time_dimension_error = (
                    'TimeDimension "start_value" of <str> type shall be '
                    "an iso-format datetime string with explicit tz info"
                    "or a reference to an attribute name starting with `$`"
                )
                try:
                    dt = datetime.datetime.fromisoformat(start_value)  # type: ignore[arg-type]
                    if dt.tzinfo is None:
                        raise DekerValidationError(incorrect_time_dimension_error)
                except ValueError:
                    raise DekerValidationError(incorrect_time_dimension_error)

        elif isinstance(start_value, datetime.datetime):
            if start_value.tzinfo is None:
                raise DekerValidationError(
                    'TimeDimension "start_value" of <datetime> type shall be '
                    "a datetime.datetime instance with explicit tzinfo"
                )

        else:
            raise DekerValidationError(
                'TimeDimension "start_value" shall be a datetime.datetime instance with explicit tzinfo'
                "or an iso-format datetime string with explicit tz info "
                "or a reference to an attribute name starting with `$`"
            )

    @property
    def name(self) -> str:
        """Get ``TimeDimension`` name."""
        return self.__name

    @property
    def size(self) -> int:
        """Get ``TimeDimension`` size."""
        return self.__size

    @property
    def start_value(self) -> Union[datetime.datetime, str]:
        """Get ``TimeDimension`` start value."""
        return self.__start_value

    @property
    def step(self) -> datetime.timedelta:
        """Get ``TimeDimension`` step."""
        return self.__step

    @property
    def as_dict(self) -> dict:
        """Serialize ``TimeDimension`` into dict."""
        return {
            "name": self.name,
            "size": self.size,
            "step": self.step,
            "start_value": (
                self.start_value.isoformat()
                if isinstance(self.start_value, datetime.datetime)
                else self.start_value
            ),
        }

    def __len__(self) -> int:
        """Get ``TimeDimension`` size."""
        return self.size

    def __repr__(self) -> str:
        s = (
            f"{self.__class__.__name__}("
            f"name={self.name!r}, "
            f"size={self.size!r}, "
            f"step={self.step!r}, "
            f"start_value={self.start_value!r})"
        )
        return s

    def __str__(self) -> str:
        return str(self.size)


"""
1이 될 때까지
"""

n, k = map(int, input().split())
answer = 0
while n != 1:
    if n % k == 0:
        n /= k
    else:
        n -= 1
    answer += 1

print(answer)

"""
한번에 빼서 시간 줄이기

n, k = map(int, input().split())
answer = 0
while True:
    target = (n//k)*k
    answer += (n-target) #n이 k로 한번에 나누어 떨어지는 수 만들기
    n = target

"""


#什么是素数：除了1和它自身外，不能整除其他整数

def prime(num):
    tag=False
    for i in range(2,num):
        if(num%i==0):
            tag=True
    return tag

print(prime(1))


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

import pandas as pd
from src.read_data import app_train, app_test

def clean(df):
    df.columns= df.columns.str.lower()
    df = df.apply(lambda x: x.str.lower() if(x.dtype == 'object') else x)
    df = df.apply(lambda x: x.str.replace(' ', '_') if(x.dtype == 'object') else x)
    return df

app_train_clean = clean(app_train)
app_test_clean = clean(app_test)


from typing import List

class Solution:

    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        candies = [1] * n

        for i in range(1, n):
            # right > left
            if ratings[i] > ratings[i - 1]:
                candies[i] = candies[i - 1] + 1

        # reverse
        for i in range(n - 2, -1, -1):
            # left < right
            if ratings[i] > ratings[i + 1]:
                candies[i] = max(candies[i + 1] + 1, candies[i])

        return sum(candies)


import argparse
import os

import dgl
import numpy as np

from .graph import KGraph

import code

def kgraph_from_dgl(dgl_graph):
    n_edges = dgl_graph.num_edges()
    relations = dgl_graph.etypes
    rel_ids = {r : i for i, r in enumerate(relations)}

    head = np.empty(n_edges, dtype=np.int32)
    tail = np.empty_like(head)
    relation = np.empty_like(head)

    offset = 0

    for edge_type in dgl_graph.canonical_etypes:
        _, r, _ = edge_type
        h, t = dgl_graph.edges(etype=edge_type)

        head[offset : offset + h.size(0)] = h
        tail[offset : offset + h.size(0)] = t
        relation[offset : offset + h.size(0)].fill(rel_ids[r])

        offset += h.size(0)

    idx = np.lexsort((tail, relation, head))
    return KGraph.from_htr(head[idx], tail[idx], relation[idx], dgl_graph.num_nodes(), len(relations))

def main():
    parser = argparse.ArgumentParser(description='Import RDF dataset from DGL.')

    # data
    parser.add_argument('--dataset', type=str, required=True)
    parser.add_argument('--out_dir', type=str, default='.')

    args = parser.parse_args()

    cls = getattr(dgl.data, args.dataset, None)
    if not issubclass(cls, dgl.data.rdf.RDFGraphDataset):
        raise ValueError('Invalid dataset.')

    dataset = cls()

    kgraph = kgraph_from_dgl(dataset[0])
    kgraph.save(os.path.join(args.out_dir, 'graph.npz'))

    data = dataset[0].nodes[dataset.predict_category].data

    train_mask = data['train_mask'] == 1
    test_mask = data['test_mask'] == 1

    code.interact(local=locals())

    print(np.min(data['_ID']))
    print(np.max(data['_ID']))

    np.savez_compressed(
        os.path.join(args.out_dir, 'labels.npz'),
        train_ids = data['_ID'][train_mask],
        train_labels = data['labels'][train_mask],
        test_ids = data['_ID'][test_mask],
        test_labels = data['labels'][test_mask]
    )

if __name__ == '__main__':
    main()


import torch.nn as nn
import torch.utils.data as data
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch.autograd import Variable
from torch.nn import CrossEntropyLoss
from sklearn.manifold import TSNE
from sklearn.metrics import confusion_matrix
from matplotlib import cm

class Flatten(nn.Module):
    def forward(self, input):
        return input.view(input.size(0), -1)

class LeNet28(nn.Module): #for 28
    def __init__(self, input_nc):
        super(LeNet28, self).__init__()
        """nn.Conv2d(channel, outputchannel, kernel_size, stride, padding)"""
        sequence1 = [
            nn.Conv2d(input_nc, 32, kernel_size=5, stride=1, padding=2),
            nn.MaxPool2d(2, 2),
            nn.ReLU(True),

            nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
            # nn.Dropout2d(0.5),
            nn.MaxPool2d(2, 2),
            nn.ReLU(True),
            Flatten(),
            nn.Linear(7 * 7 * 64, 1024),
            nn.ReLU(True),

        ]
        sequence2 = [
            # Flatten(),
            # nn.Linear(7 * 7 * 64, 1024),
            # nn.ReLU(True),
            nn.Dropout(0.8),
            nn.Linear(1024, 10),
            nn.Softmax(dim=1)
        ]

        self.net1 = nn.Sequential(*sequence1)
        self.net2 = nn.Sequential(*sequence2)

    def forward(self, input, d_feat=False):
        """Standard forward."""
        # x 是最后一层（全连接前的那一层）输出的特征
        x = self.net1(input)
        out = self.net2(x)
        return out, x

class dataset(data.Dataset):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __len__(self):
        return len(self.x)

    def __getitem__(self, item):
        data = self.x[item]
        label = self.y[item]
        return data, label

def plot_acc(train_acc_list, test_acc_list):
    time = np.arange(0, len(train_acc_list))
    plt.plot(time, train_acc_list)
    plt.plot(time, test_acc_list)
    plt.legend(["acc", "loss"], loc=0)
    plt.savefig("acc_loss.png")
    plt.show()

def text_save(filename, data):#filename为写入TEXT文件的路径，data为要写入数据列表.
    file = open(filename,'a')
    for i in range(len(data)):
        s = str(data[i]).replace('[','').replace(']','')#去除[],这两行按数据不同，可以选择
        s = s.replace("'",'').replace(',','') +'\n'   #去除单引号，逗号，每行末尾追加换行符
        file.write(s)
    file.close()
    print("保存文件成功")

def test(test_set):
    model = LeNet28(1)
    model.cuda()
    model.load_state_dict(torch.load('./0_params.pth'))
    test_loader = torch.utils.data.DataLoader(test_set, batch_size=64, shuffle=True, num_workers=4)
    model.train(False)
    criterion = CrossEntropyLoss()
    test_corrects = 0.
    test_loss = 0.
    for batch_cnt, data in enumerate(test_loader):
        inputs, labels = data
        inputs = inputs.type(torch.FloatTensor)
        inputs = Variable(inputs.cuda())
        labels = Variable(labels.long().cuda())

        outputs, last_layer = model(inputs)  ##########

        loss = criterion(outputs, labels)

        _, preds = torch.max(outputs, 1)
        test_corrects += torch.sum((preds == labels)).item()
        test_loss += loss.item()

    test_acc = test_corrects / (len(test_set))
    print('test_acc: %f' % test_acc)

def get_tsne_data(test_set):
    last = []
    label = []
    pred = []
    model = LeNet28(1)
    model.cuda()
    ####################################################################
    model.load_state_dict(torch.load('./49_0.993_0.984_params.pth'))
    test_loader = torch.utils.data.DataLoader(test_set, batch_size=64, shuffle=True, num_workers=4)
    model.train(False)
    test_corrects = 0.
    for batch_cnt, data in enumerate(test_loader):
        inputs, labels = data
        # print(labels, type(labels))
        inputs = inputs.type(torch.FloatTensor)
        inputs = Variable(inputs.cuda())
        labels = Variable(labels.long().cuda())
        outputs, last_layer = model(inputs)  #######################
        '''需根据实际情况修改参数'''
        if last_layer.shape[0] == 64:
            last_layer = last_layer.reshape([64, -1])
            # print(last_layer.shape)
        else:
            last_layer = last_layer.reshape([56, -1])
            # print(last_layer.shape)

        last_layer = last_layer.cpu().detach().numpy().tolist()
        last = last + last_layer

        labels_ = labels.cpu().detach().numpy().tolist()
        label = label + labels_

        _, preds = torch.max(outputs, 1)
        preds_ = preds.cpu().detach().numpy().tolist()
        pred = pred + preds_

        test_corrects += torch.sum((preds == labels)).item()
    print(np.array(label).shape)
    text_save('test_data.txt', last)
    text_save('test_labels.txt', label)
    text_save('test_preds.txt', pred)

    test_acc = test_corrects / (len(test_set))
    print('test_acc: %f' % test_acc)

def plt_tsne():
    data = np.loadtxt("test_data.txt")
    # print(data.shape)
    labels = np.loadtxt("test_labels.txt")
    # print(labels)
    tsne = TSNE(n_components=2, learning_rate=200.0).fit_transform(data)
    # print(tsne)
    cm = plt.cm.get_cmap('jet')
    sc = plt.scatter(tsne[:, 0], tsne[:, 1], c=labels, cmap=cm)
    plt.colorbar(sc)
    plt.savefig('tSNE.png')
    plt.show()

# def plot_confusion_matrix(cm, title='Confusion Matrix', cmap=plt.cm.binary):
#     labels = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
#     plt.imshow(cm, interpolation='nearest', cmap=cmap)
#     plt.title(title)
#     plt.colorbar()
#     xlocations = np.array(range(len(labels)))
#     plt.xticks(xlocations, labels, rotation=90)
#     plt.yticks(xlocations, labels)
#     plt.ylabel('True label')
#     plt.xlabel('Predicted label')
#     cm = confusion_matrix(y_true, y_pred)
#     np.set_printoptions(precision=2)

# def plot_with_labels(lowDWeights, labels):
#     plt.cla()
#     X, Y = lowDWeights[:, 0], lowDWeights[:, 1]
#     for x, y, s in zip(X, Y, labels):
#         c = cm.rainbow(int(255 * s / 9));
#         plt.text(x, y, s, backgroundcolor=c, fontsize=9)
#     plt.xlim(X.min(), X.max());
#     plt.ylim(Y.min(), Y.max());
#     plt.title('Visualize last layer');
#     plt.show();
#     plt.pause(0.01)


# Given a set of distinct integers, nums, return all possible subsets (the power set).
#
# Note: The solution set must not contain duplicate subsets.
#
# Example:
#
# Input: nums = [1,2,3]
# Output:
# [
#   [3],
#   [1],
#   [2],
#   [1,2,3],
#   [1,3],
#   [2,3],
#   [1,2],
#   []
# ]

def subset_rec(nums):
    n = len(nums)
    sol = [[]]
    for num in nums:
        sol += [curr + [num] for curr in sol]
        print(sol)
    return sol

def subset_backtrack(nums):
    sol = []
    def back_track(first=0, seq=[]):
        if len(seq) == n:
            sol.append(seq[:])
        for i in range(first, len(nums)):
            seq.append(nums[i])
            back_track(i+1, seq)
            seq.pop()
    for n in range(len(nums)+1):
        back_track()
    return sol

def s2(nums):
    sol = [[]]
    for num in nums:
        sol += [x + [num] for x in sol]
        print(sol)
    return sol

print(s2([1,2,3]))


import numpy as np
from netCDF4 import Dataset
import matplotlib.pyplot as plt

import glob

fsL=[]
for i in range(4,9):
    fs=glob.glob("SGP/sgp*%2.2i01*"%i)
    fsL.extend(sorted(fs))

Q1RL=[]
Q2L=[]
qvL=[]
TL=[]
TsfcL=[]
omegaL=[]
lhL=[]
for f in fsL:
    fh=Dataset(f)
    #Q1R=fh["Q1R"][:]
    #Q2=fh["Q2"][:]
    q2=fh["q_adv_h"][:]+fh["q_adv_v"][:]
    q1=fh["T_adv_h"][:]+fh["T_adv_v"][:]
    omega=fh["omega"][:]
    qv=fh['q'][:]
    T=fh["T"][:]
    lev=fh["lev"][:]
    #t=fh["time"][:]
    lh=fh["LH_col"][:]
    Tsfc=fh['T_srf'][:]

    #plt.subplot(211)
    #plt.pcolormesh(t,lev,q1.T,cmap='jet')
    #plt.ylim(1000,100)
    #plt.subplot(212)
    #plt.pcolormesh(t,lev,q2.T,cmap='jet')
    #plt.ylim(1000,100)
    #plt.figure()
    a=np.nonzero(lh>0)
    b=np.nonzero(Tsfc[a]>22)
    omegaL.extend(omega[a][b])
    qvL.extend(qv[a][b])
    TL.extend(T[a][b])
    TsfcL.extend(Tsfc[a][b])
    lhL.extend(lh[a][b])
    #plt.plot(q1[a].mean(axis=0),lev)
    #plt.plot(-q2[a].mean(axis=0),lev)
    #b=np.nonzero(lh<=0)
    #plt.plot(-q2[b].mean(axis=0),lev)
    #plt.ylim(1000,100)
    #stop
    #convFract=fh["convFract"][:]
    #for i,Q1R1 in enumerate(Q1R):
    #    if convFract[i]>0.5:
    #        Q1RL.append(Q1R1)
    #        Q2L.append(Q2[i])

from sklearn.cluster import KMeans
import numpy as np
kmeans = KMeans(n_clusters=25, random_state=0).fit(np.array(omegaL))
omegaL=np.array(omegaL)
TL=np.array(TL)
qvL=np.array(qvL)
lhL=np.array(lhL)
ic=0
classL=[]
q1L=[]
q2L=[]
for i in range(5):
    for j in range(5):
        a=np.nonzero(kmeans.labels_==ic)
        if lhL[a].mean()>100 and ic in [1,2,12,20]:
            plt.figure(figsize=(12,8))
            plt.subplot(121)
            omega_m=omegaL[a[0],:].mean(axis=0)
            T_m=TL[a[0],:].mean(axis=0)*(1000/lev)**0.287
            dT_dp=np.gradient(T_m)
            plt.plot(omega_m*dT_dp/25*24,lev)
            plt.ylim(1000,100)
            plt.title("lh=%7.2f class=%i"%(lhL[a].mean(),ic))
            plt.subplot(122)
            dq_dp=np.gradient(qvL[a[0],:].mean(axis=0))
            plt.plot(omega_m*dq_dp/25*24,lev)
            plt.ylim(1000,100)
            classL.append(ic)
            if ic in [1,2,12,20]:
                q1L.append(omega_m*dT_dp/25*24)
                q2L.append(omega_m*dq_dp/25*24)
        ic+=1

import pickle
pickle.dump({"q1L":q1L,"q2L":q2L,"lev":lev},open("q1q2.pklz","wb"))


"""
Index error

"""

lista = [1, 2, 3, 4, 5]

try:
    lista[10]
except IndexError:
    print(f"Error: el indice al que intentas acceder se encuentra fuera de rango la longgitud de la lista es:{len(lista)}")


import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from jp_extractor import JapaneseMecabWordExtractor
from chainer import iterators

def make_vectorizer(texts, null_set=(0, ""), unknown_set=(1, "###"),
                    option=None):
    """
    """
    print("Making vectorizer.")
    vectorizer = CountVectorizer(max_df=1.0,
                                 min_df=10,
                                 max_features=10000,
                                 stop_words=[null_set[1], unknown_set[1]])
    vectorizer.tokenizer = JapaneseMecabWordExtractor(split_mode="unigram",
                                                      use_all=True,
                                                      tagger_option=option)
    vectorizer.fit(texts)
    max_id = max(vectorizer.vocabulary_.values())
    prev_char = vectorizer.get_feature_names()[null_set[0]]
    vectorizer.vocabulary_[null_set[1]] = null_set[0]
    vectorizer.vocabulary_[prev_char] = max_id + 1
    prev_char = vectorizer.get_feature_names()[unknown_set[0]]
    vectorizer.vocabulary_[unknown_set[1]] = unknown_set[0]
    vectorizer.vocabulary_[prev_char] = max_id + 2
    return vectorizer

def text_to_word_sequence(texts, tokenizer, vocabulary,
                          null_id, unknown_id, max_length=None):
    """
    """
    print("Converting text to index sequences")
    num_unknown = 0
    total_words = 0
    sequences = list()
    for text in texts:
        words = tokenizer(text)
        maxl = max_length if max_length else len(words)
        ids = np.ones(maxl, dtype="int32")*null_id
        offset = 0 if len(words) > maxl else maxl - len(words)
        for i, word in enumerate(words):
            if i >= maxl:
                break
            ids[i+offset] = vocabulary.get(word, unknown_id)
            total_words += 1
            if ids[i+offset] == unknown_id:
                num_unknown += 1
        sequences.append(ids)

    print("unknown/words : {}/{} = {:.4f}".format(
        num_unknown, total_words, num_unknown/total_words))
    print("unknown/sentence : {}/{} = {:.4f}".format(
        num_unknown, len(sequences), num_unknown/len(sequences)))

    return sequences

class SerialPaddingIterator(iterators.SerialIterator):
    """
    """
    def __next__(self):
        """
        """
        batch = super(SerialPaddingIterator, self).__next__()
        sequences = [batch[i][0] for i in range(len(batch))]
        sequences = pad_sequences(sequences)
        for i, sequence in enumerate(sequences):
            batch[i] = (np.expand_dims(sequence, 0), batch[i][1])
        return batch

    next = __next__

### keras function ###
def pad_sequences(sequences, maxlen=None, dtype='int32',
                  padding='pre', truncating='pre', value=0.):
    """Pads each sequence to the same length (length of the longest sequence).
    If maxlen is provided, any sequence longer
    than maxlen is truncated to maxlen.
    Truncation happens off either the beginning (default) or
    the end of the sequence.
    Supports post-padding and pre-padding (default).
    # Arguments
        sequences: list of lists where each element is a sequence
        maxlen: int, maximum length
        dtype: type to cast the resulting sequence.
        padding: 'pre' or 'post', pad either before or after each sequence.
        truncating: 'pre' or 'post', remove values from sequences larger than
            maxlen either in the beginning or in the end of the sequence
        value: float, value to pad the sequences to the desired value.
    # Returns
        x: numpy array with dimensions (number_of_sequences, maxlen)
    # Raises
        ValueError: in case of invalid values for `truncating` or `padding`,
            or in case of invalid shape for a `sequences` entry.
    """
    if not hasattr(sequences, '__len__'):
        raise ValueError('`sequences` must be iterable.')
    lengths = []
    for x in sequences:
        if not hasattr(x, '__len__'):
            raise ValueError('`sequences` must be a list of iterables. '
                             'Found non-iterable: ' + str(x))
        lengths.append(len(x))

    num_samples = len(sequences)
    if maxlen is None:
        maxlen = np.max(lengths)

    # take the sample shape from the first non empty sequence
    # checking for consistency in the main loop below.
    sample_shape = tuple()
    for s in sequences:
        if len(s) > 0:
            sample_shape = np.asarray(s).shape[1:]
            break

    x = (np.ones((num_samples, maxlen) + sample_shape) * value).astype(dtype)
    for idx, s in enumerate(sequences):
        if not len(s):
            continue  # empty list/array was found
        if truncating == 'pre':
            trunc = s[-maxlen:]
        elif truncating == 'post':
            trunc = s[:maxlen]
        else:
            raise ValueError('Truncating type "%s" not understood' % truncating)

        # check `trunc` has expected shape
        trunc = np.asarray(trunc, dtype=dtype)
        if trunc.shape[1:] != sample_shape:
            raise ValueError('Shape of sample %s of sequence at position %s is different from expected shape %s' %
                             (trunc.shape[1:], idx, sample_shape))

        if padding == 'post':
            x[idx, :len(trunc)] = trunc
        elif padding == 'pre':
            x[idx, -len(trunc):] = trunc
        else:
            raise ValueError('Padding type "%s" not understood' % padding)
    return x


"""
find and delete all "*.pyc" bytecode files at and below the directory
named on the command-line; this uses a Python-coded find utility, and
so is portable; run this to delete .pyc's from an old Python release;
"""

import os, sys, find                                    # here, gets Tools.find

count = 0
for filename in find.find('*.pyc', sys.argv[1]):
    count += 1
    print(filename)
    os.remove(filename)

print('Removed %d .pyc files' % count)

'''
This works portably, and it avoids external program startup costs. But find is really
just half the story—it collects files matching a name pattern but doesn’t search their
content. Although extra code can add such searching to a find’s result, a more manual
approach can allow us to tap into the search process more directly.
'''


#!/usr/bin/env python3
import neovim
import re
from collections import defaultdict, namedtuple
from os import environ
from os.path import basename, dirname, join, isfile, exists
from errno import ENOENT
from stat import *
from time import time
try:
    from nvimfs.fuse import FUSE, FuseOSError, Operations
except:
    from fuse import FUSE, FuseOSError, Operations

OBuffer = namedtuple('OBuffer', 'name')

class OxberryFS(Operations):
    def __init__(self, start_client=None, mountpoint=None):
        self.mtpoint = mountpoint
        self.files = {}
        self.data = defaultdict(bytes)
        self.fd = 0 # file number
        self.cd = 0 # client number
        self.wd = 0 # window number
        now = time()
        self.files['/'] = dict(st_mode=(S_IFDIR | 0o0755), st_ctime=now,
                st_mtime=now, st_atime=now, st_nlink=2)
        self.mkdir('/clients') # contains seen clients
        self.create('/clients/new') # write to it to register a new client
        if start_client:
            self.write('/clients/new', start_client.encode())

    def init_client(self, address):
        """
        initializes a client.
        """
        path = '/clients/' + str(self.cd)
        self.mkdir(path)
        self.create(join(path,'name')) # keeps the socket address
        self.write(join(path, 'name'), address.encode())
        self.chmod(join(path, 'name'), 0o0444) # the name is read-only
        self.create(join(path, 'cmd')) # write to it to execute commands
        self.create(join(path, 'eval')) # write to set a expression, read to eval it
        self.mkdir(join(path, 'buffers')) # a directory of buffers
        self.create(join(path, 'buffers', 'new')) # write to it to create a new buffer
        self.populate_buffers(path)
        self.mkdir(join(path, 'windows')) # a directory of windows
        self.create(join(path, 'windows', 'new')) # write to it to create a new buffer
        #self.populate_windows(path)
        self.cd += 1

    def populate_buffers(self, client_path):
        cname = self.get_client_name(client_path + "/")
        nvim = neovim.attach("socket", path=cname)
        for buf in nvim.buffers:
            if isfile(buf.name):
                buf_path = join(client_path, "buffers", str(buf.number))
                self.init_buffer(buf, buf_path)

    def init_buffer(self, buf_data, address):
        self.mkdir(address)
        self.create(join(address, "name"))
        self.write(join(address, "name"), buf_data.name)
        #self.create(join(address, "options"))
        #self.create(join(address, "vars"))
        self.create(join(address, "tags"))

    def populate_windows(self, client_path):
        cname = self.get_client_name(client_path + "/")
        nvim = neovim.attach("socket", path=cname)
        for win in nvim.windows:
            self.wd += 1
            win_path = join(client_path, "windows", str(self.wd))
            self.init_window(win_path, win)

    def init_window(self, address, win_data):
        self.mkdir(address)
        #self.create(join(address, "options"))
        #self.create(join(address, "vars"))

    def get_client_name(self, address):
        m = re.match('/clients/\d*/', address)
        if m:
            name = self.data[join(m.group(), 'name')]
            if name != b'':
                return name.decode()

    def reap_clients_if_needed(self, address):
        for f in self.files:
            m = re.match('/clients/\d+/name', f)
            if m:
                name = self.data[m.group()]
                if name != b'':
                    if not exists(name):
                        self.rmdir(dirname(m.group()))
                        break

    def chmod(self, path, mode):
        self.files[path]['st_mode'] &= 0o0770000
        self.files[path]['st_mode'] |= mode
        return 0

    def chown(self, path, uid, gid):
        self.files[path]['st_uid'] = uid
        self.files[path]['st_gid'] = gid

    def create(self, path, mode=0o0644):
        self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1,
                                st_size=0, st_ctime=time(), st_mtime=time(),
                                st_atime=time())

        self.fd += 1
        return self.fd

    def getattr(self, path, fh):
        if path not in self.files:
            raise FuseOSError(ENOENT)

        return self.files[path]

    def getxattr(self, path, name, position=0):
        attrs = self.files[path].get('attrs', {})

        try:
            return attrs[name]
        except KeyError:
            return ''       # Should return ENOATTR

    def listxattr(self, path):
        attrs = self.files[path].get('attrs', {})
        return attrs.keys()

    def mkdir(self, path, mode=0o0755):
        if not path in self.files:
            self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=1,
                                    st_size=0, st_ctime=time(), st_mtime=time(),
                                    st_atime=time())

            self.files['/']['st_nlink'] += 1
            return 0
        else:
            return -1

    def open(self, path, flags):
        self.fd += 1
        return self.fd

    def read(self, path, size, offset, fh):
        return self.data[path][offset:offset + size]

    def readdir(self, path, fh):
        self.reap_clients_if_needed(path)
        files = [basename(x[1:]) for x in self.files \
                                        if x != '/' \
                                        and dirname(x) == path \
                                        and x != path]
        return ['.', '..'] + files

    def readlink(self, path):
        return self.data[path]

    def removexattr(self, path, name):
        attrs = self.files[path].get('attrs', {})

        try:
            del attrs[name]
        except KeyError:
            pass        # Should return ENOATTR

    def rename(self, old, new):
        self.files[new] = self.files.pop(old)

    def rmdir(self, path):
        # FIXME: handle files within the directories, and remove data too
        try:
            self.files.pop(path)
            self.files['/']['st_nlink'] -= 1
        except KeyError:
            pass

    def setxattr(self, path, name, value, options, position=0):
        # Ignore options
        attrs = self.files[path].setdefault('attrs', {})
        attrs[name] = value

    #def statfs(self, path):
        #return dict(f_bsize=512, f_blocks=2000000, f_bavail=1000000)

    def symlink(self, target, source):
        self.files[target] = dict(st_mode=(S_IFLNK | 0o0777), st_nlink=1,
                                  st_size=len(source))

        self.data[target] = source

    def truncate(self, path, length, fh=None):
        self.data[path] = self.data[path][:length]
        self.files[path]['st_size'] = length

    def unlink(self, path):
        self.files.pop(path)

    def utimens(self, path, times=None):
        now = time()
        atime, mtime = times if times else (now, now)
        self.files[path]['st_atime'] = atime
        self.files[path]['st_mtime'] = mtime

    def nvim_eval(self, nvim, exprs):
        vals = []
        for expr in exprs:
            try:
                evaled = nvim.eval(expr)
                if isinstance(evaled, bytes):
                    vals.append(evaled.decode())
                else:
                    vals.append(str(evaled))
            except neovim.api.nvim.NvimError:
                vals.append("'" + expr + "' couldn't be evaluated")
        vals.append('')
        read_data = "\n".join(vals).encode(errors="ignore")
        return read_data

    def write(self, path, data, offset=0, fh=None):
        r_len = len(data)
        cname = self.get_client_name(path)
        if cname:
            nvim = neovim.attach("socket", path=cname)
            lines = data.decode(errors="ignore").splitlines()
            if re.match('/clients/\d+/cmd', path):
                for c in lines:
                    nvim.command(c)
            elif re.match('/clients/\d+/eval', path):
                data = self.nvim_eval(nvim, lines)
            elif re.match('/clients/\d+/buffers/new', path):
                for f in lines:
                    nvim.command('drop ' + f)
                    # initialize the buffer tree
                    bufnr = str(nvim.eval('bufnr("%")'))
                    buf_path = join(re.match('/clients/\d+/buffers/', path).group(), bufnr)
                    self.init_buffer(OBuffer(f.encode()), buf_path)
            elif re.match('/clients/\d+/windows/new', path):
                for w in lines:
                    if re.match('((vertical|leftabove|aboveleft|rightbelow|belowright|topleft|botright)\s)*'\
                            'v?(split|new)', w):
                        nvim.command(w)
        else:
            if re.match('/clients/new', path):
                self.init_client(data.decode())
        # save the data
        # TODO: respect file permissions
        self.data[path] = self.data[path][:offset] + data
        self.files[path]['st_size'] = len(self.data[path])
        return r_len

if __name__ == '__main__':
    from sys import argv
    if 'NVIM_LISTEN_ADDRESS' in environ:
        fuse = FUSE(OxberryFS(environ['NVIM_LISTEN_ADDRESS'], argv[1]), argv[1], foreground=True)
    else:
        fuse = FUSE(OxberryFS(), argv[1], foreground=True)


from __future__ import annotations

from dataclasses import dataclass
from typing import Optional
from datetime import datetime

from ConInstance import ConFile
from FTP import FTP

# These classes track changes to the list of files for a particular Con Instance
# All it cares about is the files and their names
# Once the user is done editing the ConInstance page and does an upload, this class will provide the instructions
#   for the upload so that all the accumulated edits get up there.

@dataclass
class Delta:
    Verb: str
    Con: ConFile
    Oldname: str=""

    def __str__(self) -> str:
        s=self.Verb+": "+str(self.Con)
        if self.Oldname is not None and len(self.Oldname) > 0:
            s+=" oldname="+self.Oldname
        return s

# Changes (the tuple providing info needed to defined them) are (in the order in which they must be executed):
#       Delete a file which exists on the website ("delete", con, "")
#       Rename an existing website file ("rename", con, oldname)
#       Add a new file ("add", con, "")
#       Replace an existing file ("replace", con, oldname)
# When two deltas affect the same file, they are usually merged.  (E.g., Add followed by Delete cancels out; Add followed by Rename updates the Add with the new name.)
class ConInstanceDeltaTracker:
    def __init__(self):
        self._deltas: list[Delta]=list()

    def __str__(self) -> str:
        if self._deltas is None or len(self._deltas) == 0:
            return ""
        s=""
        for d in self._deltas:
            s+=">>"+str(d)+"\n"
        return s

    def Add(self, con: ConFile) -> None:
        self._deltas.append(Delta("add", con, ""))

    def Delete(self, con: ConFile) -> None:
        # If the item being deleted was just added, simply remove the add from the deltas list
        for i, item in enumerate(self._deltas):
            if item.Verb == "add":
                if item.Con.DisplayTitle == con.DisplayTitle:
                    del self._deltas[i]
                    return
        # OK, the item is not queued to be added so it must already be on the website: add a delete action to the deltas list
        self._deltas.append(Delta("delete", con, ""))

    # Change the name of a file on the website site
    def Rename(self, con: ConFile, oldname: str) -> None:
        # First check to see if this is a rename of a rename.  If it is, merge them by updating the existing rename.
        for i, item in enumerate(self._deltas):
            if item.Verb == "rename":
                if item.Oldname == con.DisplayTitle:
                    self._deltas[i]=Delta("rename", con, oldname)
                    return
            # Now check to see if this is a rename of a newly-added file.  If so, we just modify the add Delta
            elif item.Verb == "add":
                if item.Con.DisplayTitle == con.DisplayTitle:
                    item.Con=con
                    return

        # If it doesn't match anything in the delta list, then it must be a rename of an existing file.
        self._deltas.append(Delta("rename", con, oldname))

    # We want to replace one file with another
    def Replace(self, con: ConFile, oldname: str):
        # Check to see if the replacement is in a row yet to be uploaded or a row which has been renamed.
        for i, item in enumerate(self._deltas):
            if item.Verb == "rename":
                if item.Con.SourcePathname == con.SourcePathname:
                    self._deltas[i]=Delta("rename", con, oldname)
                    return
            # Now check to see if this is a rename of a newly-added file
            elif item.Verb == "add":
                if item.Con.SourcePathname == con.SourcePathname:
                    # Just update the local pathname in the add entry
                    self._deltas[i].Con.SourcePathname=con.SourcePathname
                    return

        # If it doesn't match anything in the delta list, then it must be a new local file to replace an old one in an existing entry
        # We need to delete the old file and then upload the new.
        self._deltas.append(Delta("replace", con, oldname))

    @property
    def Num(self) -> int:
        return len(self._deltas)

    @property
    def Deltas(self) -> list[Delta]:
        return self._deltas

class UpdateFTPLog:
    g_ID: Optional[str]=None

    def Init(self, id: str):
        UpdateFTPLog.g_ID=id
        pass

    def Log(self, series: str, con: str = "", deltas: Optional[ConInstanceDeltaTracker] = None):
        lines="Uploaded ConInstance: "+series+":"+con+"   "+"["+UpdateFTPLog.g_ID+"  "+datetime.now().strftime("%A %B %d, %Y  %I:%M:%S %p")+" EST]\n"
        if deltas is not None and deltas.Num > 0:
            lines+="^^deltas by "+FTP().GetEditor()+":\n"+str(deltas)+"\n"
        FTP().AppendString("/updatelog.txt", lines)
        pass

    def LogText(self, txt: str):
        FTP().AppendString("/updatelog.txt", txt+"   ["+UpdateFTPLog.g_ID+"  "+FTP().GetEditor()+"  "+datetime.now().strftime("%A %B %d, %Y  %I:%M:%S %p")+" EST]\n")


from heapq import nlargest
from collections import defaultdict
from nltk.probability import FreqDist
from hazm import stopwords_list, sent_tokenize, word_tokenize

def summarize_text(text: str):
    sents = sent_tokenize(text)
    size = int(len(sents) / 5)
    word_sent = word_tokenize(text)
    word_sent = [word for word in word_sent if word not in stopwords_list()]
    freq = FreqDist(word_sent)
    ranking = defaultdict(int)

    for i, sent in enumerate(sents):
        sent_words = word_tokenize(sent)
        for w in sent_words:
            if w in freq:
                ranking[i] += freq[w]
        ranking[i] /= len(sent_words)

    sents_idx = nlargest(size, ranking, key=ranking.get)

    return " ".join([sents[j] for j in sorted(sents_idx)])


# coding: utf-8

'''
YouTube Comments Sentiment Analysis
Andie's Version
Spring 2018
'''
import pandas as pd
import os
import csv
import numpy as np
import re
import warnings
warnings.filterwarnings('ignore')

# ### 1.2 Read In Data

# In[151]:

path = '/Users/andiedonovan/myProjects/Youtube_Python_Project/AndiesBranch/'

os.chdir('/Users/andiedonovan/myProjects/Youtube_Python_Project/AndiesBranch/data/') # change directory
df = pd.read_csv('OKGOcomments.csv', delimiter=";", skiprows=2, encoding='latin-1', engine='python') # read in the data
data = pd.read_csv('data.csv', delimiter=",", skiprows=2, encoding='latin-1', engine='python') # read in the user's data

# ### 1.3 Clean Data Columns

# In[152]:

df.columns = [
  'label',
  'comment','a','b'
]
df = df.drop(['a', 'b'], axis = 1).dropna() # drop columns 3 and 4 and missing values

# ### 1.4 Remove Non-Alphabetic Characters (including numbers)

# In[153]:

for row in range(len(df)):
    line = df.iloc[row,1]
    df.iloc[row,1] = re.sub("[^a-zA-Z]", " ", line)

# In[154]:

df.head(5)

import nltk
from nltk.tokenize import sent_tokenize, word_tokenize

df['com_token']=df['comment'].str.lower().str.split()

from nltk.corpus import stopwords
nltk.download('stopwords')
sw = stopwords.words('english')

df['com_remv']=df['com_token'].apply(lambda x: [y for y in x if y not in sw])

from nltk.stem import PorterStemmer
ps = PorterStemmer()
lemmatizer = nltk.stem.WordNetLemmatizer()

df["com_lemma"] = df['com_remv']     .apply(lambda x : [lemmatizer.lemmatize(y) for y in x]) # lemmatization

df['com_stem']=df['com_lemma']     .apply(lambda x : [ps.stem(y) for y in x]) # stemming

import sklearn # machine learning
from sklearn.model_selection import train_test_split # splitting up data

df["com_stem_str"] = df["com_stem"].apply(', '.join)

X_train, X_test, Y_train, Y_test = train_test_split(
                                    df["com_stem_str"], df["label"],
                                    test_size=0.25,
                                    random_state=42)

print('lengths training variables: ', len(X_train),",", len(Y_train))
print('lengths testing variables: ', len(X_test),",", len(Y_test), '\n')

print('Are there any missing values?',
      '\n * Training:', pd.isnull(X_train).values.any(), ',', pd.isnull(Y_train).values.any(),
      '\n * Testing: ', pd.isnull(X_test).values.any(), ",", pd.isnull(Y_test).values.any())

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

tfidf = TfidfVectorizer()
xtrain = tfidf.fit_transform(X_train) # transform and fit training data
xtest = tfidf.transform(X_test) # transform test data from fitted transformer

data_transformed = tfidf.fit_transform(df["com_stem_str"]) # transform entire dataset for cross validation

from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression
from sklearn import svm # support vector machine
from sklearn import metrics # for accuracy/ precision
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import SGDClassifier # Stochastic Gradient Descent

mnb = MultinomialNB()
mnb.fit(xtrain, Y_train) # fit the model on the training data word counts and training data lables

mnb_predict = mnb.predict(xtest) # make our y predictions (labels) on the comment test data
mnb_acc = metrics.accuracy_score(Y_test, mnb_predict)
print('We obtained ', round(mnb_acc, 6), '% accuracy for the model')

print(metrics.classification_report(Y_test, mnb_predict))

metrics.confusion_matrix(Y_test, mnb_predict)

scores = cross_val_score(mnb, data_transformed, df["label"], cv=5) # 5 fold cross validation
print("Confidence Interval for Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

lr = LogisticRegression(solver='sag', max_iter=100, random_state=42, multi_class="multinomial") # set multinomial setting for multiclass data

lr.fit(xtrain, Y_train)

lr_predict = lr.predict(xtest)
lr_acc = metrics.accuracy_score(Y_test, lr_predict)
print('We obtained ', round(lr_acc, 6), '% accuracy for the logistic regression model')

print(metrics.classification_report(Y_test, lr_predict))
metrics.confusion_matrix(Y_test, lr_predict)

scores = cross_val_score(lr, data_transformed, df["label"], cv=5) # 5 fold cross validation
print("Confidence Interval for Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

svm = svm.SVC()
svm.fit(xtrain, Y_train)
svm_predict = svm.predict(xtest)
svm_acc = metrics.accuracy_score(Y_test, svm_predict)
print('We obtained ', round(svm_acc, 6), '% accuracy for the SVM model')

print(metrics.classification_report(Y_test, mnb_predict))

metrics.confusion_matrix(Y_test, lr_predict)

scores = cross_val_score(svm, data_transformed, df["label"], cv=5) # 5 fold cross validation
print("Confidence Interval for Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

from sklearn.neighbors import KNeighborsClassifier # k-NN ensemble method

knn = KNeighborsClassifier()
knn.fit(xtrain, Y_train)
knn_predict = knn.predict(xtest)

knn_acc = metrics.accuracy_score(Y_test, knn_predict)
print('We obtained ', round(knn_acc, 6), '% accuracy for the KNN Bagging model')

scores = cross_val_score(knn, data_transformed, df["label"], cv=5) # 5 fold cross validation
print("Confidence Interval for Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

from sklearn.ensemble import RandomForestClassifier # random forest ensemble method

ranfor = RandomForestClassifier(n_estimators=10, random_state=10)
ranfor = ranfor.fit(xtrain, Y_train)
rf_predict = ranfor.predict(xtest)
rf_acc = metrics.accuracy_score(Y_test, rf_predict)
print('We obtained ', round(rf_acc, 6), '% accuracy for the Random Forest model')

scores = cross_val_score(ranfor, data_transformed, df["label"], cv=5) # 5 fold cross validation
print("Confidence Interval for Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

myTable = pd.DataFrame(columns=['Naive Bayes','Support Vect Machine','Logistic Regression', 'K-NN', 'Random Forest'],
                   index=["Accuracy"])
myTable['Naive Bayes']=mnb_acc; myTable['Support Vect Machine']=svm_acc; myTable['Logistic Regression']=lr_acc
myTable['K-NN']= knn_acc; myTable['Random Forest']= rf_acc
myTable

# In[186]:

labels = list(myTable.columns.values)
values = myTable.iloc[0].values
ypos = np.arange(len(labels))

# In[187]:

myTable.iloc[0].values

# In[188]:

plt.bar(ypos, values, align='center', alpha=0.5)
plt.xticks(ypos, labels); plt.ylabel('% Accuracy')
plt.xlabel('Model Type'); plt.title('Accuracy of Classification Models')
plt.xticks(rotation=90); plt.show()

# ## 7 Final Remarks

# From the five different machine learning models attempted, Linear Support Vector Machine gave the lowest accuracy (45.3%) while Multinomial Logistic Regression gave the highest (65.5%). Upon first glance, 65% accuracy seems fairly low--however, considering that this was a multinomial classification task (Positive, Neutral or Negative), 65% is significantly higher than a random distribution. Furthermore, because the data was from YouTube comments rather than from a formal body of text, the corpus contained a higher frequency of spelling errors, slang, emojis, names, and foreign languages than would otherwise be expected, all of which added noise to the models. Future advancements may focus on better recognizing and processing these attributes common to social media textual data.


f = open(os.environ['OUTPUT_PATH'], 'w')

try:
    _substr = str(input())
except:
    _substr = None

res = getMovieTitles(_substr)
for res_cur in res:
    f.write( str(res_cur) + "\n" )

f.close()