"""The Console represents the engine running the game.

This can be Dolphin (Slippi's Ishiiruka) or an SLP file. The Console object
is your method to start and stop Dolphin, set configs, and get the latest GameState.
"""

from collections import defaultdict
from typing import Optional
from packaging import version

import time
import os
import stat
import configparser
import csv
import subprocess
import platform
import math
import base64
import numpy as np
from pathlib import Path
import shutil
import tempfile

from melee import enums
from melee.enums import Action
from melee.gamestate import GameState, Projectile, PlayerState
from melee.slippstream import SlippstreamClient, EventType
from melee.slpfilestreamer import SLPFileStreamer
from melee import stages

class SlippiVersionTooLow(Exception):
    """Raised when the Slippi version is not recent enough"""
    def __init__(self, message):
        self.message = message

class InvalidDolphinPath(Exception):
    """Raised when given path to Dolphin is invalid"""
    def __init__(self, message):
        self.message = message

def _ignore_fifos(src, names):
    fifos = []
    for name in names:
        path = os.path.join(src, name)
        if stat.S_ISFIFO(os.stat(path).st_mode):
            fifos.append(name)
    return fifos

def _copytree_safe(src, dst):
    shutil.copytree(src, dst, ignore=_ignore_fifos)

def _default_home_path(path: str) -> str:
    if platform.system() == "Darwin":
        return path + "/Contents/Resources/User/"

    # Next check if the home path is in the same dir as the exe
    user_path = path + "/User/"
    if os.path.isdir(user_path):
        return user_path

    # Otherwise, this must be an appimage install. Use the .config
    if platform.system() == "Linux":
        return str(Path.home()) + "/.config/SlippiOnline/"

    raise FileNotFoundError("Could not find dolphin home directory.")

# pylint: disable=too-many-instance-attributes
class Console:
    """The console object that represents your Dolphin / GameCube / SLP file
    """
    def __init__(self,
                 path=None,
                 system="dolphin",
                 dolphin_home_path=None,
                 tmp_home_directory=True,
                 copy_home_directory=True,
                 slippi_address="127.0.0.1",
                 slippi_port=51441,
                 online_delay=2,
                 blocking_input=False,
                 polling_mode=False,
                 allow_old_version=False,
                 logger=None,
                 setup_gecko_codes=True,
                 fullscreen=True,
                 gfx_backend="",
                 disable_audio=False,
                 overclock: Optional[float] = None,
                 save_replays=True,
                ):
        """Create a Console object

        Args:
            path (str): Path to the directory where your dolphin executable is located.
                If None, will assume the dolphin is remote and won't try to configure it.
            dolphin_home_path (str): Path to dolphin user directory. Optional.
            system (string): One of "dolphin", "file", or "gamecube"
            tmp_home_directory (bool): Use a temporary directory for the dolphin User path
                This is useful so instances don't interfere with each other.
            copy_home_directory (bool): Copy an existing home directory on the system.
                Unset to get a fresh directory that doesn't depend on system state.
            slippi_address (str): IP address of the Dolphin / gamecube to connect to.
            slippi_port (int): UDP port that slippi will listen on
            online_delay (int): How many frames of delay to apply in online matches
            blocking_input (bool): Should dolphin block waiting for bot input
                This is only really useful if you're doing ML training.
            polling_mode (bool): Polls input to console rather than blocking for it
                When set, step() will always return immediately, but may be None if no
                gamestate is available yet.
            allow_old_version (bool): Allow SLP versions older than 3.0.0 (rollback era)
                Only enable if you know what you're doing. You probably don't want this.
                Gamestates will be missing key information, come in really late, or possibly not work at all
            logger (logger.Logger): Logger instance to use. None for no logger.
            setup_gecko_codes (bool): Overwrites the user's GALE01r2.ini with libmelee's
                custom gecko codes. Should be used with tmp_home_directory.
            fullscreen (bool): Run melee fullscreen.
            gfx_backend (str): Graphics backend. Leave blank to use default.
            disable_audio (bool): Turn off sound.
            overclock (bool): Overclock the dolphin CPU.
            save_replays (bool): Save slippi replays.
        """
        self.logger = logger
        self.system = system
        self.path = path
        self.dolphin_home_path = dolphin_home_path
        self.temp_dir = None
        if tmp_home_directory and self.system =="dolphin":
            self.temp_dir = tempfile.mkdtemp(prefix='libmelee_')
            home_dir = self.temp_dir + "/User/"
            if copy_home_directory:
                _copytree_safe(self._get_dolphin_home_path(), home_dir)
            self.dolphin_home_path = home_dir

        self.processingtime = 0
        self._frametimestamp = time.time()
        self.slippi_address = slippi_address
        """(str): IP address of the Dolphin / gamecube to connect to."""
        self.slippi_port = slippi_port
        """(int): UDP port of slippi server. Default 51441"""
        self.eventsize = [0] * 0x100
        self.connected = False
        self.nick = ""
        """(str): The nickname the console has given itself."""
        self.version = ""
        """(str): The Slippi version of the console"""
        self.cursor = 0
        self.controllers = []
        self._current_stage = enums.Stage.NO_STAGE
        self._frame = 0
        self._polling_mode = polling_mode
        self.slp_version = "unknown"
        """(str): The SLP version this stream/file currently is."""
        self._allow_old_version = allow_old_version
        self._use_manual_bookends = False
        self._costumes = {0:0, 1:0, 2:0, 3:0}
        self._cpu_level = {0:0, 1:0, 2:0, 3:0}
        self._team_id = {0:0, 1:0, 2:0, 3:0}
        self._invuln_start = {1:(0,0), 2:(0,0), 3:(0,0), 4:(0,0)}
        self._is_teams = False

        self.setup_gecko_codes = setup_gecko_codes
        self.online_delay = online_delay
        self.blocking_input = blocking_input
        self.fullscreen = fullscreen
        self.gfx_backend = gfx_backend
        self.disable_audio = disable_audio
        self.overclock = overclock
        self.save_replays = save_replays

        # Keep a running copy of the last gamestate produced
        self._prev_gamestate = GameState()
        # Half-completed gamestate not yet ready to add to the list
        self._temp_gamestate = None
        self._process = None
        assert(self.system in ["dolphin", "gamecube", "file"])
        if self.system == "dolphin":
            self._slippstream = SlippstreamClient(self.slippi_address, self.slippi_port, True)
            if self.path:
                self._setup_home_directory()
        elif self.system == "gamecube":
            self._slippstream = SlippstreamClient(self.slippi_address, self.slippi_port, False)
        else:
            self._slippstream = SLPFileStreamer(self.path)

        # Prepare some structures for fixing melee data
        path = os.path.dirname(os.path.realpath(__file__))
        with open(path + "/actiondata.csv") as csvfile:
            #A list of dicts containing the frame data
            actiondata = list(csv.DictReader(csvfile))
            #Dict of sets
            self.zero_indices = defaultdict(set)
            for line in actiondata:
                if line["zeroindex"] == "True":
                    self.zero_indices[int(line["character"])].add(int(line["action"]))

        # Read the character data csv
        self.characterdata = dict()
        with open(path + "/characterdata.csv") as csvfile:
            reader = csv.DictReader(csvfile)
            for line in reader:
                del line["Character"]
                #Convert all fields to numbers
                for key, value in line.items():
                    line[key] = float(value)
                self.characterdata[enums.Character(line["CharacterIndex"])] = line

    def connect(self):
        """ Connects to the Slippi server (dolphin or gamecube).

        Returns:
            True is successful, False otherwise
        """
        return self._slippstream.connect()

    def _get_dolphin_home_path(self):
        """Return the path to dolphin's home directory"""
        if self.dolphin_home_path:
            return self.dolphin_home_path

        assert self.path, "Must specify a dolphin path."

        return _default_home_path(self.path)

    def _get_dolphin_config_path(self):
        """ Return the path to dolphin's config directory."""
        return self._get_dolphin_home_path() + "Config/"

    def get_dolphin_pipes_path(self, port):
        """Get the path of the named pipe input file for the given controller port
        """
        if platform.system() == "Windows":
            return '\\\\.\\pipe\\slippibot' + str(port)
        pipes_path = self._get_dolphin_home_path() + "/Pipes/"
        if not os.path.isdir(pipes_path):
            os.makedirs(pipes_path, exist_ok=True)
        return pipes_path + f"slippibot{port}"

    def run(self, iso_path=None, dolphin_user_path=None, environment_vars=None, exe_name=None):
        """Run the Dolphin emulator.

        This starts the Dolphin process, so don't run this if you're connecting to an
        already running Dolphin instance.

        Args:
            iso_path (str, optional): Path to Melee ISO for dolphin to read
            dolphin_user_path (str, optional): Alternative user path for dolphin
                if not using the default
            environment_vars (dict, optional): Dict (string->string) of environment variables to set
            exe_name (str, optional): Name of the dolphin executable.
        """
        assert self.system == "dolphin" and self.path

        dolphin_user_path = dolphin_user_path or self._get_dolphin_home_path()

        exe_name = exe_name or "dolphin-emu"
        if platform.system() == "Windows":
            exe_name = "Slippi Dolphin.exe"
        elif platform.system() == "Darwin":
            exe_name = "Slippi Dolphin"

        exe_path = ""
        if self.path:
            exe_path = self.path
        if platform.system() == "Darwin":
            exe_path += "/Contents/MacOS"
        command = [exe_path + "/" + exe_name]

        # AppImage
        if platform.system() == "Linux" and os.path.isfile(self.path):
            command = [self.path]

        if iso_path is not None:
            command.append("-e")
            command.append(iso_path)
        command.append("-u")
        command.append(dolphin_user_path)
        env = os.environ.copy()
        if environment_vars is not None:
            env.update(environment_vars)
        self._process = subprocess.Popen(command, env=env)

    def stop(self):
        """ Stop the console.

        For Dolphin instances, this will kill the dolphin process.
        For gamecubes and SLP files, it just shuts down our connection
         """
        if self.path:
            self.connected = False
            self._slippstream.shutdown()
            # If dolphin, kill the process
            if self._process is not None:
                self._process.terminate()
                self._process = None

        if self.temp_dir:
            shutil.rmtree(self.temp_dir)
            self.temp_dir = None

    def _setup_home_directory(self,):
        self._setup_dolphin_ini()
        if self.setup_gecko_codes:
            self._setup_gecko_codes()

    def _setup_dolphin_ini(self):
        # Setup some dolphin config options
        config_path = self._get_dolphin_config_path()
        os.makedirs(config_path, exist_ok=True)
        dolphin_ini_path = os.path.join(config_path, "Dolphin.ini")

        config = configparser.ConfigParser()
        if os.path.isfile(dolphin_ini_path):
            config.read(dolphin_ini_path)

        for section in ["Core", "Input", "Display", "DSP"]:
            if not config.has_section(section):
                config.add_section(section)
        config.set("Core", 'slippienablespectator', "True")
        config.set("Core", 'slippispectatorlocalport', str(self.slippi_port))
        # Set online delay
        config.set("Core", 'slippionlinedelay', str(self.online_delay))
        # Turn on background input so we don't need to have window focus on dolphin
        config.set("Input", 'backgroundinput', "True")
        config.set("Core", 'BlockingPipes', str(self.blocking_input))
        config.set("Core", "GFXBackend", self.gfx_backend)
        config.set("Display", "Fullscreen", str(self.fullscreen))
        if self.disable_audio:
            config.set("DSP", "Backend", "No audio output")

        if self.overclock:
            config.set("Core", "Overclock", str(self.overclock))
            config.set("Core", "OverclockEnable", "True")

        config.set("Core", "SlippiSaveReplays", str(self.save_replays))

        with open(dolphin_ini_path, 'w') as dolphinfile:
            config.write(dolphinfile)

    def _setup_gecko_codes(self):
        game_settings_path = os.path.join(self._get_dolphin_home_path(), 'GameSettings')
        os.makedirs(game_settings_path, exist_ok=True)

        libmelee_path = os.path.dirname(os.path.realpath(__file__))
        gale01r2_ini_path = os.path.join(libmelee_path, "GALE01r2.ini")

        shutil.copy(gale01r2_ini_path, game_settings_path)

    def setup_dolphin_controller(self, port, controllertype=enums.ControllerType.STANDARD):
        """Setup the necessary files for dolphin to recognize the player at the given
        controller port and type"""

        pipes_path = self.get_dolphin_pipes_path(port)
        if platform.system() != "Windows" and controllertype == enums.ControllerType.STANDARD:
            if not os.path.exists(pipes_path):
                os.mkfifo(pipes_path)

        #Read in dolphin's controller config file
        controller_config_path = self._get_dolphin_config_path() + "GCPadNew.ini"
        config = configparser.ConfigParser()
        config.read(controller_config_path)

        #Add a bot standard controller config to the given port
        section = "GCPad" + str(port)
        if not config.has_section(section):
            config.add_section(section)

        if controllertype == enums.ControllerType.STANDARD:
            config.set(section, 'Device', 'Pipe/0/slippibot' + str(port))
            config.set(section, 'Buttons/A', 'Button A')
            config.set(section, 'Buttons/B', 'Button B')
            config.set(section, 'Buttons/X', 'Button X')
            config.set(section, 'Buttons/Y', 'Button Y')
            config.set(section, 'Buttons/Z', 'Button Z')
            config.set(section, 'Buttons/L', 'Button L')
            config.set(section, 'Buttons/R', 'Button R')
            config.set(section, 'Buttons/Threshold', '50.00000000000000')
            config.set(section, 'Main Stick/Up', 'Axis MAIN Y +')
            config.set(section, 'Main Stick/Down', 'Axis MAIN Y -')
            config.set(section, 'Main Stick/Left', 'Axis MAIN X -')
            config.set(section, 'Main Stick/Right', 'Axis MAIN X +')
            config.set(section, 'Triggers/L', 'Button L')
            config.set(section, 'Triggers/R', 'Button R')
            config.set(section, 'Main Stick/Modifier', 'Shift_L')
            config.set(section, 'Main Stick/Modifier/Range', '50.000000000000000')
            config.set(section, 'Main Stick/Radius', '100.000000000000000')
            config.set(section, 'D-Pad/Up', 'Button D_UP')
            config.set(section, 'D-Pad/Down', 'Button D_DOWN')
            config.set(section, 'D-Pad/Left', 'Button D_LEFT')
            config.set(section, 'D-Pad/Right', 'Button D_RIGHT')
            config.set(section, 'Buttons/Start', 'Button START')
            config.set(section, 'Buttons/A', 'Button A')
            config.set(section, 'C-Stick/Up', 'Axis C Y +')
            config.set(section, 'C-Stick/Down', 'Axis C Y -')
            config.set(section, 'C-Stick/Left', 'Axis C X -')
            config.set(section, 'C-Stick/Right', 'Axis C X +')
            config.set(section, 'C-Stick/Radius', '100.000000000000000')
            config.set(section, 'Triggers/L-Analog', 'Axis L -+')
            config.set(section, 'Triggers/R-Analog', 'Axis R -+')
            config.set(section, 'Triggers/Threshold', '90.00000000000000')
        #This section is unused if it's not a standard input (I think...)
        else:
            config.set(section, 'Device', 'XInput2/0/Virtual core pointer')

        with open(controller_config_path, 'w') as configfile:
            config.write(configfile)

        dolphin_config_path = self._get_dolphin_config_path() + "Dolphin.ini"
        config = configparser.ConfigParser()
        config.read(dolphin_config_path)
        # Indexed at 0. "6" means standard controller, "12" means GCN Adapter
        #  The enum is scoped to the proper value, here
        config.set("Core", 'SIDevice'+str(port-1), controllertype.value)
        with open(dolphin_config_path, 'w') as dolphinfile:
            config.write(dolphinfile)

    def step(self):
        """ 'step' to the next state of the game and flushes all controllers

        Returns:
            GameState object that represents new current state of the game"""
        self.processingtime = time.time() - self._frametimestamp

        # Flush the controllers
        for controller in self.controllers:
            controller.flush()

        if self._temp_gamestate is None:
            self._temp_gamestate = GameState()

        frame_ended = False
        while not frame_ended:
            message = self._slippstream.dispatch(self._polling_mode)
            if message:
                if message["type"] == "connect_reply":
                    self.connected = True
                    self.nick = message["nick"]
                    self.version = message["version"]
                    self.cursor = message["cursor"]

                elif message["type"] == "game_event":
                    if len(message["payload"]) > 0:
                        if self.system == "dolphin":
                            frame_ended = self.__handle_slippstream_events(base64.b64decode(message["payload"]), self._temp_gamestate)
                        else:
                            frame_ended = self.__handle_slippstream_events(message["payload"], self._temp_gamestate)

                elif message["type"] == "menu_event":
                    if len(message["payload"]) > 0:
                        if self.system == "dolphin":
                            self.__handle_slippstream_menu_event(base64.b64decode(message["payload"]), self._temp_gamestate)
                        else:
                            self.__handle_slippstream_menu_event(message["payload"], self._temp_gamestate)
                        frame_ended = True

                elif self._use_manual_bookends and message["type"] == "frame_end" and self._frame != -10000:
                    frame_ended = True
            else:
                return None

        gamestate = self._temp_gamestate
        self._temp_gamestate = None
        self.__fixframeindexing(gamestate)
        self.__fixiasa(gamestate)
        # Insert some metadata into the gamestate
        gamestate.playedOn = self._slippstream.playedOn
        gamestate.startAt = self._slippstream.timestamp
        gamestate.consoleNick = self._slippstream.consoleNick
        for i, names in self._slippstream.players.items():
            try:
                gamestate.players[int(i)+1].nickName = names["names"]["netplay"]
            except KeyError:
                pass
            try:
                gamestate.players[int(i)+1].connectCode = names["names"]["code"]
            except KeyError:
                pass

        # Start the processing timer now that we're done reading messages
        self._frametimestamp = time.time()
        return gamestate

    def __handle_slippstream_events(self, event_bytes, gamestate):
        """ Handle a series of events, provided sequentially in a byte array """
        gamestate.menu_state = enums.Menu.IN_GAME
        while len(event_bytes) > 0:
            # A null message type means that the rest of the data is padding
            if event_bytes[0] == 0x00:
                return True
            event_size = self.eventsize[event_bytes[0]]
            if len(event_bytes) < event_size:
                print("WARNING: Something went wrong unpacking events. Data is probably missing")
                print("\tDidn't have enough data for event")
                return False
            if EventType(event_bytes[0]) == EventType.PAYLOADS:
                cursor = 0x2
                payload_size = event_bytes[1]
                num_commands = (payload_size - 1) // 3
                for i in range(0, num_commands):
                    command = np.ndarray((1,), ">B", event_bytes, cursor)[0]
                    command_len = np.ndarray((1,), ">H", event_bytes, cursor + 0x1)[0]
                    self.eventsize[command] = command_len+1
                    cursor += 3
                event_bytes = event_bytes[payload_size + 1:]

            elif EventType(event_bytes[0]) == EventType.FRAME_START:
                event_bytes = event_bytes[event_size:]

            elif EventType(event_bytes[0]) == EventType.GAME_START:
                self.__game_start(gamestate, event_bytes)
                event_bytes = event_bytes[event_size:]
                # The game needs to know what to press on the first frame of the game
                #   Just give it empty input. Characters are not actionable anyway.
                for controller in self.controllers:
                    controller.release_all()
                    controller.flush()

            elif EventType(event_bytes[0]) == EventType.GAME_END:
                event_bytes = event_bytes[event_size:]
                return self._use_manual_bookends

            elif EventType(event_bytes[0]) == EventType.PRE_FRAME:
                self.__pre_frame(gamestate, event_bytes)
                event_bytes = event_bytes[event_size:]

            elif EventType(event_bytes[0]) == EventType.POST_FRAME:
                self.__post_frame(gamestate, event_bytes)
                event_bytes = event_bytes[event_size:]

            elif EventType(event_bytes[0]) == EventType.GECKO_CODES:
                event_bytes = event_bytes[event_size:]

            elif EventType(event_bytes[0]) == EventType.FRAME_BOOKEND:
                self.__frame_bookend(gamestate, event_bytes)
                event_bytes = event_bytes[event_size:]
                # If this is an old frame, then don't return it.
                if gamestate.frame <= self._frame:
                    return False
                self._frame = gamestate.frame
                return True

            elif EventType(event_bytes[0]) == EventType.ITEM_UPDATE:
                self.__item_update(gamestate, event_bytes)
                event_bytes = event_bytes[event_size:]

            else:
                print("WARNING: Something went wrong unpacking events. " + \
                    "Data is probably missing")
                print("\tGot invalid event type: ", event_bytes[0])
                return False
        return False

    def __game_start(self, gamestate, event_bytes):
        self._frame = -10000
        major = np.ndarray((1,), ">B", event_bytes, 0x1)[0]
        minor = np.ndarray((1,), ">B", event_bytes, 0x2)[0]
        version_num = np.ndarray((1,), ">B", event_bytes, 0x3)[0]
        self.slp_version = str(major) + "." + str(minor) + "." + str(version_num)
        self._use_manual_bookends = self._allow_old_version and (version.parse(self.slp_version) < version.parse("3.0.0"))
        if major < 3 and not self._allow_old_version:
            raise SlippiVersionTooLow(self.slp_version)
        try:
            self._current_stage = enums.to_internal_stage(np.ndarray((1,), ">H", event_bytes, 0x13)[0])
        except ValueError:
            self._current_stage = enums.Stage.NO_STAGE

        self._is_teams = not (np.ndarray((1,), ">H", event_bytes, 0xD)[0] == 0)

        for i in range(4):
            self._costumes[i] = np.ndarray((1,), ">B", event_bytes, 0x68 + (0x24 * i))[0]

        for i in range(4):
            self._cpu_level[i] = np.ndarray((1,), ">B", event_bytes, 0x74 + (0x24 * i))[0]

        for i in range(4):
            self._team_id[i] = np.ndarray((1,), ">B", event_bytes, 0x6E + (0x24 * i))[0]

        for i in range(4):
            if np.ndarray((1,), ">B", event_bytes, 0x66 + (0x24 * i))[0] != 1:
                self._cpu_level[i] = 0

    def __pre_frame(self, gamestate, event_bytes):
        # Grab the physical controller state and put that into the controller state
        controller_port = np.ndarray((1,), ">B", event_bytes, 0x5)[0] + 1

        if controller_port not in gamestate.players:
            gamestate.players[controller_port] = PlayerState()
        playerstate = gamestate.players[controller_port]

        # Is this Nana?
        if np.ndarray((1,), ">B", event_bytes, 0x6)[0] == 1:
            playerstate.nana = PlayerState()
            playerstate = playerstate.nana

        playerstate.costume = self._costumes[controller_port-1]
        playerstate.cpu_level = self._cpu_level[controller_port-1]
        playerstate.team_id = self._team_id[controller_port-1]

        main_x = (np.ndarray((1,), ">f", event_bytes, 0x19)[0] / 2) + 0.5
        main_y = (np.ndarray((1,), ">f", event_bytes, 0x1D)[0] / 2) + 0.5
        playerstate.controller_state.main_stick = (main_x, main_y)

        c_x = (np.ndarray((1,), ">f", event_bytes, 0x21)[0] / 2) + 0.5
        c_y = (np.ndarray((1,), ">f", event_bytes, 0x25)[0] / 2) + 0.5
        playerstate.controller_state.c_stick = (c_x, c_y)

        # The game interprets both shoulders together, so the processed value will always be the same
        trigger = (np.ndarray((1,), ">f", event_bytes, 0x29)[0])
        playerstate.controller_state.l_shoulder = trigger
        playerstate.controller_state.r_shoulder = trigger

        buttonbits = np.ndarray((1,), ">H", event_bytes, 0x31)[0]
        playerstate.controller_state.button[enums.Button.BUTTON_A] = bool(int(buttonbits) & 0x0100)
        playerstate.controller_state.button[enums.Button.BUTTON_B] = bool(int(buttonbits) & 0x0200)
        playerstate.controller_state.button[enums.Button.BUTTON_X] = bool(int(buttonbits) & 0x0400)
        playerstate.controller_state.button[enums.Button.BUTTON_Y] = bool(int(buttonbits) & 0x0800)
        playerstate.controller_state.button[enums.Button.BUTTON_START] = bool(int(buttonbits) & 0x1000)
        playerstate.controller_state.button[enums.Button.BUTTON_Z] = bool(int(buttonbits) & 0x0010)
        playerstate.controller_state.button[enums.Button.BUTTON_R] = bool(int(buttonbits) & 0x0020)
        playerstate.controller_state.button[enums.Button.BUTTON_L] = bool(int(buttonbits) & 0x0040)
        playerstate.controller_state.button[enums.Button.BUTTON_D_LEFT] = bool(int(buttonbits) & 0x0001)
        playerstate.controller_state.button[enums.Button.BUTTON_D_RIGHT] = bool(int(buttonbits) & 0x0002)
        playerstate.controller_state.button[enums.Button.BUTTON_D_DOWN] = bool(int(buttonbits) & 0x0004)
        playerstate.controller_state.button[enums.Button.BUTTON_D_UP] = bool(int(buttonbits) & 0x0008)
        if self._use_manual_bookends:
            self._frame = gamestate.frame

    def __post_frame(self, gamestate, event_bytes):
        gamestate.stage = self._current_stage
        gamestate.is_teams = self._is_teams
        gamestate.frame = np.ndarray((1,), ">i", event_bytes, 0x1)[0]
        controller_port = np.ndarray((1,), ">B", event_bytes, 0x5)[0] + 1

        if controller_port not in gamestate.players:
            gamestate.players[controller_port] = PlayerState()
        playerstate = gamestate.players[controller_port]

        # Is this Nana?
        if np.ndarray((1,), ">B", event_bytes, 0x6)[0] == 1:
            playerstate.nana = PlayerState()
            playerstate = playerstate.nana

        playerstate.position.x = np.ndarray((1,), ">f", event_bytes, 0xa)[0]
        playerstate.position.y = np.ndarray((1,), ">f", event_bytes, 0xe)[0]

        playerstate.x = playerstate.position.x
        playerstate.y = playerstate.position.y

        playerstate.character = enums.Character(np.ndarray((1,), ">B", event_bytes, 0x7)[0])
        try:
            playerstate.action = enums.Action(np.ndarray((1,), ">H", event_bytes, 0x8)[0])
        except ValueError:
            playerstate.action = enums.Action.UNKNOWN_ANIMATION

        # Melee stores this in a float for no good reason. So we have to convert
        playerstate.facing = np.ndarray((1,), ">f", event_bytes, 0x12)[0] > 0

        playerstate.percent = int(np.ndarray((1,), ">f", event_bytes, 0x16)[0])
        playerstate.shield_strength = np.ndarray((1,), ">f", event_bytes, 0x1A)[0]
        playerstate.stock = np.ndarray((1,), ">B", event_bytes, 0x21)[0]
        playerstate.action_frame = int(np.ndarray((1,), ">f", event_bytes, 0x22)[0])

        try:
            sb4 = int(np.ndarray((1,), ">B", event_bytes, 0x29)[0])
            playerstate.is_powershield = (sb4 & 0x20) == 0x20
        except TypeError:
            playerstate.is_powershield = False

        try:
            playerstate.hitstun_frames_left = int(np.ndarray((1,), ">f", event_bytes, 0x2B)[0])
        except TypeError:
            playerstate.hitstun_frames_left = 0
        except ValueError:
            playerstate.hitstun_frames_left = 0
        try:
            playerstate.on_ground = not bool(np.ndarray((1,), ">B", event_bytes, 0x2F)[0])
        except TypeError:
            playerstate.on_ground = True
        try:
            playerstate.jumps_left = np.ndarray((1,), ">B", event_bytes, 0x32)[0]
        except TypeError:
            playerstate.jumps_left = 1

        try:
            playerstate.invulnerable = int(np.ndarray((1,), ">B", event_bytes, 0x34)[0]) != 0
        except TypeError:
            playerstate.invulnerable = False

        try:
            playerstate.speed_air_x_self = np.ndarray((1,), ">f", event_bytes, 0x35)[0]
        except TypeError:
            playerstate.speed_air_x_self = 0

        try:
            playerstate.speed_y_self = np.ndarray((1,), ">f", event_bytes, 0x39)[0]
        except TypeError:
            playerstate.speed_y_self = 0

        try:
            playerstate.speed_x_attack = np.ndarray((1,), ">f", event_bytes, 0x3D)[0]
        except TypeError:
            playerstate.speed_x_attack = 0

        try:
            playerstate.speed_y_attack = np.ndarray((1,), ">f", event_bytes, 0x41)[0]
        except TypeError:
            playerstate.speed_y_attack = 0

        try:
            playerstate.speed_ground_x_self = np.ndarray((1,), ">f", event_bytes, 0x45)[0]
        except TypeError:
            playerstate.speed_ground_x_self = 0

        try:
            playerstate.hitlag_left = int(np.ndarray((1,), ">f", event_bytes, 0x49)[0])
        except TypeError:
            playerstate.hitlag_left = 0

        # Keep track of a player's invulnerability due to respawn or ledge grab
        if controller_port in self._prev_gamestate.players:
            playerstate.invulnerability_left = max(0, self._invuln_start[controller_port][1] - (gamestate.frame - self._invuln_start[controller_port][0]))
        if playerstate.action == Action.ON_HALO_WAIT:
            playerstate.invulnerability_left = 120
            self._invuln_start[controller_port] = (gamestate.frame, 120)
        # Don't give invulnerability to the first descent
        if playerstate.action == Action.ON_HALO_DESCENT and gamestate.frame > 150:
            playerstate.invulnerability_left = 120
            self._invuln_start[controller_port] = (gamestate.frame, 120)
        if playerstate.action == Action.EDGE_CATCHING and playerstate.action_frame == 1:
            playerstate.invulnerability_left = 36
            self._invuln_start[controller_port] = (gamestate.frame, 36)
        # First frame of the game
        if gamestate.frame == -123:
            playerstate.invulnerability_left = 0
            self._invuln_start[controller_port] = (gamestate.frame, 0)

        if playerstate.invulnerability_left > 0:
            playerstate.invulnerable = True

        # The pre-warning occurs when we first start a dash dance.
        if controller_port in self._prev_gamestate.players:
            if playerstate.action == Action.DASHING and \
                    self._prev_gamestate.players[controller_port].action not in [Action.DASHING, Action.TURNING]:
                playerstate.moonwalkwarning = True

        # Take off the warning if the player does an action other than dashing
        if playerstate.action != Action.DASHING:
            playerstate.moonwalkwarning = False

        # "off_stage" helper
        try:
            if (abs(playerstate.position.x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \
                    playerstate.y < -6) and not playerstate.on_ground:
                playerstate.off_stage = True
            else:
                playerstate.off_stage = False
        except KeyError:
            playerstate.off_stage = False

        # ECB top edge, x
        ecb_top_x = 0
        ecb_top_y = 0
        try:
            ecb_top_x = np.ndarray((1,), ">f", event_bytes, 0x51)[0]
        except TypeError:
            ecb_top_x = 0
        # ECB Top edge, y
        try:
            ecb_top_y = np.ndarray((1,), ">f", event_bytes, 0x55)[0]
        except TypeError:
            ecb_top_y = 0
        playerstate.ecb.top.x = ecb_top_x
        playerstate.ecb.top.y = ecb_top_y
        playerstate.ecb_top = (ecb_top_x, ecb_top_y)

        # ECB bottom edge, x coord
        ecb_bot_x = 0
        ecb_bot_y = 0
        try:
            ecb_bot_x = np.ndarray((1,), ">f", event_bytes, 0x59)[0]
        except TypeError:
            ecb_bot_x = 0
        # ECB Bottom edge, y coord
        try:
            ecb_bot_y = np.ndarray((1,), ">f", event_bytes, 0x5D)[0]
        except TypeError:
            ecb_bot_y = 0
        playerstate.ecb.bottom.x = ecb_bot_x
        playerstate.ecb.bottom.y = ecb_bot_y
        playerstate.ecb_bottom = (ecb_bot_x, ecb_bot_y)

        # ECB left edge, x coord
        ecb_left_x = 0
        ecb_left_y = 0
        try:
            ecb_left_x = np.ndarray((1,), ">f", event_bytes, 0x61)[0]
        except TypeError:
            ecb_left_x = 0
        # ECB left edge, y coord
        try:
            ecb_left_y = np.ndarray((1,), ">f", event_bytes, 0x65)[0]
        except TypeError:
            ecb_left_y = 0
        playerstate.ecb.left.x = ecb_left_x
        playerstate.ecb.left.y = ecb_left_y
        playerstate.ecb_left = (ecb_left_x, ecb_left_y)

        # ECB right edge, x coord
        ecb_right_x = 0
        ecb_right_y = 0
        try:
            ecb_right_x = np.ndarray((1,), ">f", event_bytes, 0x69)[0]
        except TypeError:
            ecb_right_x = 0
        # ECB right edge, y coord
        try:
            ecb_right_y = np.ndarray((1,), ">f", event_bytes, 0x6D)[0]
        except TypeError:
            ecb_right_y = 0
        playerstate.ecb.right.x = ecb_right_x
        playerstate.ecb.right.y = ecb_right_y
        playerstate.ecb_right = (ecb_right_x, ecb_right_y)
        if self._use_manual_bookends:
            self._frame = gamestate.frame

        # FoD platform heights
        try:
            gamestate._fod_platform_left = np.ndarray((1,), ">f", event_bytes, 0x71)[0]
        except TypeError:
            gamestate._fod_platform_left = 0
        try:
            gamestate._fod_platform_right = np.ndarray((1,), ">f", event_bytes, 0x75)[0]
        except TypeError:
            gamestate._fod_platform_right = 0

    def __frame_bookend(self, gamestate, event_bytes):
        self._prev_gamestate = gamestate
        # Calculate helper distance variable
        #   This is a bit kludgey.... :/
        i = 0
        player_one_x, player_one_y, player_two_x, player_two_y = 0, 0, 0, 0
        for _, player_state in gamestate.players.items():
            if i == 0:
                player_one_x, player_one_y = player_state.position.x, player_state.position.y
            if i == 1:
                player_two_x, player_two_y = player_state.position.x, player_state.position.y
            i += 1
        xdist = player_one_x - player_two_x
        ydist = player_one_y - player_two_y
        gamestate.distance = math.sqrt((xdist**2) + (ydist**2))

    def __item_update(self, gamestate, event_bytes):
        projectile = Projectile()
        projectile.position.x = np.ndarray((1,), ">f", event_bytes, 0x14)[0]
        projectile.position.y = np.ndarray((1,), ">f", event_bytes, 0x18)[0]
        projectile.x = projectile.position.x
        projectile.y = projectile.position.y
        projectile.speed.x = np.ndarray((1,), ">f", event_bytes, 0xc)[0]
        projectile.speed.y = np.ndarray((1,), ">f", event_bytes, 0x10)[0]
        projectile.x_speed = projectile.speed.x
        projectile.y_speed = projectile.speed.y
        try:
            projectile.owner = np.ndarray((1,), ">B", event_bytes, 0x2A)[0] + 1
            if projectile.owner > 4:
                projectile.owner = -1
        except TypeError:
            projectile.owner = -1
        try:
            projectile.type = enums.ProjectileType(np.ndarray((1,), ">H", event_bytes, 0x5)[0])
        except ValueError:
            projectile.type = enums.ProjectileType.UNKNOWN_PROJECTILE

        try:
            projectile.frame = int(np.ndarray((1,), ">f", event_bytes, 0x1E)[0])
        except ValueError:
            projectile.frame = -1

        projectile.subtype = np.ndarray((1,), ">B", event_bytes, 0x7)[0]

        # Ignore exploded Samus bombs. They are subtype 3
        if projectile.type == enums.ProjectileType.SAMUS_BOMB and projectile.subtype == 3:
            return
        # Ignore exploded Samus missles
        if projectile.type == enums.ProjectileType.SAMUS_MISSLE and projectile.subtype in [2, 3]:
            return
        # Ignore Samus charge beam while charging (not firing)
        if projectile.type == enums.ProjectileType.SAMUS_CHARGE_BEAM and projectile.subtype == 0:
            return

        # Add the projectile to the gamestate list
        gamestate.projectiles.append(projectile)

    def __handle_slippstream_menu_event(self, event_bytes, gamestate):
        """ Internal handler for slippstream menu events

        Modifies specified gamestate based on the event bytes
         """
        scene = np.ndarray((1,), ">H", event_bytes, 0x1)[0]
        if scene == 0x02:
            gamestate.menu_state = enums.Menu.CHARACTER_SELECT
            # All the controller ports are active on this screen
            gamestate.players[1] = PlayerState()
            gamestate.players[2] = PlayerState()
            gamestate.players[3] = PlayerState()
            gamestate.players[4] = PlayerState()
        elif scene in [0x0102, 0x0108]:
            gamestate.menu_state = enums.Menu.STAGE_SELECT
            gamestate.players[1] = PlayerState()
            gamestate.players[2] = PlayerState()
            gamestate.players[3] = PlayerState()
            gamestate.players[4] = PlayerState()
        elif scene == 0x0202:
            gamestate.menu_state = enums.Menu.IN_GAME
        elif scene == 0x0001:
            gamestate.menu_state = enums.Menu.MAIN_MENU
        elif scene == 0x0008:
            gamestate.menu_state = enums.Menu.SLIPPI_ONLINE_CSS
            gamestate.players[1] = PlayerState()
            gamestate.players[2] = PlayerState()
            gamestate.players[3] = PlayerState()
            gamestate.players[4] = PlayerState()
        elif scene == 0x0000:
            gamestate.menu_state = enums.Menu.PRESS_START
        elif scene == 0x0402:
            gamestate.menu_state = enums.Menu.POSTGAME_SCORES
        else:
            gamestate.menu_state = enums.Menu.UNKNOWN_MENU

        # controller port statuses at CSS
        if gamestate.menu_state in [enums.Menu.CHARACTER_SELECT, enums.Menu.SLIPPI_ONLINE_CSS]:
            gamestate.players[1].controller_status = enums.ControllerStatus(np.ndarray((1,), ">B", event_bytes, 0x25)[0])
            gamestate.players[2].controller_status = enums.ControllerStatus(np.ndarray((1,), ">B", event_bytes, 0x26)[0])
            gamestate.players[3].controller_status = enums.ControllerStatus(np.ndarray((1,), ">B", event_bytes, 0x27)[0])
            gamestate.players[4].controller_status = enums.ControllerStatus(np.ndarray((1,), ">B", event_bytes, 0x28)[0])

            # CSS Cursors
            gamestate.players[1].cursor_x = np.ndarray((1,), ">f", event_bytes, 0x3)[0]
            gamestate.players[1].cursor_y = np.ndarray((1,), ">f", event_bytes, 0x7)[0]
            gamestate.players[2].cursor_x = np.ndarray((1,), ">f", event_bytes, 0xB)[0]
            gamestate.players[2].cursor_y = np.ndarray((1,), ">f", event_bytes, 0xF)[0]
            gamestate.players[3].cursor_x = np.ndarray((1,), ">f", event_bytes, 0x13)[0]
            gamestate.players[3].cursor_y = np.ndarray((1,), ">f", event_bytes, 0x17)[0]
            gamestate.players[4].cursor_x = np.ndarray((1,), ">f", event_bytes, 0x1B)[0]
            gamestate.players[4].cursor_y = np.ndarray((1,), ">f", event_bytes, 0x1F)[0]

            # Ready to fight banner
            gamestate.ready_to_start = np.ndarray((1,), ">B", event_bytes, 0x23)[0]

            # Character selected
            try:
                gamestate.players[1].character = enums.to_internal(np.ndarray((1,), ">B", event_bytes, 0x29)[0])
                gamestate.players[1].character_selected = gamestate.players[1].character
            except TypeError:
                gamestate.players[1].character = enums.Character.UNKNOWN_CHARACTER
                gamestate.players[1].character_selected = enums.Character.UNKNOWN_CHARACTER
            try:
                gamestate.players[2].character = enums.to_internal(np.ndarray((1,), ">B", event_bytes, 0x2A)[0])
                gamestate.players[2].character_selected = gamestate.players[2].character
            except TypeError:
                gamestate.players[2].character = enums.Character.UNKNOWN_CHARACTER
                gamestate.players[2].character_selected = enums.Character.UNKNOWN_CHARACTER
            try:
                gamestate.players[3].character = enums.to_internal(np.ndarray((1,), ">B", event_bytes, 0x2B)[0])
                gamestate.players[3].character_selected = gamestate.players[3].character

            except TypeError:
                gamestate.players[3].character = enums.Character.UNKNOWN_CHARACTER
                gamestate.players[3].character_selected = enums.Character.UNKNOWN_CHARACTER
            try:
                gamestate.players[4].character = enums.to_internal(np.ndarray((1,), ">B", event_bytes, 0x2C)[0])
                gamestate.players[4].character_selected = gamestate.players[4].character
            except TypeError:
                gamestate.players[4].character = enums.Character.UNKNOWN_CHARACTER
                gamestate.players[4].character_selected = enums.Character.UNKNOWN_CHARACTER

            # Coin down
            try:
                gamestate.players[1].coin_down = np.ndarray((1,), ">B", event_bytes, 0x2D)[0] == 2
            except TypeError:
                gamestate.players[1].coin_down = False
            try:
                gamestate.players[2].coin_down = np.ndarray((1,), ">B", event_bytes, 0x2E)[0] == 2
            except TypeError:
                gamestate.players[2].coin_down = False
            try:
                gamestate.players[3].coin_down = np.ndarray((1,), ">B", event_bytes, 0x2F)[0] == 2
            except TypeError:
                gamestate.players[3].coin_down = False
            try:
                gamestate.players[4].coin_down = np.ndarray((1,), ">B", event_bytes, 0x30)[0] == 2
            except TypeError:
                gamestate.players[4].coin_down = False

        if gamestate.menu_state == enums.Menu.STAGE_SELECT:
            # Stage
            try:
                gamestate.stage = enums.Stage(np.ndarray((1,), ">B", event_bytes, 0x24)[0])
            except ValueError:
                gamestate.stage = enums.Stage.NO_STAGE

            # Stage Select Cursor X, Y
            for _, player in gamestate.players.items():
                player.cursor.x = np.ndarray((1,), ">f", event_bytes, 0x31)[0]
                player.cursor.y = np.ndarray((1,), ">f", event_bytes, 0x35)[0]
                gamestate.stage_select_cursor_x = player.cursor.x
                gamestate.stage_select_cursor_y = player.cursor.y

        # Frame count
        gamestate.frame = np.ndarray((1,), ">i", event_bytes, 0x39)[0]

        # Sub-menu
        try:
            gamestate.submenu = enums.SubMenu(np.ndarray((1,), ">B", event_bytes, 0x3D)[0])
        except TypeError:
            gamestate.submenu = enums.SubMenu.UNKNOWN_SUBMENU
        except ValueError:
            gamestate.submenu = enums.SubMenu.UNKNOWN_SUBMENU

        # Selected menu
        try:
            gamestate.menu_selection = np.ndarray((1,), ">B", event_bytes, 0x3E)[0]
        except TypeError:
            gamestate.menu_selection = 0

        # Online costume chosen
        try:
            if gamestate.menu_state == enums.Menu.SLIPPI_ONLINE_CSS:
                for i in range(4):
                    gamestate.players[i+1].costume = np.ndarray((1,), ">B", event_bytes, 0x3F)[0]
        except TypeError:
            pass

        # This value is 0x05 in the nametag entry
        try:
            if gamestate.menu_state == enums.Menu.SLIPPI_ONLINE_CSS:
                nametag = np.ndarray((1,), ">B", event_bytes, 0x40)[0]
                if nametag == 0x05:
                    gamestate.submenu = enums.SubMenu.NAME_ENTRY_SUBMENU
                elif nametag == 0x00:
                    gamestate.submenu = enums.SubMenu.ONLINE_CSS
        except TypeError:
            pass

        # CPU Level
        try:
            for i in range(4):
                gamestate.players[i+1].cpu_level = np.ndarray((1,), ">B", event_bytes, 0x41 + i)[0]
        except TypeError:
            pass
        except KeyError:
            pass

        # Is Holding CPU Slider
        try:
            for i in range(4):
                gamestate.players[i+1].is_holding_cpu_slider = np.ndarray((1,), ">B", event_bytes, 0x45 + i)[0]
        except TypeError:
            pass
        except KeyError:
            pass

        # Set CPU level to 0 if we're not a CPU
        for port in gamestate.players:
            if gamestate.players[port].controller_status != enums.ControllerStatus.CONTROLLER_CPU:
                gamestate.players[port].cpu_level = 0

    def __fixframeindexing(self, gamestate):
        """ Melee's indexing of action frames is wildly inconsistent.
            Here we adjust all of the frames to be indexed at 1 (so math is easier)"""
        for _, player in gamestate.players.items():
            if player.action.value in self.zero_indices[player.character.value]:
                player.action_frame = player.action_frame + 1

    def __fixiasa(self, gamestate):
        """ The IASA flag doesn't set or reset for special attacks.
            So let's just set IASA to False for all non-A attacks.
        """
        for _, player in gamestate.players.items():
            # Luckily for us, all the A-attacks are in a contiguous place in the enums!
            #   So we don't need to call them out one by one
            if player.action.value < Action.NEUTRAL_ATTACK_1.value or player.action.value > Action.DAIR.value:
                player.iasa = False


#1 Import dependencies
import speech_recognition as sr

#2 Call function recognizer() from SpeechRecognition
r = sr.Recognizer()

#3 Use and Listen from microphone as source
with sr.Microphone() as source:
	#4 Indication to start your speech, you can change the text as your preference
	print ('Say something')
	#5 Store the audio in variable
	audio = r.listen(source)

try:
	#6 Use recognize_google function to print audio to text
	print('Google thinks you said: \n' + r.recognize_google(audio))

	#7 Exception
except Exception as Ex:
	print(Ex)


import sys
from Helpers.instances import InstanceFactory

class UnlockHelper():

    def __init__(self):
        self.api = InstanceFactory.getInstance().get_fusion_instance()
        pass

    def do_unlock(self):
        itp = InstanceFactory.getInstance().get_itp_instance()
        part_id = self.extract_part_id(itp)
        if '0000000000000000' not in part_id:
            print("Performing iSeed Unlock!!!")
            iseed_hex = self.api.get_iseed_key('PVC',"0x{}".format(part_id),'PVC_RED_PROD',"U1.U1")
            iseed_key = iseed_hex[2:]
            if not self.do_iseed_unlock(iseed_key, itp):
                raise ("Failed to unlock the part.")
        else:
            do_metal_unlock()

    def do_metal_unlock(self):
        print("Executing Metal Unlock")
        sys.path.append(r'C:\STHI\Fusion\ptm')
        import pontem as m
        m.unlock()
        itp = InstanceFactory.getInstance().get_itp_instance()
        print("Part is unlocked ? {}".format(itp.isunlocked('PVC_CLTAP0')))
        if not itp.isunlocked('PVC_CLTAP0'):
            raise("Failed Metal unlock.")
        pass

    def do_iseed_unlock(self, iseed_key, itp):
        print("Executing Iseed unlock")
        sv = InstanceFactory.getInstance().get_python_sv_instance()
        tap = itp.devs.pvc_agg0
        tap.irdrscan(0x30,32)

        ##Shift in the passcode to the Payload registers.
        tap.irdrscan(0x50,32,int("0x{}".format(iseed_key[24:32]),16))
        tap.irdrscan(0x54,32,int("0x{}".format(iseed_key[16:24]),16))
        tap.irdrscan(0x58,32,int("0x{}".format(iseed_key[8:16]),16))
        tap.irdrscan(0x5C,32,int("0x{}".format(iseed_key[0:8]),16))

        personality = 0

        ##Set tap_ctrl to perform the unlock.
        tap.irdrscan(0x40, 32, personality);##Write the pc_pers_select=tap_ctrl[6:4]
        tap.irdrscan(0x40, 32, 0x8)

        ##Check if the status_0 reflects it.
        tap.irdrscan(0x30,32)##Check policy_output=status_0[23:20]==0x4(Intel Unlocked)
        print("Policy output is :{}".format(sv.gfxcard0.tile0.taps.pvc_agg0.status_0.policy_output))
        return sv.gfxcard0.tile0.taps.pvc_agg0.status_0.policy_output == 0x4

    def extract_part_id(self, itp):
        tap = itp.devs.pvc_agg0
        unique_id_63_32 = tap.irdrscan(0x74,32)
        unique_id_31_0 = tap.irdrscan(0x70,32)
        unique_part_id = str(unique_id_63_32)[8:] + str(unique_id_31_0)[8:]
        return unique_part_id


from __future__ import print_function

import os
import sys
import click
import logging
import yaml

# from gevent import server as gserver  # pylint: disable=F401
# from gevent import socket  # pylint: disable=F401

import jitfs

from jitfs.backend import local

_LOGGER = logging.getLogger(__name__)

logging.basicConfig(
    format='%(asctime)s %(levelname)s: %(message)s',
    level=logging.DEBUG
)

def make_local_provider(cache_db):
    return local.JitfsLocalBackend(cache_db)

_PROVIDERS = {
    'local': make_local_provider,
}

@click.group()
def run():
    pass

@run.group(chain=True)
def backend():
    """Manage jitfs backend configuration."""
    pass

@backend.resultcallback()
def backend_resultclbk(configs):
    """Output combined backend configuration."""
    combined = {}
    for config in configs:
        combined.update(config)
    yaml.dump(combined,
              stream=sys.stdout,
              default_flow_style=False,
              explicit_start=True,
              explicit_end=False)

@backend.command(name='local')
@click.option('--cache-db', required=True,
              help='Path to cache database.')
def local_cmd(cache_db):
    """Local provider configuration."""
    return {'local': {'cache_db': cache_db}}

@run.command()
@click.option('--cache', help='Path to cache.',
              required=True)
@click.option('--backend', help='Jitfs backend plugin',
              required=True,
              type=click.Choice(_PROVIDERS))
@click.option('--backend-config', help='Jitfs backend configuration',
              type=click.File(mode='r'))
@click.argument('socket_path')
def server(cache, backend, backend_config, socket_path):
    """Run jitfs server."""
    _LOGGER.info('Starting server: cache: %s, socket: %s',
                 cache, socket_path)
    config = yaml.load(backend_config.read())
    kwargs = config.get(backend, {})

    provider = _PROVIDERS[backend](**kwargs)
    jitfs.run_server(cache, socket_path, provider)

@run.command()
@click.argument('socket_path')
@click.argument('cache')
@click.argument('checksum')
def request(socket_path, cache, checksum):
    """Request checksum."""

    part1 = checksum[0:2]
    part2 = checksum[2:4]
    cache_path = os.path.join(cache, part1, part2, checksum)

    if os.path.exists(cache_path):
        _LOGGER.info('Cache file exists: %s', cache_path)
        return

    jitfs_client = jitfs.JitfsClient(socket_path)
    jitfs_client.request(checksum)

    if os.path.exists(cache_path):
        _LOGGER.info('Cache fetched successdully: %s, %s',
                     cache_path, os.path.exists(cache_path))

@run.command()
@click.option('--backend', help='Jitfs backend plugin',
              required=True,
              type=click.Choice(_PROVIDERS))
@click.option('--backend-config', help='Jitfs backend configuration',
              type=click.File(mode='r'))
@click.option('--mirror', help='Path to mirror directory.',
              required=True)
@click.option('--mirror-db', help='Path to mirror db.',
              required=True)
@click.option('--rel-path', help='Use relative path for jitfs symlinks.',
              is_flag=True, default=False)
@click.option('--root', default='/')
@click.argument('source')
def checkout(backend, backend_config, mirror, mirror_db, rel_path, root,
             source):
    """Checkout local directory."""
    kwargs = yaml.load(backend_config.read()).get(backend, {})
    provider = _PROVIDERS[backend](**kwargs)

    jitfs.checkout(provider, mirror, mirror_db, root, source, rel_path)


"""
Given an unsorted array of integers, find the length of longest continuous increasing subsequence (subarray).

Example 1:
Input: [1,3,5,4,7]
Output: 3
Explanation: The longest continuous increasing subsequence is [1,3,5], its length is 3.
Even though [1,3,5,7] is also an increasing subsequence, it's not a continuous one where 5 and 7 are separated by 4.
Example 2:
Input: [2,2,2,2,2]
Output: 1
Explanation: The longest continuous increasing subsequence is [2], its length is 1.
Note: Length of the array will not exceed 10,000.
"""

class Solution(object):
    def findLengthOfLCIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums or len(nums) == 1:
            return nums
        max_count = 0
        first = 0
        last = 0
        while last != len(nums):
            if last == 0:
                max_count += 1
            elif nums[last] > nums[last - 1]:
                max_count = max(max_count, last - first + 1)
            else:
                max_count = 1
                first = last
            last += 1
        return max_count

class Solution(object):
    def findLengthOfLCIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        max_length = 0
        increasing_subsequence_start_index = 0
        current_index = 0
        while current_index < len(nums):
            if current_index == len(nums) - 1:
                max_length = max(max_length, current_index - increasing_subsequence_start_index + 1)
            elif nums[current_index] >= nums[current_index + 1]:
                max_length = max(max_length, current_index - increasing_subsequence_start_index + 1)
                increasing_subsequence_start_index = current_index + 1
            current_index += 1
        return max_length

674. Longest Continuous Increasing Subsequence.py


from scipy.special import gamma, gammasgn

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 50000)

plt.plot(x, gamma(x), label=r"$\gamma(x)$")

#plt.plot(x, gammasgn(x)*50, label=r"$sign(\gamma(x))$")

plt.grid(True)
plt.xlim(-5, 5)
plt.ylim(-100, 100)
plt.xlabel(r"$x$")
plt.ylabel(r"$\gamma(x)$")
plt.legend(loc="lower right")
plt.title(r"Gamma functions $\gamma$ : $\gamma_{(n+1)} = n!$")

#plt.show()

plt.savefig('gamma.pdf')


"""
Task is to download video from s3 bucket save to /tmp foldder of lambda,
using opwncv-python change codec of video and then,
upload back to s3 bucket
"""
import json
import boto3
import os
import cv2

s3 = boto3.client('s3')

def lambda_handler(event, context):
    download_bucket = 'darshbucketforcv'
    upload_bucket = 'darshmlaicv'
    key = 'sample_one.mp4'

    try:
        s3.download_file(download_bucket, key, '/tmp/sample.mp4')

        cap = cv2.VideoCapture('/tmp/sample.mp4')

        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter('/tmp/new_video.mov', fourcc, 30.0, (int(cap.get(3)), int(cap.get(4))))
        while(cap.isOpened()):
            ret, frame = cap.read()
            if ret==True:
                out.write(frame)
            else:
                break
        cap.release()
        out.release()

        s3.upload_file('/tmp/new_video.mov', upload_bucket, 'new_video.mov')

        response = {
            'code': 200,
            'status': True,
            'message': "Video Downloaded and Uploaded successfully",
            'data': None
            }

    except Exception as e:
        response = {
            'code': 500,
            'status': False,
            'message': e,
            'data': None
            }

    return response


from __future__ import unicode_literals

from subprocess import Popen, PIPE, STDOUT
import re
import datetime
import os

from pyunpack import Archive

# use archive file
class ArchiveFile:

    # init filename
    def __init__(self, filename, extract_dir):
        self.filename = filename
        self.extract_dir = extract_dir

    # get type archive
    def type(self):
        out = Popen('7za l {0}'.format(self.filename),
                    shell=True,
                    stdout=PIPE,
                    stderr=STDOUT)
        text = out.stdout.read()
        pattern = re.compile('Type\s\=\s(\S+)\s')
        match = re.search(pattern, text)
        if match:
            _type = match.group(1)
            if _type == '7z':
                solid = re.search('Solid\s\=\s(\S)\s', text)
                if solid.group(1) == '+':
                    return '7z'
                else:
                    return 'tar7z'
            return _type
        return 'file_obj'

    # get prepare list
    def prepare(self):
        out = Popen('7za l {0}'.format(self.filename),
                    shell=True,
                    stdout=PIPE,
                    stderr=STDOUT)
        lines = out.stdout.readlines()
        string = lines[len(lines) - 1]
        pattern = re.compile('\s*(\d+)\s*(\d+)\s*(\d+) files, (\d+) folders')
        result = re.search(pattern, string)
        if result:
            return {'folders': int(result.group(4)),
                    'files': int(result.group(3)),
                    'size': int(result.group(1))}
        else:
            return {'folders': 0,
                    'files': 0,
                    'size': 0}

    # get list dir
    def list_dir(self, path):
        return os.listdir(path)

    # extract files
    def extract(self):
        folder = datetime.datetime.now().isoformat()
        path = '{0}{1}/'.format(EXTRACT_DIR, folder)
        if not os.path.exists(path):
            os.makedirs(path)

        try:
            folder = datetime.datetime.now().isoformat()
            path = '{0}{1}/'.format(EXTRACT_DIR, folder)
            if not os.path.exists(path):
                os.makedirs(path)

            Archive(self.filename).extractall(path)
            result = []
            for file in self.list_dir(path):
                if file != '__MACOSX':
                    result.append({'orig_name': str(file),
                                   'result_link': '{0}{1}'.format(path, file),
                                   'folder': folder,
                                   'filename': file})
            return result
        except:
            return False


import sys
pos = list(map(int, input().strip().split()))
actions = input().strip()
for act in actions:
    if act == 'U':
        pos[1] += 1
    elif act == 'D':
        pos[1] -= 1
    elif act == 'L':
        pos[0] -= 1
    elif act == 'R':
        pos[0] += 1
sys.stdout.write(" ".join(map(str, pos)))


#!/usr/bin/python3
def add_tuple(tuple_a=(), tuple_b=()):
    # If tuple_a or tuple_b has less than 2 elements,
    # add 0s to make it a tuple of length 2
    if len(tuple_a) < 2:
        tuple_a += (0,) * (2 - len(tuple_a))
    if len(tuple_b) < 2:
        tuple_b += (0,) * (2 - len(tuple_b))

    # Extract the first two elements of the tuples
    a1, a2 = tuple_a[:2]
    b1, b2 = tuple_b[:2]

    # Add the first elements and second elements separately
    result = (a1 + b1, a2 + b2)

    return result


#n_testcases = int(input())
n_testcases=1
output=[]

def count_right(game,player):
    #count consecutive Alphabets of player on right side
    count=0
    while len(game)>0 and game[-1]==player:
        game=game[:-1]
        count+=1
    return count

def count_left(game,player):
    #count consecutive Alphabets of player on left side
    count=0
    while len(game)>0 and game[0]==player:
        game=game[1:]
        count+=1
    return count

for t in range(n_testcases):
    #game=list(input())
    game=['I','O','I','O','O','O','I','I']
    original_game_length= len(game)
    turn = 0
    Score_I,Score_O=0,0

    #game loop
    while len(game)>0:
        #determine player
        player = 'I' if turn%2==0 else 'O'

        #game snapshot
        print(game, player)

        #compare consecutive letters from Left and right side
        R = count_right(game, player)
        L = count_left(game,player)

        if (L==0 and R==0) :
            len_game=len(game)

            if player=='O':
                output.append("Case #{}: {} {}".format((t+1), "I", Score_I+len_game))
            if player=='I':
                output.append("Case #{}: {} {}".format((t+1), "O", Score_O+len_game))
            break

        if R>0  :
            game = game[:-1]

        elif L>0:
            game = game[1:]

        if len(game)==0:
            if player=='I':
                output.append("Case #{}: {} {}".format((t+1), "I", 1))
            if player=='O':
                output.append("Case #{}: {} {}".format((t+1), "O", 1))
        turn+=1
        print(game, player, Score_I, Score_O)

#print output
for o in output:
    print(o)


"""Facilitates interaction with OpenCV objects."""

import cv2
import numpy as np

IMG_SIZE_VERY_SMALL = 8
IMG_SIZE_SMALL = 24
IMG_SIZE_MED = 72
IMG_SIZE_LARGE = 192
IMG_SIZE_VERY_LARGE = 320

class RGB():
    """Describes a pixel in terms of its RGB (red, green, blue) value."""
    LENGTH = 3
    def __init__(self, pixel: np.ndarray = None):
        self.r = 0
        self.g = 0
        self.b = 0
        if pixel is not None:
            # OpenCV decoded images have the channels stored in **B G R** order
            self.b, self.g, self.r = pixel

    def is_white(self):
        # allow for slightly off-white
        return self.r >= 245 and self.g >= 245 and self.b >= 245

    def is_black(self):
        return self.r == 0 and self.g == 0 and self.b == 0

    def max(self):
        return max(self.r, self.g, self.b)

    def offset(self, number: float):
        self.r += number
        self.g += number
        self.b += number

def compare_img_color(img1: cv2.Mat,
                      img2: cv2.Mat,
                      ignore_white: bool = True,
                      offset_shading: bool = True) -> float:
    """Compares the color of two images.
    Result is a number between [min=0,max=255*sqrt(3)] where min -> same color and
    max -> opposite color (white vs black)."""
    rgb1 = get_img_color(img1, ignore_white)
    rgb2 = get_img_color(img2, ignore_white)

    if offset_shading:
        # offset the values of rgb2 by difference in max from rgb1
        amt_offset = rgb1.max() - rgb2.max()
        rgb2.offset(amt_offset)

    return get_color_diff(rgb1, rgb2)

def compare_img_pixels(img1: cv2.Mat, img2: cv2.Mat, img_resize: int = IMG_SIZE_MED) -> float:
    """Compares two images for pixel equality.
    Result is a number between [min=0,max=unknown] where min -> same image and
    increasing value indicates more differences between the images."""
    # resize the images
    img1 = cv2.resize(img1, (img_resize, img_resize))
    img2 = cv2.resize(img2, (img_resize, img_resize))

    diff = 0
    for row1, row2 in zip(img1, img2):
        for pixel1, pixel2 in zip(row1, row2):
            # unpack into rgb values for each image
            rgb1 = RGB(pixel1)
            rgb2 = RGB(pixel2)
            diff += get_color_diff(rgb1, rgb2)
    return diff

def is_img_white(img: cv2.Mat) -> bool:
    """Determine if an image is all white."""
    img_color = get_img_color(img, ignore_white=False)
    return img_color.is_white()

def get_n_pixels(img: cv2.Mat) -> int:
    """Obtain the total number of pixels in an image."""
    return int(img.size/RGB.LENGTH)

def get_img_height(img: cv2.Mat) -> int:
    """Obtain the height of an image, in pixels."""
    return img.shape[0]

def get_img_width(img: cv2.Mat) -> int:
    """Obtain the width of an image, in pixels."""
    return img.shape[1]

def get_color_diff(rgb1: RGB, rgb2: RGB) -> float:
    """Use euclidean distance formula to calculate difference
    between two RGB values."""
    r_dist = int(rgb2.r) - int(rgb1.r)
    g_dist = int(rgb2.g) - int(rgb1.g)
    b_dist = int(rgb2.b) - int(rgb1.b)
    return np.sqrt(pow(r_dist, 2) + pow(g_dist, 2) + pow(b_dist, 2))

def get_img_color(img: cv2.Mat, ignore_white: bool = True) -> RGB:
    """Obtain the color of an image as a single RGB value.
    Result is the color averaged over all pixels.

    Optionally, ignore white pixels.
    """
    tot_color = RGB()
    n_pixels = get_n_pixels(img)
    for row in img:
        for pixel in row:
            rgb = RGB(pixel)
            if ignore_white and rgb.is_white():
                n_pixels -= 1
                continue
            tot_color.r += rgb.r
            tot_color.g += rgb.g
            tot_color.b += rgb.b
    rgb_norm = RGB()
    rgb_norm.r = tot_color.r/n_pixels
    rgb_norm.g = tot_color.g/n_pixels
    rgb_norm.b = tot_color.b/n_pixels
    return rgb_norm


import os
from librosa.core import load
from librosa.feature import melspectrogram
import torch
import numpy as np
import threading
import time

class DataMaker(threading.Thread):
    def __init__(self, name, featurePath, labelDict, xCatData, yCatData, Type):
        super().__init__()
        self.x_cat_data = xCatData
        self.y_cat_data = yCatData
        self.name = name
        self.fType = Type
        self.features = None
        self.labels = None
        # 原始数据的路径
        self.featurePath = featurePath
        self.labelDict = labelDict

    def run(self):
        print("Starting " + self.name)
        try:
            if self.fType == "cutting":
                self.make_cutting_data()
            elif self.fType == "blocking":
                self.make_blocking_data()
        except Exception as e:
            print(e, end='\n')
        print("Exiting " + self.name)

    def make_cutting_data(self):
        xData, yData = list(), list()
        path = self.featurePath + self.name + '/'
        for j, filename in enumerate(os.listdir(path)):
            print(f"{self.name} {filename} ({j + 1})")
            WavPath = path + filename
            y, sr = load(WavPath, mono=True)
            S = melspectrogram(y, sr).T
            S = S[:-1 * (S.shape[0] % 128)]
            xData.append(S)
            yData.append(self.labelDict[self.name])

        self.features = torch.tensor(data=xData, device=device)
        self.labels = torch.tensor(data=yData, device=device)
        print(self.features.shape)
        print(self.labels.shape)
        self.x_cat_data.append(self.features)
        self.y_cat_data.append(self.labels)
        return

    def make_blocking_data(self):
        xData, yData = list(), list()
        path = self.featurePath + self.name + '/'
        for j, filename in enumerate(os.listdir(path)):
            print(f"{self.name} {filename} ({j + 1})")
            WavPath = path + filename
            y, sr = load(WavPath, mono=True)
            S = melspectrogram(y, sr).T
            S = S[:-1 * (S.shape[0] % 128)]
            num_chunk = S.shape[0] / 128
            data_chunks = np.split(S, num_chunk)
            xChunks, yChunks = list(), list()
            for unit in data_chunks:
                xChunks.append(unit)
                yChunks.append(self.labelDict[self.name])
            xData.append(xChunks)
            yData.append(yChunks)
        xData = [unit for record in xData for unit in record]
        yData = [unit for record in yData for unit in record]

        self.features = torch.tensor(data=xData, device=device)
        self.labels = torch.tensor(data=yData, device=device)
        print(self.features.shape)
        print(self.labels.shape)
        self.x_cat_data.append(self.features)
        self.y_cat_data.append(self.labels)
        return

def save(featureSavePath, labelSavePath):
    features = torch.cat(tensors=x_cat_data, dim=0)
    labels = torch.cat(tensors=y_cat_data, dim=0)
    print(features.shape)
    print(labels.shape)
    torch.save(features, featureSavePath)
    torch.save(labels, labelSavePath)
    print("数据已保存")
    return

def loads(featureSavePath, labelSavePath):
    features = torch.load(featureSavePath)
    labels = torch.load(labelSavePath)
    print(features.shape)
    print(labels.shape)
    print("数据已加载")

if __name__ == '__main__':
    device = torch.device('cpu')

    OriginalMusicDataPath = 'C:/Users/90430/Desktop/DataMaker/original_data/genres/'

    MusicLabels = {'blues': 0, 'classical': 1, 'country': 2, 'disco': 3, 'hiphop': 4,
                   'jazz': 5, 'metal': 6, 'pop': 7, 'reggae': 8, 'rock': 9, }

    SavaPath = {
        "cutting": {"features": "cutting/features.pt",
                    "labels": "cutting/labels.pt"},

        "blocking": {"features": "blocking/features.pt",
                     "labels": "blocking/labels.pt"},
    }

    threadList = ["blues", "classical", "country", "disco", "hiphop",
                  "jazz", "metal", "pop", "reggae", "rock"]
    # 线程池
    threads = []

    # 拼接张量的列表
    x_cat_data = []
    y_cat_data = []
    fType = "cutting"

    start = time.time()
    # 创建新线程
    for tName in threadList:
        thread = DataMaker(tName, OriginalMusicDataPath, MusicLabels, x_cat_data, y_cat_data, fType)
        thread.start()
        threads.append(thread)

    # 等待所有线程完成
    for t in threads:
        t.join()

    end = time.time()
    print(f"多线程分批执行时间{end - start}")
    if len(x_cat_data) and len(y_cat_data):
        save(SavaPath[fType]["features"], SavaPath[fType]["labels"])
        loads(SavaPath[fType]["features"], SavaPath[fType]["labels"])
    else:
        print("张量为空")


from model.group import Group
import random

def test_delete_some_group(app, db, check_ui):
    if len(db.get_group_list()) == 0:
        app.group.create(Group(name="test"))
    oldgroups = db.get_group_list()
    group = random.choice(oldgroups)
    app.group.delete_group_by_id(group.id)
    newgroups = db.get_group_list()
    assert len(oldgroups) - 1 == len(newgroups)
    oldgroups.remove(group)
    assert oldgroups == newgroups
    if check_ui:
        assert sorted(newgroups, key=Group.id_or_max()) == sorted(app.group.get_group_list(), key=Group.id_or_max())


import csv
import urllib.request
import urllib.parse
import re
from datetime import date
from bs4 import BeautifulSoup

gbc_link = "https://www.gettysburg.edu"

def gen_dept(name, link):
    result = f'
{name}'
    result += '
'
    result += '
'
    result += gen_link('Program Description', link)
    result += '
'
    result += '
'
    result += gen_link('Program Requirements', f'{link}/major-minor')
    result += '
'
    result += '
'
    result += gen_link('Courses', f'{link}/courses')
    result += '
'
    result += '

'
    return result

def gen_link(text, link):
    query = ""
    if "?id=" in link:
      query = link.split('?')[1][3:]
      link = link.split('?')[0]
    return f'{text}'

def local_link_from_relative(link, query=""):
    # Remove Leading "/"
    link = link[1:]

    # Remove trailing "/" if present
    if link[-1] == "/":
        link = link[:-1]

    # Replace forward slashes with hyphens and prepend hash sign
    if query != "":
      link = f'#{link.replace("/", "-")}-{translate_url_query(query)}'
    else:
      link = f'#{link.replace("/", "-")}'
    return link

def translate_url_query(query):
  if query != "":
    return query.replace('&', '-').replace('+', '-')
  return ""

def generate_toc(year1, year2):
    result = ""

    # Top Headings (Title, Date, Link to GBC Website)
    heading = f'
{year1}-{year2} Course Catalog
'
    college_link = f'
Gettysburg College
'
    mdy_date = date.today().strftime("%m/%d/%Y")
    catalog_link = f'
Catalog generated on {mdy_date}
'
    result += heading + college_link + catalog_link

    result += '
Contents
'

    # Context manager for table of contents csv
    with open("toc.csv") as toc_csv:
        toc_reader = csv.reader(toc_csv, delimiter=',')
        past_first_row = False
        for row in toc_reader:
            # Check for content in columns
            level2_header_present = row[0] != ""
            level3_header_present = row[1] != ""
            is_dept_row = row[4]

            # Skip first row
            if row[0] == "Level 2 Heading":
                result += "
"
                continue

            # Carry out department specific behavior (Program + Course subheadings)

            if level2_header_present:
                if past_first_row:
                  result += "

"
                result += "
"
                result += gen_link(f'{row[0]}', row[5])
                result += "
"
                past_first_row = True
            if is_dept_row:
                result += gen_dept(row[1], row[5])
                continue
            if level3_header_present:
                result += gen_link(f'
{row[1]}
', row[5])
    return result

def gen_content():
    result = ""

    with open('pages.csv') as page_list:
        pages_reader = csv.reader(page_list, delimiter=',')

        # Uncomment to test
        #rows_to_test = 60

        for row in pages_reader:
            # Flags to set for each page
            title = None

            # Ignore header row
            if row[0] == "URL":
                continue

            # Check to see if page has "featured course" header or "title"
            if row[2] == 'Until Featured course header':
                above_featured = True
            elif row[1] != '':
                title = row[1]

            # Insert title header if provided
            if title != None:
                result += f'
{title}
'

            # Fetch the page
            page = urllib.request.urlopen(row[0])
            print(f'Fetching page: {row[0]}')
            soup = BeautifulSoup(page.read(), 'html.parser')

            # Extract the path from the url
            url_path = urllib.parse.urlparse(row[0]).path
            id_prefix = url_path[1:].replace('/', '-')
            page_id = id_prefix
            page_query = urllib.parse.urlparse(row[0]).query

            content = soup.find('div', {'id': 'content'})

            # Find + correct links
            all_links = content.find_all('a')
            for link in all_links:
                if link.has_attr('href'):
                    # Change relative links to id-based links
                    if link['href'][0] == "/":
                        link_url = urllib.parse.urlparse(link['href'])
                        link_url_path = link_url.path
                        link_url_query = link_url.query
                        link['href'] = local_link_from_relative(link_url_path, link_url_query)
                    # Change existing skip links to new links with prefix based
                    # what page they are from
                    elif link['href'][0] == "#":
                        old_id = link['href'][1:]
                        new_id = f"#{page_id}{old_id}"
                        link['href'] = new_id

            # Find all elements with ids
            all_elems_with_ids = content.find_all(id=re.compile(".+"))
            for elem in all_elems_with_ids:
                elem["id"] = id_prefix + elem["id"]

            # Remove svgs
            for svg_item in content.find_all('svg'):
                svg_item.decompose()

            page_content = None
            try:
                columns = content.find_all('div', {'class': 'column'})
                page_content = columns[1]
            # BUG: When index error is catched, there are type errors in the exception code.
            # Handle the case when the content element is a main instead of a div
            except IndexError:
                # Handle the case when the columns are not all divs
                page_content = content.find('main', {'class', 'column'})

            # Add an id to the content to allow skip linking to it
            if page_query != "":
              page_content['id'] = page_id + '-' + translate_url_query(page_query)[3:]
              print(page_content['id'])
            else:
              page_content['id'] = page_id[:-1]
            result += str(page_content)

            # This is the old way that I used to grab the content above the
            # "Featured Courses" header
            """
            if above_featured:

                above_featured = columns[1].find(
                    'div', {'class': 'gb-u-spacing-bottom'})
                result += str(above_featured)
            else:
                result += str(columns[1])
                """

            # Uncomment to test with the first rows_to_test rows in the csv file
            #rows_to_test -= 1
            #if rows_to_test < 0:
            #   break
    return result

with open("output.html", "a") as output:
    output.write(generate_toc(2019, 2020))
    output.write(gen_content())


# operator.add(a, b)
# operator.mul(a, b)

"""
https://komiyam.hatenadiary.org/entry/20131202/1385992406
大雑把に言って、平衡二分探索木からinsert,delete,split,mergeなどができないよう制限したのがsegment treeで、
segment treeの区間[L,R)に対するクエリをL=0に制限したのがbinary indexed treeだと見なすことができます。
"""
"""
N = 2 のときは使えない！！
get(x, N + 1)の処理で index out of range エラーとなる(ABC047-D) ??
"""

class SegmentTree():
    """
    update, get を提供するSegmentTree

    Attributes
    ----------
    __n : int
        葉の数。2 ^ i - 1
    __dot :
        Segment function
    __e: int
        単位元
    __node: list
        Segment Tree
    """

    def __init__(self, A, dot, e):
        """
        Parameters
        ----------
        A : list
            対象の配列
        dot :
            Segment function
        e : int
            単位元
        """
        n = 2 ** (len(A) - 1).bit_length()
        self.__n = n
        self.__dot = dot
        self.__e = e
        self.__node = [e] * (2 * n)
        for i in range(len(A)):
            self.__node[i + n] = A[i]
        for i in range(n - 1, 0, -1):
            self.__node[i] = self.__dot(self.__node[2 * i], self.__node[2 * i + 1])

    def update(self, i, c):
        i += self.__n
        node = self.__node
        if node[i] == c:
            return
        node[i] = c
        while i > 1:
            i //= 2
            node[i] = self.__dot(node[2 * i], node[2 * i + 1])

    def get(self, l, r):
        """
        区間[l, r)での値を返す. 0-index.
        """
        vl, vr = self.__e, self.__e
        l += self.__n
        r += self.__n
        while (l < r):
            if l & 1:
                vl = self.__dot(vl, self.__node[l])
                l += 1
            l //= 2
            if r & 1:
                r -= 1
                vr = self.__dot(vr, self.__node[r])
            r //= 2
        return self.__dot(vl, vr)

    def node(self, i):
        '''node[i]を返す(0-index)
        '''
        return self.__node[i + self.__n]

# http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DSL_2_A
def AOJ_DSL_2_A():
    n, q = map(int, input().split())
    e = (1 << 31) - 1
    A = [e] * n
    seg = SegmentTree(A, min, e)

    for i in range(q):
        t, x, y = map(int, input().split())
        if t == 0:
            seg.update(x, y)
        else:
            print(seg.get(x, y + 1))

# http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DSL_2_B
def AOJ_DSL_2_B():
    from operator import add
    import sys
    n, q = map(int, input().split())
    e = 0
    A = [e] * n
    seg = SegmentTree(A, add, e)

    query = sys.stdin.readlines()
    for i in range(q):
        t, x, y = map(int, query[i].split())
        x -= 1
        if t == 0:
            A[x] += y
            seg.update(x, A[x])
        else:
            y -= 1
            print(seg.get(x, y + 1))

def ABC125_C():
    from fractions import gcd
    N = int(input())
    A = list(map(int, input().split()))

    seg = SegmentTree(A, gcd, 0)
    ans = 0
    for i in range(N):
        ans = max(ans, gcd(seg.get(0, i), seg.get(i + 1, N)))
    print(ans)

# AOJ_DSL_2_A()
AOJ_DSL_2_B()
# ABC125_C()


kwh=float(input('qual a quantidade de Kwh: '))
print('tipo de estalação')
print('Digite r para residência \nDigite i para industria e \nDigite c para comercios')

estalacao=input('tipo de estalaçao:  ')

if (estalacao=='r'):
    if (kwh<=500):
        preco= 0.40
        q = 'residência'
    else:
        preco= 0.65
        q = 'residência'
elif (estalacao=='c'):
    if (kwh<=100):
        preco= 0.55
        q = 'comercial'
    else:
        preco= 0.60
        q = 'comercial'
elif (estalacao=='i'):
    if (kwh<=5000):
        preco= 0.55
        q = 'industria'
    else:
        preco= 0.60
        q = 'industria'
if (estalacao=='r' or estalacao=='i' or estalacao=='c'):
    print(f'seu imóvel é {q}, o preço da fatura R${preco * kwh:.2f}')

else:
    print('opção invalida!')


'''
Created on May 1, 2015

@author: Jaswant.Jonnada
'''

from itertools import groupby

data = [['c','a'],'a','b','c','d']

gb = groupby(data,key = lambda x:x[0])

for key,value in gb:
    print (key,list(value))


import sys
import time

from logger import Logger
from irc_mechanics import Irc

if __name__ == "__main__":
    Logger.init()

    irc = Irc.load()
    if irc is not None:
        if len(irc.irc_servers) > 0:
            i = 0
        else:
            i = -1

        if i != -1:
            if sys.stdin.isatty():
                while irc.is_alive():
                    try:
                        line = sys.stdin.readline()
                    except KeyboardInterrupt:
                        break

                    if not line or line.strip() == "^C":
                        Logger.print()
                        break

                    irc.irc_servers[i].send_method(line)
            else:
                Logger.print("No stdin, just sitting around and drinking tea")
                Logger.print()
                while irc.is_alive():
                    time.sleep(5)

            irc.irc_servers[i].close()
    else:
        Logger.print("There was no IRC")
    Logger.print("Closing")


from app import app
import pymysql
import json
import requests
from auth import auth_required
from config import mysql
from flask import jsonify
from flask import request
from validation import *

@app.route('/cliente', methods =['POST'])
@auth_required
def create_cliente():
    try:
        _json = request.json
        _name = _json['name']
        _email = _json['email']
        _cpf = _json['cpf']
        _tel = _json['tel']
        _age = _json['age']
        if _name and _email and _cpf and _tel and _age and request.method == 'POST':
            conn = mysql.connect()
            cursor = conn.cursor(pymysql.cursors.DictCursor)
            sqlQuery = "INSERT INTO cliente(name, email, cpf, tel, age) VALUES(%s,%s, %s, %s, %s)"
            bindData = (_name, _email, _cpf, _tel, _age,)
            cursor.execute(sqlQuery, bindData)
            conn.commit()
            response = jsonify('Cliente adicionado!')
            response.status_code == 200
            return response
        else:
            return showMessage()
    except Exception as e:
       response = jsonify({"Message": f"{e}"})
       return response
    finally:
        cursor.close()
        conn.close()

@app.route('/cliente')
@auth_required
def cliente():
    try:
        conn = mysql.connect()
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        cursor.execute("SELECT * FROM cliente")
        cliRow = cursor.fetchall()
        response = jsonify(cliRow)
        response.status_code == 200
        return response
    except Exception as e:
        print(e)
    finally:
        cursor.close()
        conn.close()

@app.route('/cliente/<int:id>')
@auth_required
def cliente_detail(id):
    try:
        conn = mysql.connect()
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        cursor.execute("SELECT id, name, email, cpf, tel, age FROM cliente WHERE id=%s", id)
        cliRow = cursor.fetchall()
        response = jsonify(cliRow)
        response.status_code == 200
        return response
    except Exception as e:
        print(e)
    finally:
        cursor.close()
        conn.close()

@app.route('/cliente', methods=['PUT'])
@auth_required
def update_cliente():
    try:
        _json = request.json
        _id = _json['id']
        _name = _json['name']
        _email = _json['email']
        _cpf = _json['cpf']
        _tel = _json['tel']
        _age = _json['age']
        if _name and _email and _cpf and _tel and _age and _id and request.method == 'PUT':
            conn = mysql.connect()
            cursor = conn.cursor(pymysql.cursors.DictCursor)
            sqlQuery = "UPDATE cliente SET name=%s, email=%s, cpf=%s, tel=%s, age=%s WHERE id=%s"
            bindData = (_name, _email, _cpf, _tel, _age, _id)
            cursor.execute(sqlQuery, bindData)
            conn.commit()
            response = jsonify('Cliente Atualizado!')
            response.status_code == 200
            return response
        else:
            return showMessage()
    except Exception as e:
       reponse = jsonify({"Message": f"{e}"})
       return reponse
    finally:
        cursor.close()
        conn.close()

@app.route('/cliente/address/<int:id>')
@auth_required
def client_address(id):

    r = requests.get('http://127.0.0.1:5001/address', auth=('admin', '123'))

    text = r.text
    data =json.loads(text)

    lista = []
    for endereco in data:
        if endereco['idcliente'] == id:
                lista.append(endereco)
    try:
        conn = mysql.connect()
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        cursor.execute("SELECT id, name, email, cpf, tel, age FROM cliente WHERE id=%s", id)
        cliRow = cursor.fetchall()
        response = jsonify(cliRow, lista)
        response.status_code == 200
        return response
    except Exception as e:
        print(e)
    finally:
        cursor.close()
        conn.close()

@app.route('/cliente/<int:id>', methods=['DELETE'])
@auth_required
def delete_client(id):
    try:
        conn = mysql.connect()
        cursor = conn.cursor()
        cursor.execute("DELETE FROM cliente WHERE id =%s",(id))
        cursor.execute("DELETE FROM address WHERE idcliente =%s", (id))
        conn.commit()
        response = jsonify('Client deleted successfully!')
        response.status_code = 200
        return response
    except Exception as e:
        print(e)
    finally:
        cursor.close()
        conn.close()

@app.errorhandler(404)
def showMessage(error=None):
    message = {
        'status': 404,
        'message': 'Record not found: ' + request.url,
    }
    response = jsonify(message)
    return response

health_status = True

@app.route('/toggle')
def toggle():
    global health_status
    health_status = not health_status
    return jsonify(health_value=health_status)

@app.route('/health')
def health():
    if health_status:
        resp = jsonify(health="healthy")
        resp.status_code = 200
    else:
        resp = jsonify(health="unhealthy")
        resp.status_code = 500

    return resp

if __name__ == "__main__":
     app.run(host='0.0.0.0', port=5000, debug=True)


import requests
from rich import print as rprint

BASE_URL = "http://192.168.4.63:5050"

def get_networks():
    response = requests.get(f"{BASE_URL}/networks")
    return response.json()

def get_network(id):
    response = requests.get(f"{BASE_URL}/network/{id}")
    if response.status_code == 404:
        raise ValueError(response.json()["error"])
    return response.json()

def create_network(name, subnets):
    data = {"name": name, "subnets": subnets}
    response = requests.post(f"{BASE_URL}/network", json=data)
    if response.status_code == 400:
        raise ValueError(response.json()["error"])
    return response.json()

def update_network(id, name, subnets):
    data = {"name": name, "subnets": subnets}
    response = requests.put(f"{BASE_URL}/network/{id}", json=data)
    if response.status_code == 404 or response.status_code == 400:
        raise ValueError(response.json()["error"])
    return response.json()

def delete_network(id):
    response = requests.delete(f"{BASE_URL}/network/{id}")
    if response.status_code == 404:
        raise ValueError(response.json()["error"])
    return response.json()

def main():
    while True:
        answer = input(
            "Which function would you like to use? [get_networks, get_network, create_network, update_network, delete_network]. Please type 'exit' to exit the program: "
        )
        if answer == "get_networks":
            rprint(get_networks())
        elif answer == "get_network":
            id = input("Enter the network ID you wish to target: ")
            rprint(get_network(id))
        elif answer == "create_network":
            name = input("Enter the name of the network you want to create: ")
            subnet_list = input(
                "Enter the subnets you want to add (separated by commas): "
            )
            subnets = subnet_list.split(",")
            rprint(create_network(name, subnets))
        elif answer == "update_network":
            id = input("Enter the network ID you wish to target: ")
            name = input("Enter the name of the network you want to create: ")
            subnet_list = input(
                "Enter the subnets you want to add (separated by commas): "
            )
            subnets = subnet_list.split(",")
            rprint(update_network(id, name, subnets))
        elif answer == "delete_network":
            id = input("Enter the network ID you wish to target: ")
            rprint(delete_network(id))
        elif answer == "exit":
            break
        else:
            print("That input is invalid")

if __name__ == "__main__":
    main()


"""
접두사
길이 긴거 순으로 정렬하고
하나씩 순회 하면서 ans에 하나씩 넣을꺼다
넣는 조건 -> 현재 확인 중인 단어가 ans에 들어가있는 모든 단어의 접두사가 아니면
"""

import sys
input = lambda: sys.stdin.readline().rstrip()

N = int(input())

arr = [input() for _ in range(N)]

arr.sort(key=lambda x: -len(x))

ans = []

for pref in arr:
    for word in ans:
        if word[:len(pref)] == pref:
            break
    else:
        ans.append(pref)

print(len(ans))