import time
from itertools import cycle
from typing import Iterable

import cv2  # type: ignore[import]
from ark import (
    ArkWindow,
    Bed,
    Dinosaur,
    IndustrialGrinder,
    Player,
    Structure,
    TekDedicatedStorage,
    exceptions,
    items,
    tools,
)
from discord import Embed  # type: ignore[import]
from PIL import Image  # type: ignore[import]
from pytesseract import pytesseract as tes  # type: ignore[import]

from ...tools import format_seconds, mss_to_pil
from ...webhooks import InfoWebhook, TribeLogWebhook
from .._station import Station
from ._settings import GrindingStationSettings
from ._stations import Stations
from ._status import Status

# map common mistakes in the dedi OCR
DEDI_NUMBER_MAPPING = {"l": "1", "i": "1", "I": "1", "|": "1", "O": "0"}

# the default mats we assume when the dedis could not be determined
DEFAULT_MATS: dict[items.Item, int] = {
    items.SILICA_PEARL: 7000,
    items.PASTE: 5000,
    items.METAL_INGOT: 8757,
    items.ELECTRONICS: 900,
    items.CRYSTAL: 10000,
    items.HIDE: 20000,
}

class GrindingStation(Station):

    GRINDER_AVATAR = "https://static.wikia.nocookie.net/arksurvivalevolved_gamepedia/images/f/fe/Industrial_Grinder.png/revision/latest/scale-to-width-down/228?cb=20160728174054"
    EXOMEK_AVATAR = "https://static.wikia.nocookie.net/arksurvivalevolved_gamepedia/images/6/6d/Unassembled_Exo-Mek_%28Genesis_Part_2%29.png/revision/latest/scale-to-width-down/228?cb=20210603184626"
    ELECTRONICS_AVATAR = "https://static.wikia.nocookie.net/arksurvivalevolved_gamepedia/images/d/dd/Electronics.png/revision/latest/scale-to-width-down/228?cb=20150615100650"

    """Represents the grinding station on the gacha bot.

    The basic concept is to grind the gear from higher quality crystals into
    material to then craft turrets. The station is set ready by the crystal
    station when it notices the vault is full, until finished it then uses
    a `Status` enum to control the tasks.

    Parameters:
    -----------
    station_data :class:`StationData`:
        A dataclass containing data about the station

    player :class:`Player`:
        The player controller handle responsible for movement

    tribelog :class:`Tribelog`:
        The tribelog object to check tribelogs when spawning

    """
    _SUPPORTED_CRAFTABLES = {
        items.HEAVY_AUTO_TURRET,
        items.AUTO_TURRET,
        items.METAL_FOUNDATION,
        items.METAL_TRIANGLE,
        items.METAL_GATE,
        items.C4_DETONATOR,
        items.ROCKET_LAUNCHER,
        items.TEK_TURRET,
    }

    _CRAFTABLES_MAP = {item.name: item for item in _SUPPORTED_CRAFTABLES}

    def __init__(
        self,
        player: Player,
        tribelog: TribeLogWebhook,
        info_webhook: InfoWebhook,
    ) -> None:
        self._name = "grinding"
        self._player = player
        self._tribelog = tribelog
        self._webhook = info_webhook
        self.settings = GrindingStationSettings.load()

        if self.settings.item_to_craft == "None":
            self.item_to_craft = None
        else:
            self.item_to_craft = self._CRAFTABLES_MAP[self.settings.item_to_craft]

        self.bed = Bed("grinding")
        self.ready = False
        self.status = Status.WAITING_FOR_ITEMS
        self.current_station = "Gear Vault"

        self.grinder = IndustrialGrinder()
        self.dedi = TekDedicatedStorage()
        self.vault = Structure("Vault", "assets/templates/vault_capped.png")
        self.exo_mek = Dinosaur("Exo Mek", "assets/templates/exo_mek.png")
        self.screen = ArkWindow()

        self.STATION_MAPPING: dict[str, tuple | list] = {
            "Grinder": (self._player.turn_x_by, -50),
            "Exo Mek": (self._player.turn_x_by, -110),
            "Vault": (self._player.turn_x_by, -95),
            "Crystal": (self._player.turn_x_by, -70),
            "Hide": (self._player.turn_y_by, 40),
            "Ingot": (self._player.turn_x_by, -60),
            "Electronics": (self._player.turn_y_by, -40),
            "Pearls": (self._player.turn_x_by, -60),
            "Paste": (self._player.turn_y_by, 40),
            "Gear Vault": [
                (self._player.turn_y_by, -40),
                (self._player.turn_x_by, -70),
            ],
        }

    def spawn(self) -> None:
        """Override spawn method to set current station"""
        super().spawn()
        self.current_station = Stations.GEAR_VAULT

    def is_ready(self) -> bool:
        """Checks if the station is ready to be completed. Overriding the
        base `is_ready` method because the grinding station is responsible
        for multiple steps within the same instance.
        """
        if not self.settings.enabled:
            return False

        print(f"Checking whether grinding station is ready, status: '{self.status}'")
        if self.status == Status.WAITING_FOR_ITEMS:
            return self.ready

        if self.status == Status.AWAITING_EVALUTION:
            return True

        if self.status in [
            Status.CRAFTING_SUBCOMPONENTS,
            Status.AWAITING_CRAFT,
            Status.AWAITING_PICKUP,
        ]:
            return self.crafting_finished()

        raise ValueError(
            f"Grinding Station failed to match current status '{self.status}'!"
        )

    def complete(self) -> None:
        """Completes the current task at the grinding station."""
        # check what status we are on
        if self.status == Status.WAITING_FOR_ITEMS:
            self.grind_and_deposit()

        elif self.status == Status.AWAITING_EVALUTION:
            self.determine_materials()

        elif self.status == Status.CRAFTING_SUBCOMPONENTS:
            self.do_next_craft()

        elif self.status == Status.AWAITING_CRAFT:
            self.do_final_craft()

        elif self.status == Status.AWAITING_PICKUP:
            self.pickup_final_craft()

        else:
            raise ValueError(
                f"Grinding Station failed to match current status '{self.status}'!"
            )

    def grind_and_deposit(self) -> None:
        """Grinds the gear down, determines the amount of resources we have
        and calculates the optimal crafting."""
        self.spawn()
        start = time.time()

        self.grind_armor()
        self.grind_weapons()
        self.empty_grinder(turn_off=True)

        embed = self._create_grinding_finished_embed(round(time.time() - start))
        self._webhook.send_embed(embed)

        if self.item_to_craft is None:
            self._transfer_dedi_wall()
            self.ready = False
        else:
            self.status = Status.AWAITING_EVALUTION

    def determine_materials(self, debug: bool = False) -> None:
        assert self.item_to_craft is not None and self.item_to_craft.recipe is not None

        result = self.get_dedi_materials(debug)

        available_mats = result["determined"]
        undetermined = result["undetermined"]

        for item in undetermined:
            available_mats[item] = DEFAULT_MATS[item]

        available_mats[items.ORGANIC_POLYMER] = 5000

        self.current_station = Stations.ELECTRONICS
        img = self.screen.grab_screen((0, 0, 1920, 1080))

        for item in self.item_to_craft.recipe:
            if item in available_mats:
                continue

            self.turn_to(Stations.EXO_MEK)
            self.exo_mek.access()
            self.exo_mek.inventory.search(item)
            self.exo_mek.sleep(0.3)

            available_mats[item] = max(
                0,
                self.exo_mek.inventory.count(item) * item.stack_size
                - (0.5 * item.stack_size),
            )
        self.exo_mek.close()

        embed = self.create_available_materials_embed(available_mats, undetermined)
        self._webhook.send_embed(embed, img=img)
        self._player.sleep(3)

        self.compute_crafting_plan(available_mats)
        self.status = Status.CRAFTING_SUBCOMPONENTS

        embed = self.create_crafting_embed()
        self._webhook.send_embed(embed)

    def grind_armor(self) -> None:
        """Grind all the riot gear down, putting the polymer from the first grinding
        into the Exo Mek and dropping the rest. If a piece was not found, it will
        simply continue with the next one in the list.

        If any piece was found, after grinding all armor the crystal will be taken
        from the grinder and deposited into its dedi.
        """
        # pieces to grind
        armor = [
            items.RIOT_LEGGS,
            items.RIOT_CHEST,
            items.RIOT_GAUNTLETS,
            items.RIOT_BOOTS,
            items.MINER_HELMET,
            items.RIOT_HELMET,
        ]

        # flag to determine if any items were grinded
        amount_found = 0
        for piece in armor:

            # empty grinder before we grind miner helmets
            # to avoid overcapping and wasting electronics
            if piece is items.MINER_HELMET:
                self.vault.close()
                self.empty_grinder()

            if not self.take_item(piece):
                continue

            if piece is items.MINER_HELMET:
                self.grind(piece, [items.ORGANIC_POLYMER, items.ELECTRONICS])
            else:
                self.grind(piece, [items.ORGANIC_POLYMER, items.SILICA_PEARL])

            if amount_found < 2:
                self.put_into_exo_mek(items.ORGANIC_POLYMER)
            amount_found += 1

            # deposit the grinded items, turn back to gear vault
            self.deposit(
                [items.ELECTRONICS]
                if piece is items.MINER_HELMET
                else items.SILICA_PEARL
            )
            self.turn_to(Stations.GEAR_VAULT)

        self.vault.close()
        if amount_found:
            self.drop_script_from_grinder(items.CRYSTAL)

    def grind_weapons(self) -> None:
        """Grinds all the weapons in the Gear Vault. If a weapon was not found,
        it will simply continue with the next one.

        Poly from all weapons will be dropped, paste will be taken and directly
        deposited into the dedi. After grinding all weapons, the ingots will be
        taken and deposited.
        """
        weapons = [
            items.FABRICATED_PISTOL,
            items.FABRICATED_SNIPER,
            items.ASSAULT_RIFLE,
            items.PUMPGUN,
            items.LONGNECK,
            items.SIMPLE_PISTOL,
        ]
        for weapon in weapons:
            if not self.take_item(weapon):
                continue

            self.grind(weapon, [items.ORGANIC_POLYMER, items.PASTE])
            self.deposit(items.PASTE)
            self.turn_to(Stations.GEAR_VAULT)
        self._player.drop_all()

        self.drop_script_from_grinder(items.METAL_INGOT)

    def get_cycle(self, stations: Iterable[str]) -> cycle:
        """Converts the list of our stations to a `cycle` object starting at
        our current station to help find the quickest way to the target station.

        Parameters:
        -----------
        stations :class:`Iterable`:
            The stations to convert to the cycle

        Returns:
        ----------
        A `cycle` object with the iteration pointer on the current station.
        """
        stations = cycle(stations)

        # set the cycle pointer to current station
        for station in stations:
            if station == self.current_station:
                return stations
        raise Exception("Not sure how we got here...")

    def find_quickest_way_to(self, target_station: Stations) -> tuple[list[str], str]:
        """Finds the quickest way to the passed target station, forwards
        and backwards possibilities are considered, the shortest is returned.

        Parameters:
        -----------
        target_station :class:`Stations.enum`:
            The station to turn to

        Returns:
        --------
        A list containing the stations to go through to reach the target Stations.

        Raises:
        --------
        `InvalidStationError` if you passed a station that doesnt exist.
        """
        # ensure the passed station exists to avoid looping infinitely
        if not target_station in self.STATION_MAPPING:
            raise ValueError(f"{target_station} is not a valid station!")

        if target_station == self.current_station:
            return [], "stay"

        # remove values from keys to get an proper iterable
        stations = list(self.STATION_MAPPING)

        # get the amount of station to go through if we move forwards
        # use itertools cycle method to allow counting over lists end
        forward_path = []
        for station in self.get_cycle(stations):
            forward_path.append(station)
            if station == target_station:
                break

        backward_path = [self.current_station]
        for station in self.get_cycle(reversed(stations)):
            backward_path.append(station)
            if station == target_station:
                break

        # compare both resulting lists, return the shorter one together with
        # the direction
        if len(forward_path) < len(backward_path):
            return forward_path, "forward"
        return backward_path, "backwards"

    def turn_to(self, target_station: Stations) -> None:
        """Turns to the given station using the fastest way around possible.

        If we are going backwards, the turn amount is multiplies by -1 to
        get the opposite of the actual movement and go backwards instead.

        Parameters:
        -----------
        target_station :class:`Station` | `str`:
            The station to turn to
        """
        # get the quickest path and the direction
        path, direction = self.find_quickest_way_to(target_station)
        self._player.sleep(1)

        for station in path:
            self.current_station = station
            # when going backwards, done once the station is the target
            if direction == "backwards" and station == target_station:
                break

            # check if the station requires multiple turns
            # a station consists of the method (turn x...) + the value
            if isinstance(self.STATION_MAPPING[station], list):
                for func, amount in self.STATION_MAPPING[station]:
                    func(amount * (-1 if direction == "backwards" else 1))
                    self._player.sleep(0.3)
                continue

            func, amount = self.STATION_MAPPING[station]
            func(amount * (-1 if direction == "backwards" else 1))
            self._player.sleep(0.3)
        self._player.sleep(1)

    def grind(self, item: items.Item, take: list[items.Item]) -> None:
        """Turns to the grinder and grinds the item, then takes all requested
        items. Leaves the grinder in a closed state after finishing.

        Can be called with the grinder already being open or at any other
        station as long as the current station is CLOSED!

        Parameters:
        -----------
        grind :class:`Item`:
            The items to grind

        take :class:`list`:
            A list of Items to take after grinding
        """
        # open the grinder and transfer the items into it
        if not self.grinder.inventory.is_open():
            self.turn_to(Stations.GRINDER)

        self.grinder.open()
        self._player.inventory.transfer_all(item)
        self._player.sleep(0.5)

        # turn the grinder on if its not already, grind all the items
        self.grinder.turn_on()
        self.grinder.grind_all()

        for item in take:
            self.grinder.inventory.transfer_all(item)
        self.grinder.close()

    def deposit(self, items: list[items.Item] | items.Item) -> None:
        """Turns to the dedi for each item given and deposits it.

        Parameters:
        ------------
        items :class:`list` | `Item`:
            The item(s) to deposit as Item object(s)
        """
        # turn it into a list if only 1 item was passed
        if not isinstance(items, list):
            items = [items]

        # deposit
        for item in items:
            self.turn_to(Stations.from_item(item))
            self.dedi.deposit([item], get_amount=False)

    def take_item(self, item: items.Item) -> bool:
        """Turns to the gear vault and takes the specified item from it.
        Leaves the vault in a closed state.

        Can be called with the vault already open or at any other station
        as long as it is CLOSED.

        Parameters:
        -----------
        item :class:`Item`:
            The item to take, as Item object

        Returns:
        -----------
        Whether there was any of the specified item in the vault.
        """
        if not self.vault.inventory.is_open():
            self.turn_to(Stations.GEAR_VAULT)
            self.vault.open()

        # clear inventory, search for the target item
        self._player.inventory.drop_all()
        self.vault.inventory.search(item)
        self._player.sleep(0.5)

        # check if any items are within the vault
        if not self.vault.inventory.has(item, is_searched=True):
            return False

        # take all (remember search filter is active), close vault
        self.vault.inventory.transfer_all()
        self.vault.close()
        return True

    def put_into_exo_mek(self, items: list[items.Item] | items.Item) -> None:
        """Puts all given items into exo mek. Leaves the exo mek in a
        closed state.

        Parameters:
        -----------
        item :class:`list` | `Item`:
            The item(s) to put into the exo mek as item object(s)
        """
        # turn it into a list if only 1 item was passed
        if not isinstance(items, list):
            items = [items]

        if not self.exo_mek.inventory.is_open():
            self.turn_to(Stations.EXO_MEK)
            self.exo_mek.access()

        # deposit the items
        for item in items:
            self._player.inventory.transfer_all(item)
            self._player.sleep(0.5)
        self.exo_mek.close()

    def empty_grinder(self, turn_off: bool = False) -> None:
        """Empties the grinder by taking hide and despawning the rest.

        Parameters:
        ----------
        turn_off :class:`bool`:
            Whether to turn the grinder off after cleanup.
        """
        # get all the hide and deposit it
        self.turn_to(Stations.GRINDER)
        self.grinder.open()
        self.grinder.inventory.transfer_all(items.HIDE)
        self.grinder.close()
        self.deposit(items.HIDE)

        # drop all on the remaining items
        self.turn_to(Stations.GRINDER)
        self.grinder.open()
        for item in [items.FIBER, items.STONE, items.ANGLER_GEL, items.WOOD]:
            self.grinder.inventory.search(item)
            self.grinder.sleep(0.3)

            if self.grinder.inventory.has(item, is_searched=True):
                self.grinder.inventory.transfer_all(item)

        self._player.inventory.drop_all()

        # turn off grinder if requestes and close it
        if turn_off:
            self.grinder.turn_off()
        self.grinder.close()

    def drop_script_from_grinder(self, item: items.Item) -> None:
        """Drop scripts the given item from the grinder and puts it
        into its respective dedi, returns to Gear Vault upon finishing.
        """
        # drop script the items out
        self.turn_to(Stations.GRINDER)
        self._player.do_drop_script(item, self.grinder.inventory)
        self._player.turn_y_by(-163)

        # deposit the items, turn back to gear vault
        self.deposit(item)
        self.turn_to(Stations.GEAR_VAULT)

    def put_item_into_exo_mek(self, item: items.Item, amount: int) -> None:
        """Takes the given resource from its dedi and puts the given
        amount into the exo mek.

        Parameters:
        ------------
        item :class:`Item`:
            The item to put into the exo mek

        amount :class:`int`:
            The amount of the item to put into the exo mek
        """
        self.turn_to(Stations.from_item(item))
        self.dedi.open()
        self.dedi.inventory.transfer_all()

        self.dedi.close()
        self._player.sleep(1)

        # transfer the amount into the exo mek
        self.turn_to(Stations.EXO_MEK)
        self.exo_mek.access()

        if self.exo_mek.inventory.has(items.GACHA_CRYSTAL):
            # when travelling from the station before a bag can end up on the
            # bed, check if we opened one on accident.
            self._player.pick_up_bag()
            self.exo_mek.access()

        self._player.inventory.transfer(
            item, amount + item.stack_size, self.exo_mek.inventory
        )
        self.exo_mek.close()

        # put remaining resources back into dedi
        self.turn_to(Stations.from_item(item))
        self.dedi.deposit([item], get_amount=False)

    def get_dedi_screenshot(self, spawn: bool = True) -> Image.Image:
        """Grabs a screenshot of the dedi wall to later determine the
        amount of resources available.

        Parameters:
        -----------
        spawn :class:`bool`:
            Whether the bot should resync to the bed first

        Returns:
        ----------
        A PIL Image of the current dedi wall.
        """

        # sync to bed, look at dedi wall
        if spawn:
            self.spawn()
            self._player.turn_x_by(130)

        # looking up hard then down to make our hands move out of the
        # screen temporarily for better clarity
        self._player.hide_hands()

        # save the result, enable HUD and return the Image
        img = self.screen.grab_screen(region=(0, 0, 1920, 1080))

        self._player.sleep(0.5)
        self._player.disable_hud()

        return mss_to_pil(img)

    def walk_back_little(self) -> None:
        """Crouches and walks back a tiny bit, attempting to getting a better
        view on the dedis."""
        self._player.crouch()
        self._player.walk("s", 0.1)
        self._player.crouch()
        self._player.sleep(0.5)

    def amount_valid(self, item: items.Item, amount: int) -> bool:
        """Validates that the amount we think we OCR'd is within a valid range.

        Parameters:
        -----------
        item :class:`Item`:
            The item object we are determining the amount of

        amount :class:`int`:
            The amount of the item OCRing gave us.

        Returns:
        ----------
        Whether the amount is within a regular range.
        """
        expected = {
            items.SILICA_PEARL: (3000, 130000),
            items.PASTE: (7000, 180000),
            items.ELECTRONICS: (800, 10000),
            items.METAL_INGOT: (5000, 60000),
        }
        if self.item_to_craft is None:
            return True

        assert self.item_to_craft.recipe is not None

        # we dont care about crystal or hide
        if item not in expected or (
            item not in list(self.item_to_craft.recipe)
            and not any(
                subitem.recipe is not None for subitem in self.item_to_craft.recipe
            )
        ):
            return True

        # ensure the OCRd amount is within a f
        lower_range, upper_range = expected[item]
        return upper_range >= amount >= lower_range - 300

    def get_dedi_materials(self, debug: bool = False) -> dict:
        """Tries to get the dedi materials up to 10 times. Will return a dict
        of the material and its amount on the first successful attempt.

        Raises `DediNotFoundError` after 10 unsuccessful attempts.
        """

        dedi_to_region = {
            items.SILICA_PEARL: self.settings.pearls_region,
            items.PASTE: self.settings.paste_region,
            items.ELECTRONICS: self.settings.electronics_region,
            items.METAL_INGOT: self.settings.ingots_region,
            items.CRYSTAL: self.settings.crystal_region,
            items.HIDE: self.settings.hide_region,
        }

        img = self.get_dedi_screenshot(True)
        result: dict[str, dict[items.Item, int]] = {
            "determined": {},
            "undetermined": {},
        }

        for item, region in dedi_to_region.items():
            roi = img.crop(
                (region[0], region[1], region[0] + region[2], region[1] + region[3])
            )
            denoised_roi = self.screen.denoise_text(roi, self.settings.text_rgb, 22)
            amount: str = tes.image_to_string(
                denoised_roi,
                config="-c tessedit_char_whitelist=0123456789liI|O --psm 6",
            ).replace("\n", "")

            # replace common tesseract fuckups
            for char, new_char in DEDI_NUMBER_MAPPING.items():
                amount = amount.replace(char, new_char)
            if debug:
                cv2.imshow(f"{item.name} - {amount}", denoised_roi)
                cv2.waitKey(0)

            try:
                final_result = int(amount)
            except ValueError as e:
                print(e)
                final_result = 0

            # validate that the result is within a logical range
            if not self.amount_valid(item, final_result):
                result["undetermined"][item] = final_result
            else:
                result["determined"][item] = final_result

        return result

    def compute_crafting_plan(self, owned_items: dict[items.Item, int]):
        """Receives the list of materials we own and figures out the most
        'efficient' way to craft turrets mainly taking in consideration the
        balance between metal and electronics.

        It first calculates the usage of the crafted mats we have, and then
        calculates with pearls and ingots instead of electronics.

        Sets object variables `session_turrets`, `session_cost` and
        `electronics_to_craft`.

        Parameters:
        -----------
        owned_items :class:`dict`:
            A dict containing each item and the amount we have available
        """
        assert self.item_to_craft is not None and self.item_to_craft.recipe is not None

        amount, to_craft, total_cost = tools.compute_crafting_plan(
            self.item_to_craft, owned_items
        )

        self.session_crafts = amount
        self.subcomponents_to_craft = to_craft
        self.total_session_cost = total_cost

    def create_crafting_embed(self) -> Embed:
        """Sends an embed to the info webhook informing about the crafting
        plan that has been calculated for the ongoing session. Takes its data
        from the session class attributes."""
        assert self.item_to_craft is not None and self.item_to_craft.recipe is not None

        # reformat the amounts to make it look nicer
        formatted: dict[items.Item, str] = {}
        desired_order = [
            items.METAL_INGOT,
            items.PASTE,
            items.ELECTRONICS,
            items.SILICA_PEARL,
            items.CRYSTAL,
            items.HIDE,
            items.ORGANIC_POLYMER,
            items.ELEMENT,
        ]
        for resource, amount in self.total_session_cost.items():
            formatted[resource] = f"{amount:_}x".replace("_", " ")
        formatted = {k: formatted[k] for k in desired_order if k in formatted}

        # create embed, black sidebar
        embed = Embed(
            type="rich",
            title="Calculated crafting!",
            description="Required materials:",
            color=0x000000,
        )

        # add each resource to the embed, heavies and electronics on the
        # righthand side
        for resource, quantity in formatted.items():
            embed.add_field(name=f"{resource.name}:", value=quantity.strip())

        embed.add_field(
            name="Expected Result:",
            value=f"{self.session_crafts}x {self.item_to_craft.name}",
        )

        embed.add_field(
            name="Subcomponents to craft:", value=f"{self.subcomponents_to_craft}"
        )

        # set exo mek image
        embed.set_thumbnail(url=self.EXOMEK_AVATAR)
        embed.set_footer(text="Ling Ling Bot - Kenny#0947 - discord.gg/2mPhj8xhS5")

        # send the embed
        return embed

    def create_available_materials_embed(
        self, available: dict[items.Item, int], undetermined: dict[items.Item, int]
    ) -> Embed:
        """Sends an embed to the info webhook informing about the crafting
        plan that has been calculated for the ongoing session. Takes its data
        from the session class attributes."""
        # reformat the amounts to make it look nicer
        formatted: dict[items.Item, str] = {}
        desired_order = [
            items.METAL_INGOT,
            items.PASTE,
            items.ELECTRONICS,
            items.SILICA_PEARL,
            items.CRYSTAL,
            items.HIDE,
            items.ORGANIC_POLYMER,
            items.ELEMENT,
        ]
        for resource, amount in available.items():
            formatted[resource] = f"{amount:_}x".replace("_", " ")
        formatted = {k: formatted[k] for k in desired_order if k in formatted}

        # create embed, black sidebar
        embed = Embed(
            type="rich",
            title="Determined available materials!",
            description="Available materials:",
            color=0x000000,
        )

        if undetermined:
            embed.description = f"OCR'd invalid amounts for {undetermined}"

        for resource, quantity in formatted.items():
            embed.add_field(name=f"{resource.name}:", value=quantity.strip())

        embed.set_thumbnail(url=self.EXOMEK_AVATAR)

        # send the embed
        return embed

    def create_embed(self, profit: int, time_taken: int) -> Embed:
        """Creates the final embed displaying the time taken and the amount
        of heavies that have been crafted."""
        embed = Embed(
            type="rich",
            title="Finished crafting!",
            color=0x133DE7,
        )
        embed.add_field(name=f"Time taken:", value=f"{time_taken} seconds")
        embed.add_field(name="Heavies crafted:", value=profit)
        embed.set_thumbnail(url=self.EXOMEK_AVATAR)
        embed.set_footer(text="Ling Ling Bot - Kenny#0947 - discord.gg/2mPhj8xhS5")
        return embed

    def crafting_finished(self) -> bool:
        """Checks if more than 2 minutes passed since the last electronic
        queue up."""

        try:
            time_diff = round(time.time() - self.last_crafted)
            time_left = max(0, (3 * 60) - time_diff)
            if not time_left:
                print("Grinding station has finished crafting.")
                return True
            print(f"{format_seconds(time_left)} left on crafting components..")
            return False
        except AttributeError:
            return True

    def craft(self, item: items.Item, amount: int, put_items: bool = True) -> None:
        """Turns to the exo mek and crafts the given amount of the given item.

        Parameters:
        -----------
        item :class:`Item`:
            The item to craft

        amount :class:`int`:
            The amount to craft, will be A spammed if more than 50.
        """
        assert item.recipe is not None

        if put_items:
            for material, per_craft in item.recipe.items():
                if material is items.ORGANIC_POLYMER:
                    continue

                self.put_item_into_exo_mek(material, per_craft * amount)

        self.turn_to(Stations.EXO_MEK)
        self.exo_mek.access()
        self.exo_mek.inventory.open_tab("crafting")
        self.exo_mek.inventory.craft(item, amount)
        self.exo_mek.inventory.open_tab("inventory")

    def clear_up_exo_mek(self) -> None:
        """Clears the exo mek after a crafting session."""
        self.turn_to(Stations.VAULT)
        self.vault.open()
        self._player.inventory.transfer_all(self.item_to_craft)
        self.vault.close()

        try:
            # clean up the remaining mats from exo mek
            self.turn_to(Stations.EXO_MEK)
            self._player.do_drop_script(items.METAL_INGOT, self.exo_mek.inventory)
            self._player.turn_y_by(-163)
            self.deposit(items.METAL_INGOT)

        except exceptions.NoItemsAddedError:
            print("Failed to take metal from the exo mek!")
            self.exo_mek.close()
            self._player.crouch()

        # get the non heavy mats, drop all on the rest (poly)
        self.turn_to(Stations.EXO_MEK)
        self.exo_mek.access()
        for item in [items.SILICA_PEARL, items.PASTE, items.ELECTRONICS]:
            self.exo_mek.inventory.transfer_all(item)

        self.exo_mek.inventory.drop_all([items.ORGANIC_POLYMER])
        self.exo_mek.close()

        # deposit the lightweight mats
        for item in [items.SILICA_PEARL, items.PASTE, items.ELECTRONICS]:
            self.deposit(item)

    def do_final_craft(self, spawn: bool = True) -> None:
        """Crafts the turrets after all electronics have been crafted.
        Posts the final result to discord as an embed displaying how
        many Heavies ended up crafting (as the result may vary from the
        excpected amount).
        """
        assert self.item_to_craft is not None and self.item_to_craft.recipe is not None
        if spawn:
            self.spawn()

        for item, amount_per_craft in self.item_to_craft.recipe.items():
            if item in [items.ORGANIC_POLYMER, items.AUTO_TURRET, items.ELEMENT]:
                continue

            amount_needed = amount_per_craft * self.session_crafts
            self.put_item_into_exo_mek(item, amount_needed)

        self.craft(self.item_to_craft, self.session_crafts, put_items=False)
        if self.session_crafts < 50:
            while self.exo_mek.inventory.is_crafting():
                self._player.sleep(0.3)
            self.pickup_final_craft(spawn=False)
        else:
            self.exo_mek.close()
            self.last_crafted = time.time()
            self.status = Status.AWAITING_PICKUP

    def pickup_final_craft(self, spawn: bool = True) -> None:
        assert self.item_to_craft is not None
        if spawn:
            self.spawn()

        self.turn_to(Stations.EXO_MEK)

        self.exo_mek.access()
        self.exo_mek.inventory.transfer_all(self.item_to_craft)
        self._player.sleep(1)

        img = self.screen.grab_screen(self._player.inventory._ITEM_REGION)
        stacks_crafted = self._player.inventory.count(self.item_to_craft)
        self._add_crafts_to_statistics(stacks_crafted)
        self.exo_mek.close()

        embed = self._create_items_picked_up_embed(stacks_crafted)
        self._webhook.send_embed(embed, img=img)

        self.clear_up_exo_mek()
        try:
            self._transfer_vault()
            self._transfer_dedi_wall()
        except Exception as e:
            self._webhook.send_error("Transferring items", e)

        self.status = Status.WAITING_FOR_ITEMS
        self.ready = False

    def _add_crafts_to_statistics(self, crafts: int) -> None:
        assert self.item_to_craft is not None

        self.statistics[self.item_to_craft.name] = (
            self.statistics.get(self.item_to_craft.name, 0)
            + crafts * self.item_to_craft.stack_size
        )

    def do_next_craft(self, spawn: bool = True) -> None:
        """Checks if we need to queue another 1000 electronics to reach the
        total needed amount. Returns `False` if we dont, else `True`.

        This method is intended to be checked every 4 minutes by the main
        gacha bot function to either spawn and queue new electronics, or
        start crafting the turrets.
        """
        if not self.subcomponents_to_craft:
            self.do_final_craft(spawn=True)
            return

        if spawn:
            self.spawn()

        for item, amount in self.subcomponents_to_craft.items():
            assert item.recipe is not None
            if not amount:
                continue

            craft_amount = min(amount, 1000)
            self.craft(item, craft_amount)
            self.subcomponents_to_craft[item] -= craft_amount
            self.last_crafted = time.time()
            break

        img = self.screen.grab_screen(self.exo_mek.inventory.CRAFTING_QUEUE)
        self._player.drop_all()
        self._webhook.send_embed(
            self._create_components_queued_embed(item, craft_amount), img=img
        )

        if not any(amount_left for amount_left in self.subcomponents_to_craft.values()):
            self.status = Status.AWAITING_CRAFT
            if amount < 50:
                self.do_final_craft(spawn=False)
        elif amount < 50:
            self._player.sleep(10)
            self.do_next_craft(spawn=False)

    def _create_grinding_finished_embed(self, time_taken: int) -> Embed:
        """Creates an embed to display the materials we have available to craft."""
        embed = Embed(
            type="rich",
            title="Finished grinding!",
            description="Grinding all gear in the gear vault completed.",
            color=0x03DD74,
        )

        embed.add_field(name="Time taken:", value=format_seconds(time_taken))
        embed.add_field(name="Item to craft:", value=self.item_to_craft)

        embed.set_thumbnail(url=self.GRINDER_AVATAR)
        embed.set_footer(text="Ling Ling Bot - Kenny#0947 - discord.gg/2mPhj8xhS5")

        return embed

    def _create_items_picked_up_embed(self, stacks: int) -> Embed:
        assert self.item_to_craft is not None

        embed = Embed(
            type="rich",
            title="Finished grinding station!",
            description="The final result has been picked up.",
            color=0x000000,
        )
        total = self.item_to_craft.stack_size * stacks
        if self.item_to_craft.stack_size > 1:
            amount = f"{total} - {total + self.item_to_craft.stack_size}"
        else:
            amount = str(total)

        embed.add_field(name="Item:", value=self.item_to_craft.name)
        embed.add_field(name="Amount crafted:", value=amount)

        embed.set_thumbnail(url=self.EXOMEK_AVATAR)
        return embed

    def _create_components_queued_embed(self, item: items.Item, amount: int) -> Embed:
        embed = Embed(
            type="rich",
            title="Subcomponents have been queued!",
            description="A chunk of subcomponents for the final craft has been queued up.",
            color=0x000000,
        )

        embed.add_field(name="Component:", value=item.name)
        embed.add_field(name="Amount queued:", value=amount)
        embed.add_field(name="Amount left:", value=self.subcomponents_to_craft[item])

        if item is items.ELECTRONICS:
            embed.set_thumbnail(url=self.ELECTRONICS_AVATAR)
        else:
            embed.set_thumbnail(url=self.EXOMEK_AVATAR)

        embed.set_footer(text="Ling Ling Bot - Kenny#0947 - discord.gg/2mPhj8xhS5")
        return embed

    def _transfer_dedi_wall(self) -> None:
        """Spawns at the grindingtransfer bed and transfers the materials
        left after crafting into the next dediwall, so that the ARB station
        can use the metal and it does not clog up the grinding dedis.

        Im sure theres a cleaner way to do this but it works.
        """
        bed = Bed("dedi_transfer")
        self._player.look_down_hard()
        self._player.prone()
        bed.spawn()
        self._tribelog.check_tribelogs()
        self._player.spawn_in()

        for i in range(2):
            # transfer pearls and paste
            self._player.turn_x_by(-90, delay=0.5)
            if i != 0:
                self._player.turn_y_by(50, delay=0.5)

            self.dedi.open()
            self.dedi.inventory.transfer_all()
            self.dedi.close()
            self._player.sleep(2)

            for _ in range(2):
                self._player.turn_x_by(90, delay=0.5)
            self.dedi.deposit([items.SILICA_PEARL], get_amount=False)
            self._player.turn_x_by(-90, delay=0.5)
            self._player.turn_x_by(-90, delay=0.5)

            self.dedi.deposit([items.PASTE], get_amount=False)
            self._player.turn_x_by(90, delay=0.5)

            if i != 0:
                self._player.turn_y_by(-50, delay=0.5)

        self._player.turn_x_by(-160, delay=0.5)

        # take electronics
        self.dedi.open()
        self.dedi.inventory.transfer_all()
        self.dedi.close()
        self._player.sleep(2)

        # take metal
        self._player.turn_y_by(50)
        self.dedi.open()
        self.dedi.inventory.transfer_all()
        self.dedi.close()
        self._player.sleep(2)

        for _ in range(2):
            self._player.turn_x_by(160, delay=0.5)

        self.dedi.deposit([items.METAL_INGOT], get_amount=False)
        self._player.turn_y_by(-50, delay=0.5)
        self.dedi.deposit([items.ELECTRONICS], get_amount=False)

    def _transfer_vault(self) -> None:
        bed = Bed("vault_transfer")
        self._player.look_down_hard()
        self._player.prone()
        bed.spawn()
        self._player.spawn_in()

        self.vault.open()
        self.vault.inventory.transfer_all()
        self.vault.close()

        for turn in [-100, -50, -55, -50]:
            self._player.turn_x_by(turn, delay=0.5)

            self.vault.open()
            self._player.inventory.transfer_all()
            self.vault.close()
            self._player.sleep(0.5)


import db_connect
import getallid

class Schedule:
    def __init__(self, sdate, start_time, end_time, status, remarks):
        self.doctor_id = getallid.get_d_id()
        self.patient_id = getallid.get_p_id()
        self.date = sdate
        self.start_time = start_time
        self.end_time = end_time
        self.status = status
        self.remarks = remarks

    def add_schedule(self):
        mycon = db_connect.connect_sql()
        cursor = mycon.cursor()
        sql = "INSERT INTO schedule (patient_id,doctor_id,  sdate, start_time, end_time, sstatus, remarks) VALUES (" \
              "%s, %s,%s,%s, %s,%s,%s) "
        val = (self.patient_id, self.doctor_id, self.date, self.start_time, self.end_time, self.status, self.remarks)
        cursor.execute(sql, val)
        mycon.commit()

    def update_schedule(self, sid, status):
        mycon = db_connect.connect_sql()
        cursor = mycon.cursor()
        sql = f"UPDATE schedule SET sstatus= %s WHERE schedule_id= {sid}"
        val = (status,)
        cursor.execute(sql, val)
        mycon.commit()

s = Schedule('2021-10-13', "5:00 PM", "6:00 PM", "Not Examined", "NA")
s.add_schedule()


import os
from pdfminer.pdfparser import PDFParser, PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTTextLine

#alternate method of accessing file
# base_path = "C://some_folder"
# my_file = os.path.join(base_path + "/" + "test_pdf.pdf")
# Loop through base_path

# can make list of pdfs to loop through (read filename, )

listOfFilenames = []
base_path = "C://some_folder"

# ---------------------------   Loop through files in base_path --------------------------
# ---------------------------   Append their filenames to list   --------------------------
for filename in base_path:
  if filename.endswith(".pdf"):
    listOfFilenames.append(filename)

# ---------------------------   Loop through listOfFilenames list of filenames --------------------------
# ---------------------------   Process each pdf file into txt      --------------------------
# for list in listOfFilename
# fp = open('./testpdf2.pdf', 'rb')
# for filename in listOfFilenames:
#   fp = open(filename, 'rb')
#   password = ""
#   extracted_text = ""
#Process each filename in loop

# ---------------------------   Create log entry for each processed file --------------------------
# ---------------------------  to trackdown the error invoices      --------------------------
log_file = os.path.join(base_path + "/" + "pdf_log.txt")

# - ----- -- - -DEPRECATED------
# pull file
# fp = open('./testpdf2.pdf', 'rb')
# password = ""
# extracted_text = ""

# ---------------------------    PROGRAM   --------------------------

# document requests objects from pdf
# parser stores objects from pdf into document
parser = PDFParser(fp)
document = PDFDocument()
parser.set_document(document)
document.set_parser(parser)
document.initialize('')

# Create PDFResourceManager object that stores shared resources such as fonts or images
rsrcmgr = PDFResourceManager()

# set parameters for analysis
laparams = LAParams()
laparams.char_margin = 1.0
laparams.word_margin = 1.0

# Create a PDFDevice object which translates interpreted information into desired format
# Device needs to be connected to resource manager to store shared resources
# device = PDFDevice(rsrcmgr)
# Extract the decive to page aggregator to get LT object elements
device = PDFPageAggregator(rsrcmgr, laparams=laparams)

# Create interpreter object to process page content from PDFDocument
# Interpreter needs to be connected to resource manager for shared resources and device
interpreter = PDFPageInterpreter(rsrcmgr, device)
extracted_text = ''

for page in document.get_pages():
    interpreter.process_page(page)
    # The device renders the layout from interpreter
    layout = device.get_result()
    #look for LTTextBox LTTextLine in lt_obj list
    for lt_obj in layout:
        if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine):
            extracted_text += lt_obj.get_text()

#close the pdf file
fp.close()

# print (extracted_text.encode("utf-8"))
# The outfile should be in binary mode.
with open('./eg2.txt', "wb") as my_log:
	my_log.write(extracted_text.encode("utf-8"))
print("Done !!")

# ---------------------------   NOTES     --------------------------

# directory = 'the/directory/you/want/to/use'

# for filename in os.listdir(directory):
    #   if filename.endswith(".txt"):
    #     f = open(filename)
    #     lines = f.read()
    #     print(lines[10])
    #     continue
    # else:
    # continue

# pull file
# fp = open('./testpdf2.pdf', 'rb')
# password = ""
# extracted_text = ""


#!/usr/bin/python

import sys

newLines = []
lineNumberCount = 1

if len(sys.argv) != 2:
    print("Invalid arguments: Please put SUT as second argument")
    sys.exit()

print("Generating Mutant List for:"), str(sys.argv[1])
newLines.append("Library of Mutants\n\n")
newLines.append("Line\t\tMutant\n")
try:
    with open(str(sys.argv[1])) as f:
        lines = f.readlines()

        for line in lines:
            if "/" in line or "+" in line or "-" in line or "*" in line: #or "=" in line:
                l = line[line.find('=') + 1:].strip()
                print(l)
                newLines.append(str(lineNumberCount) +  "\t\t" + str(l) +"\n")
            lineNumberCount+=1

        # lines = [l for l in lines if "/" in l]
        with open("library-of-mutants.txt", "w") as f1:
            f1.writelines(newLines)
except:
    print("Error with file")
    sys.exit()


from tkinter import *

def beoordeel(goedgekeurd):
    """Beoordeeld wat er gedaan moet worden"""
    import mainadmin, mainclient, logfilegui
    if goedgekeurd == 'quit':
        exit()
    elif goedgekeurd == 'Client':
        mainclient.clientmain('Client')
    elif goedgekeurd == 'Check Tweet':
        mainadmin.tweetAck('NS Approval App')
    elif goedgekeurd == 'log':
        logfilegui.logmain()

def main():
    '''Main menu'''
    root = Tk()
    root.title('NS Feedback Poster')

    background_image = PhotoImage(file="GUI/NS_logo.png")
    background_label = Label(root, image=background_image)
    background_label.grid(rowspan=30, columnspan=12)
    root.resizable(False, False)

    label_titel = Label(root, text='Main Menu', bg='#FFCC18')
    label_titel.config(font=('Helvetica', 24))
    label_titel.grid(row=0, columnspan=12, sticky='n')

    client = lambda:beoordeel('Client')
    check = lambda:beoordeel('Check Tweet')
    quit = lambda:beoordeel('quit')
    log = lambda:beoordeel('log')

    button_client = Button(root, text='Make tweets', bg="green", font=('Helvetica', 30), command=client)
    button_check = Button(root, text='Acknowledge tweets', bg="Green",font=('Helvetica', 30), command=check)
    button_log = Button(root, text='View logfile', bg='Gray', font=('Helvetica', 30), command=log)
    button_quit = Button(root, text='Quit program', bg='red', font=('Helvetica', 30), command=quit)

    button_client.grid(row=1, columnspan=12, sticky='n')
    button_check.grid(row= 2, columnspan=12, sticky='n')
    button_log.grid(row=28, columnspan=12)
    button_quit.grid(row=29, columnspan=12)

    root.mainloop()
main()


#memoizacao
f_cache = {}
fib_cache = {}
def f(n):
	if n in f_cache:
		return f_cache[n]
	counter = 0
	if n < 2:
		counter = 1
		return counter
	counter = 1+f(n-1)+f(n-2)
	f_cache[n] = counter
	return counter

def fib(x):
	if x in fib_cache:
		return fib_cache[x]
	if x <= 2:
		return 1
	val = fib(x-1)+fib(x-2)
	fib_cache[x] = val
	return val

testCases = int(input())

for i in range(testCases):
	num = int(input())
	a = f(num)
	b = fib(num)
	print('fib(%d) = %d calls = %d' % (num,a-1,b))


import numpy as np
from pyscf_TDDFT_ris import math_helper
import time

def Davidson(matrix_vector_product,
                    hdiag,
                    N_states=20,
                    conv_tol=1e-5,
                    max_iter=25 ):
    '''
    AX = XΩ
    Davidson frame, can use different initial guess and preconditioner
    initial_guess is a function, takes the number of initial guess as input
    preconditioner is a function, takes the residual as the input
    '''
    print('====== Davidson Diagonalization Starts ======')

    D_start = time.time()

    A_size = hdiag.shape[0]
    print('size of A matrix =', A_size)
    size_old = 0
    size_new = min([N_states+8, 2*N_states, A_size])

    max_N_mv = max_iter*N_states + size_new
    V_holder = np.zeros((A_size, max_N_mv))
    W_holder = np.zeros_like(V_holder)
    sub_A_holder = np.zeros((max_N_mv,max_N_mv))
    '''
    generate the initial guesss and put into the basis holder V_holder
    '''
    V_holder = math_helper.TDA_diag_initial_guess(V_holder=V_holder, N_states=size_new, hdiag=hdiag)

    # V_holder[:,:size_new] = initial_vectors[:,:]

    subcost = 0
    MVcost = 0
    GScost = 0
    subgencost = 0
    full_cost = 0
    print('step max||r||   sub_A.shape')
    for ii in range(max_iter):

        MV_start = time.time()
        W_holder[:, size_old:size_new] = matrix_vector_product(V_holder[:,size_old:size_new])
        MV_end = time.time()
        iMVcost = MV_end - MV_start
        MVcost += iMVcost

        subgencost_start = time.time()
        sub_A_holder = math_helper.gen_VW(sub_A_holder, V_holder, W_holder, size_old, size_new)
        sub_A = sub_A_holder[:size_new,:size_new]
        subgencost_end = time.time()
        subgencost += subgencost_end - subgencost_start
        # sub_A = math_helper.utriangle_symmetrize(sub_A)
        # sub_A = math_helper.symmetrize(sub_A)

        '''
        Diagonalize the subspace Hamiltonian, and sorted.
        sub_eigenvalue[:N_states] are smallest N_states eigenvalues
        '''

        subcost_start = time.time()
        sub_eigenvalue, sub_eigenket = np.linalg.eigh(sub_A)
        sub_eigenvalue = sub_eigenvalue[:N_states]
        sub_eigenket = sub_eigenket[:,:N_states]
        subcost_end = time.time()
        subcost += subcost_end - subcost_start

        full_cost_start = time.time()
        full_guess = np.dot(V_holder[:,:size_new], sub_eigenket)
        full_cost_end = time.time()
        full_cost += full_cost_end - full_cost_start

        AV = np.dot(W_holder[:,:size_new], sub_eigenket)
        residual = AV - full_guess * sub_eigenvalue

        r_norms = np.linalg.norm(residual, axis=0).tolist()
        max_norm = np.max(r_norms)
        print('{:<3d}  {:<10.4e} {:<5d}'.format(ii+1, max_norm, sub_A.shape[0]))
        if max_norm < conv_tol or ii == (max_iter-1):
            break

        index = [r_norms.index(i) for i in r_norms if i>conv_tol]

        new_guess = math_helper.TDA_diag_preconditioner(residual = residual[:,index],
                                            sub_eigenvalue = sub_eigenvalue[index],
                                            hdiag = hdiag)

        GScost_start = time.time()
        size_old = size_new
        V_holder, size_new = math_helper.Gram_Schmidt_fill_holder(V_holder, size_old, new_guess, double=True)
        GScost_end = time.time()
        GScost += GScost_end - GScost_start

    # energies = sub_eigenvalue*parameter.Hartree_to_eV

    D_end = time.time()
    Dcost = D_end - D_start

    if ii == max_iter-1:
        print('=== TDA Failed Due to Iteration Limit ===')
        print('current residual norms', r_norms)

    # print('energies:')
    # print(energies)
    print('Finished in {:d} steps, {:.2f} seconds'.format(ii+1, Dcost))
    print('Maximum residual norm = {:.2e}'.format(max_norm))
    print('Final subspace size = {:d}'.format(sub_A.shape[0]))
    for enrty in ['MVcost','GScost','subgencost','subcost','full_cost']:
        cost = locals()[enrty]
        print("{:<10} {:<5.4f}s {:<5.2%}".format(enrty, cost, cost/Dcost))
    print('========== Davidson Diagonalization Done ==========')
    return sub_eigenvalue, full_guess

def Davidson_Casida(matrix_vector_product,
                        hdiag,
                        N_states=20,
                        conv_tol=1e-5,
                        max_iter=25 ):
    '''
    [ A B ] X - [1   0] Y Ω = 0
    [ B A ] Y   [0  -1] X   = 0

    '''
    print('======= TDDFT-ris Eiegn Solver Statrs =======')

    TD_start = time.time()
    A_size = hdiag.shape[0]
    print('size of A matrix =', A_size)
    size_old = 0
    size_new = N_states

    max_N_mv = (max_iter+1)*N_states

    V_holder = np.zeros((A_size, max_N_mv))
    W_holder = np.zeros_like(V_holder)

    U1_holder = np.zeros_like(V_holder)
    U2_holder = np.zeros_like(V_holder)

    VU1_holder = np.zeros((max_N_mv,max_N_mv))
    VU2_holder = np.zeros_like(VU1_holder)
    WU1_holder = np.zeros_like(VU1_holder)
    WU2_holder = np.zeros_like(VU1_holder)

    VV_holder = np.zeros_like(VU1_holder)
    VW_holder = np.zeros_like(VU1_holder)
    WW_holder = np.zeros_like(VU1_holder)

    '''
    set up initial guess V= TDA initila guess, W=0
    '''

    V_holder = math_helper.TDA_diag_initial_guess(V_holder = V_holder,
                                         N_states = N_states,
                                         hdiag = hdiag)
    subcost = 0
    MVcost = 0
    GScost = 0
    subgencost = 0
    full_cost = 0

    print('step maximum residual norm')
    for ii in range(max_iter):

        V = V_holder[:,:size_new]
        W = W_holder[:,:size_new]

        '''
        U1 = AV + BW
        U2 = AW + BV
        '''
        # print('size_old =', size_old)
        # print('size_new =', size_new)
        MV_start = time.time()
        U1_holder[:, size_old:size_new], U2_holder[:, size_old:size_new] = matrix_vector_product(
                                                            X=V[:, size_old:size_new],
                                                            Y=W[:, size_old:size_new])
        MV_end = time.time()
        MVcost += MV_end - MV_start

        U1 = U1_holder[:,:size_new]
        U2 = U2_holder[:,:size_new]

        subgenstart = time.time()

        '''
        [U1] = [A B][V]
        [U2]   [B A][W]

        a = [V.T W.T][A B][V] = [V.T W.T][U1] = VU1 + WU2
                     [B A][W]            [U2]
        '''

        (sub_A, sub_B, sigma, pi,
        VU1_holder, WU2_holder, VU2_holder, WU1_holder,
        VV_holder, WW_holder, VW_holder) = math_helper.gen_sub_ab(
                      V_holder, W_holder, U1_holder, U2_holder,
                      VU1_holder, WU2_holder, VU2_holder, WU1_holder,
                      VV_holder, WW_holder, VW_holder,
                      size_old, size_new)

        subgenend = time.time()
        subgencost += subgenend - subgenstart

        '''
        solve the eigenvalue omega in the subspace
        '''
        subcost_start = time.time()
        omega, x, y = math_helper.TDDFT_subspace_eigen_solver(sub_A, sub_B, sigma, pi, N_states)
        subcost_end = time.time()
        subcost += subcost_end - subcost_start

        '''
        compute the residual
        R_x = U1x + U2y - X_full*omega
        R_y = U2x + U1y + Y_full*omega
        X_full = Vx + Wy
        Y_full = Wx + Vy
        '''
        full_cost_start = time.time()
        X_full = np.dot(V,x)
        X_full += np.dot(W,y)

        Y_full = np.dot(W,x)
        Y_full += np.dot(V,y)

        R_x = np.dot(U1,x)
        R_x += np.dot(U2,y)
        R_x -= X_full*omega

        R_y = np.dot(U2,x)
        R_y += np.dot(U1,y)
        R_y += Y_full*omega

        full_cost_end = time.time()
        full_cost += full_cost_end - full_cost_start

        residual = np.vstack((R_x, R_y))
        r_norms = np.linalg.norm(residual, axis=0).tolist()
        max_norm = np.max(r_norms)
        print('{:<3d}  {:<10.4e}'.format(ii+1, max_norm))
        if max_norm < conv_tol or ii == (max_iter -1):
            break

        index = [r_norms.index(i) for i in r_norms if i > conv_tol]

        '''
        preconditioning step
        '''
        X_new, Y_new = math_helper.TDDFT_diag_preconditioner(R_x = R_x[:,index],
                                                   R_y = R_y[:,index],
                                                 omega = omega[index],
                                                 hdiag = hdiag)
        '''
        GS and symmetric orthonormalization
        '''
        size_old = size_new
        GScost_start = time.time()
        V_holder, W_holder, size_new = math_helper.VW_Gram_Schmidt_fill_holder(V_holder=V_holder,
                                                                                W_holder=W_holder,
                                                                                X_new=X_new,
                                                                                Y_new=Y_new,
                                                                                m=size_old,
                                                                                double=False)
        GScost_end = time.time()
        GScost += GScost_end - GScost_start

        if size_new == size_old:
            print('All new guesses kicked out during GS orthonormalization')
            break

    TD_end = time.time()

    TD_cost = TD_end - TD_start

    if ii == (max_iter -1):
        print('=== TDDFT eigen solver Failed Due to Iteration Limit ===')
        print('current residual norms', r_norms)

    print('Finished in {:d} steps, {:.2f} seconds'.format(ii+1, TD_cost))
    print('final subspace', sub_A.shape[0])
    print('max_norm = {:.2e}'.format(max_norm))
    for enrty in ['MVcost','GScost','subgencost','subcost','full_cost']:
        cost = locals()[enrty]
        print("{:<10} {:<5.4f}s {:<5.2%}".format(enrty, cost, cost/TD_cost))
    print('======= TDDFT Eigen Solver Done =======' )
    # energies = omega*parameter.Hartree_to_eV

    return omega, X_full, Y_full


from test_helpers import *
from pagefeed.models import transform
from pagefeed.lib.BeautifulSoup import BeautifulSoup as b_soup

class TransformTest(TestCase):
	def tearDown(self):
		for trans in transform.Transform.all():
			trans.delete()

	def test_should_create_the_appropriate_model(self):
		orig_follow = transform.Transform.create(action='follow', selector='foo', host_match='bar', owner=fixtures.a_user, name='blah')
		orig_follow.put()
		follows = transform.Transform.all().fetch(2)
		self.assertEqual(len(follows), 1)
		follow = follows[0]
		self.assertEqual(type(follow), transform.FollowTransform)
		self.assertEqual(follow.selector, 'foo')
		self.assertEqual(follow.host_match, 'bar')
		self.assertEqual(follow.name, 'blah')
		self.assertEqual(follow.owner, fixtures.a_user)

	@ignore("todo")
	def test_should_assign_index_automatically(self):
		pass

	def test_should_find_transforms_based_on_owner(self):
		kw = dict(action='follow', selector='foo', name='foo')
		transform.Transform.create(host_match='xyz', owner=fixtures.a_user, **kw).put()
		transform.Transform.create(host_match='bar', owner=fixtures.a_user, **kw).put()

		self.assertEqual(len(transform.Transform.find_all(user=fixtures.a_user).fetch(10)), 2)
		self.assertEqual(len(transform.Transform.find_all(user=fixtures.another_user).fetch(10)), 0)

	@ignore("needs transform equality")
	def test_should_find_transforms_based_on_host_and_owner(self):
		orig_follow = transform.Transform.create(action='follow', selector='foo', host_match='bar', owner=fixtures.a_user, name='foo')
		orig_follow.put()

		follows = list(transform.FollowTransform.find_all(user=fixtures.a_user, host='bar'))
		self.assertEqual(len(follows), 1)
		self.assertEqual(orig_follow, follows[0])

	def test_should_follow_url_for_follow_action(self):
		selector = "div[class=content]|a[1]"
		xform = transform.FollowTransform(owner=fixtures.a_user, selector=selector, host_match='localhost', name='blah')
		html = """

					blah
					blah
					blah

			"""
		page = mock('page').with_children(host='')
		expect(page).use_content_url('http://linked_url')
		xform.apply(page, b_soup(html))
		page._fetch_raw_content()

	def test_should_not_follow_url_when_selection_fails(self):
		selector = "div[class=content]|a[1]"
		xform = transform.FollowTransform(owner=fixtures.a_user, action='follow', selector=selector, host_match='localhost', name='test_transform')
		html = """

					blah

			"""
		page = mock('page').with_children(raw_content=html)
		expect(page).use_content_url.never()
		expect(page).error("transform FollowTransform failed: no links found")
		self.assertRaises(transform.TransformError, lambda: transform.Transform.apply_transform(xform, page, b_soup(html)))

class SelectorTest(TestCase):
	def assertExtraction(self, content, selector, expected_results):
		soup = b_soup(content)
		results = transform.apply_selector(soup, selector)
		string_results = map(str, results)
		self.assertEqual(string_results, expected_results)

	def test_should_apply_a_complex_selector(self):
		selector="div[class=the_class]|[0]|p[2],a"
		html = """

zero

one

two

				link1

			link2
			"""
		self.assertExtraction(html, selector,
			["
two
", "link1", "link2"])

	def test_should_select_on_presence_of_attribute(self):
		html = """

not here

here

			"""
		self.assertExtraction(html, "div[class]",
			['
here
'])

	def test_should_select_on_presence_of_attribute_without_tag(self):
		html = """

not here

here

			"""
		self.assertExtraction(html, "[class=present]",
			['
here
'])

	def test_should_select_index(self):
		html = """

0

1

			"""
		self.assertExtraction(html, "div[0],div[-2]",
			['
0
', '
0
'])

	def test_should_select_subnode_and_attribute(self):
		html = """

-1

0

1

			"""
		self.assertExtraction(html, "div[class]|[0]",
			['
0
'])

	def test_should_select_multiple_items(self):
		html = """

0

1

				bold

			"""
		self.assertExtraction(html, "div,b",
			['
0
', '
1
', 'bold'])


"""
Reads .db files produced by ChangeAbsPositionAz.tnhs script
Counts hits on absorber for each angle and position
Plots heatmap
Plots yposition - angle linear regression
"""

import os
import glob
import sqlite3
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.api as sm
from statsmodels.api import add_constant

def view(dbname):
    """ Reads .db files
    :param str dbname: .db file path
    :returns: contents of Photons and Surfaces tables
    """
    conn = sqlite3.connect(dbname)
    df = pd.read_sql_query("SELECT * FROM Photons", con=conn)
    ids = pd.read_sql_query("SELECT * FROM Surfaces", con=conn)
    conn.close()
    return df, ids

def load(dbfiles):
    """ Counts hits on absorber and mirror aperture (auxiliary surface) and calculates intercept factor

    :param list dbfiles: list of .dbfiles
    :returns: dataframe with intercept factor for each position and angle
    """
    df = pd.DataFrame(columns=['angle', 'position', 'intercept factor'])
    for i in dbfiles:
        fname = os.path.basename(i).split('_')
        pos = fname[0]  # extracts abs position from filename
        angle = fname[1][:-3]  # extract angle from filename
        pos = float(pos) * 1000  # converts abs position to mm
        photons, surfaces = view(i)
        aux_id = surfaces['id'][surfaces["Path"].str.contains("aux_surface")].values[0]  # Finds auxiliary surface id
        aux_hits = photons['surfaceID'].value_counts()[aux_id]
        try:
            absorber_id = surfaces['id'][surfaces["Path"].str.contains("Tracking_unit")].values[0]  # Finds absorber id
            abs_hits = photons['surfaceID'].value_counts()[absorber_id]
            nj = 100*abs_hits/aux_hits
            df = df.append({'angle': angle, 'position': pos, 'intercept factor': nj},
                             ignore_index=True)
        except IndexError:
            print('No absorber surface in:', os.path.basename(i), 'skipping...')
            pass
    df = df.astype(int)
    df = df.pivot('position', 'angle', 'intercept factor')
    df = df.sort_values(by='position', ascending=False)
    return df

def get_maxnj(df):
    """
    Selects max intercept factor values for each azimuth angle
    :param df: pd.DataFrame
    :return: pd.DataFrame
    """
    dfnj = pd.DataFrame(columns=['angle','pos','maxnj'])
    angles = list(df)
    for j in angles:
        max_nj = df[j].nlargest(1).values[0]
        bestpos = df[j].nlargest(1).index.values[0]
        dfnj = dfnj.append({'angle':j,'pos':bestpos, 'maxnj': max_nj},ignore_index=True)
    dfnj = dfnj.set_index('angle',drop=True)
    return dfnj

def linregres(df):
    """ Calculates linear regression parameters and standard errors"""
    x = df.index.values
    y = df['pos'].values.tolist()

    X = add_constant(x)  # include constant (intercept) in ols model
    mod = sm.OLS(y, X)
    results = mod.fit()
    #    intercept, slope = results.params
    #    intercept_stderr, slope_stderr = results.bse
    #    rsquared = results.rsquared
    return results

def linear_fit(df,picname = None):
    """ Plots Y-angle scatter, line and equation
    If picname specified, saves plot"""
    x = df.index.values
    y = df['pos'].values.tolist()
    res = linregres(df)
    intercept,slope = res.params

    fig, axes = plt.subplots()
    axes = sns.regplot(x = x, y=y, line_kws={'label': f"$y={slope:.4f}x{intercept:+.4f}$"})
    axes.legend()
    axes.set_ylabel('Y (m)')
    axes.set_xlabel(r'$\theta_{az} (°)$')
    plt.show()
    try:
        fig.savefig(picname)
    except:
        pass

def linregres2csv(df,csvname=None):
    """ Prints linear regression parameters and standard errors
    If csvname specified, saves dataframe to .csv"""
    linres = linregres(df)
    rdf = pd.DataFrame(columns=['Intercept','Slope'])
    rdf.loc['Value']= linres.params[:] # Fill rows
    rdf.loc['SE']= linres.bse[:]
    rdf.loc['StDev'] = linres.bse*np.sqrt(linres.nobs)
    rdf.loc['R2'] = linres.rsquared
    rdf.loc['n'] = linres.nobs
    rdf.loc['P-value'] = linres.pvalues[:]
    rdf = rdf.T # Transpose
    rdf.index.name='Parameter'
    print(rdf)
    try:
        rdf.to_csv(csvname,float_format='%g')
    except:
        pass

def make_dir(dirname):
    if not os.path.exists(dirname):
        os.makedirs(dirname)
    return f"{os.getcwd()}/{dirname}/"

dbfileslist = glob.glob(os.getcwd() + '/raw/*.db') # creates list of .db files in /raw
dfr = load(dbfileslist)

ax = sns.heatmap(dfr,cbar_kws={'label': '$\gamma_{th}$'})

maxnj = get_maxnj(dfr)
maxnj['pos'] = maxnj['pos']/1000 # converts to m

dfleft = maxnj.loc[:180] # selects angles up to 180°
dfright = maxnj.loc[181:] # selects angles over 180°

linear_fit(dfleft,picname=make_dir('results/plots')+'linregres_left.png')
linear_fit(dfright,picname=make_dir('results/plots')+'linregres_right.png')
linregres2csv(dfleft,csvname=make_dir('results')+'regresults_left.csv')
linregres2csv(dfright,csvname=make_dir('results')+'regresults_right.csv')


with open('d2-input.txt', 'r') as file:
    lines = file.readlines()

horizontal = 0
depth = 0
aim = 0
for line in lines:
    list = line.split(" ")
    command = list[0]
    num = int(list[1])
    if command == "forward":
        horizontal += num
        depth += aim * num
    elif command == "down":
        aim += num
    elif command == "up":
        aim -= num

print(horizontal*depth)


class Solution:
    """
    This question is very tricky due to the edge case, when a number is 999...9.
    Therefore, a carry flag must be established so that, after iterating through all digits,
    should there still be a carry, a one will be inserted to the head of the list.
    """

    def plusOne(self, digits: List[int]) -> List[int]:
        carry = False

        for i in range(len(digits) - 1, -1, -1):
            digits[i] += 1

            if digits[i] == 10:
                digits[i] = 0
                carry = True
            else:
                carry = False
                break

        if carry == True:
            digits.insert(0, 1)

        return digits


from random import randrange

NB_SQUARES=4

def shuffle_board(n=NB_SQUARES, N=100):
    # Renvoie un plateau bien mélangé
    # N : Nombre de mélanges

    def echange(board, empty):
        i, j=empty
        V=[(a,b) for (a, b) in
                [(i, j+1),(i, j-1), (i-1, j), (i+1,j)]
                if a in range(n) and b in range(n)]
        ii, jj=V[randrange(len(V))]
        board[ii][jj], board[i][j]=board[i][j],board[ii][jj]
        return ii, jj

    board=[[n*lin+col+1 for col in range(n)]
        for lin in range(n)]
    board[n-1][n-1]=0
    empty=(n-1,n-1)

    for i in range(N):
        empty=echange(board, empty)

    return board

def voisins(n,i,j):
    D=[]

    if(i-1)>=0:
        D.append(board[i-1][j])
    if(j-1)>=0:
        D.append(board[i][j-1])
    if(j+1)<=3:
        D.append(board[i][j+1])
    if(i+1)<=3:
        D.append(board[i+1][j])

    return D

def caseVide(board, i, j):
    n=len(board)
    for line, col in voisins(n, i,j):
        if board[line][col]==0:
            return line,col
    return None

def find_empty():
    for i in range(NB_SQUARES):
        for j in range(NB_SQUARES):
            if board[i][j]==0:
                x = [i,j]
    return x

def print_board(board):
    print('\n'.join([' '.join(["%2s"%v for v in L ]) for L in board]))

board = shuffle_board(4,100)
x = find_empty()
print_board(board)

print()
print(x)
print(voisins(NB_SQUARES,0,0))
print(caseVide(board,0,0))


import io
import json

from collections import namedtuple, deque
from typing import Iterator, List, Optional, Tuple, NamedTuple

from ms_deisotope.data_source.scan import ScanBase
from ms_deisotope.peak_dependency_network import Interval, IntervalTreeNode

class BoundingBox(NamedTuple):
    mz: Interval
    rt: Interval

    def merge(self, other: 'BoundingBox'):
        min_mz = min(self[0].start, other[0].start)
        max_mz = max(self[0].end, other[0].end)
        min_rt = min(self[1].start, other[1].start)
        max_rt = max(self[1].end, other[1].end)
        return self.__class__(Interval(min_mz, max_mz), Interval(min_rt, max_rt))

    def overlaps(self, other: 'BoundingBox'):
        return self.mz.overlaps(other.mz) and self.rt.overlaps(other.rt)

    def __add__(self, other):
        return self.merge(other)

def extract_intervals(scan_iterator: Iterator[Tuple[ScanBase, List[ScanBase]]], time_radius: float=5., mz_lower: float=2., mz_higher: float=3.):
    intervals = []
    for scan, products in scan_iterator:
        for product in products:
            if product.precursor_information is None:
                continue
            intervals.append(BoundingBox(
                Interval(max(0, product.precursor_information.mz - mz_lower),
                         product.precursor_information.mz + mz_higher),
                Interval(max(0, product.scan_time - time_radius),
                         product.scan_time + time_radius)))
    return intervals

def nest_2d_intervals(intervals: List[BoundingBox]) -> List[Interval]:
    out = []
    for interval in intervals:
        mz, rt = interval
        rt.members.append(mz)
        out.append(rt)
    return out

def make_rt_tree(intervals: List[BoundingBox]) -> IntervalTreeNode:
    nested = nest_2d_intervals(intervals)
    tree = IntervalTreeNode.build(nested)
    return tree

class ScanIntervalTree(object):
    rt_tree: IntervalTreeNode
    original_intervals: Optional[BoundingBox]

    @classmethod
    def build(cls, scan_iterator, time_radius=3., mz_lower=2., mz_higher=3.):
        intervals = extract_intervals(
            scan_iterator, time_radius=time_radius,
            mz_lower=mz_lower, mz_higher=mz_higher)
        return cls(make_rt_tree(intervals), intervals)

    def __init__(self, rt_tree, original_intervals=None):
        self.rt_tree = rt_tree
        self.original_intervals = original_intervals

    def get_mz_intervals_for_rt(self, rt_point: float) -> List[Interval]:
        if self.rt_tree is None:
            return [[0, float('inf')]]
        intervals = [
            i.members[0] for i in self.rt_tree.contains_point(rt_point)
        ]
        intervals = sorted([[i.start, i.end] for i in intervals])
        out = []
        if len(intervals) == 0:
            return []
        last = intervals[0]
        for interval in intervals[1:]:
            if interval[0] < last[1]:
                last[1] = interval[1]
            else:
                out.append(last)
                last = interval
        out.append(last)
        return out

    def __call__(self, scan: ScanBase):
        intervals = self.get_mz_intervals_for_rt(scan.scan_time)
        return intervals

    def serialize(self, handle: io.TextIOBase):
        work_queue = deque()
        id_counter = 0
        node_id_map = {}

        def serialize_contained(interval_list):
            out = []
            for interval in interval_list:
                item = {"start": interval.start, "end": interval.end}
                inner = [{"start": i.start, "end": i.end}
                         for i in interval.members]
                item['members'] = inner
                out.append(item)
            return out

        root = self.rt_tree
        node_dict = {
            "id": id_counter,
            "contained": serialize_contained(root.contained),
            "center": root.center,
            "start": root.start,
            "end": root.end,
            "left": None,
            "right": None
        }
        root_id = node_dict['id']
        node_id_map[node_dict['id']] = node_dict
        id_counter += 1
        work_queue.append((root.left, node_dict['id'], 'left'))
        work_queue.append((root.right, node_dict['id'], 'right'))

        while work_queue:
            node, parent_id, side = work_queue.popleft()
            if node is None:
                continue
            node_dict = {
                "id": id_counter,
                "contained": serialize_contained(node.contained),
                "center": node.center,
                "start": node.start,
                "end": node.end,
                "left": None,
                "right": None
            }
            node_id_map[node_dict['id']] = node_dict
            id_counter += 1
            parent = node_id_map[parent_id]
            parent[side] = node_dict
            work_queue.append((node.left, node_dict['id'], 'left'))
            work_queue.append((node.right, node_dict['id'], 'right'))

        json.dump(node_id_map[root_id], handle)

    @classmethod
    def load(cls, handle: io.TextIOBase):
        data = json.load(handle)

        def load_contained(interval_list):
            out = []
            for item in interval_list:
                inner = [Interval(**i) for i in item['members']]
                out.append(Interval(start=item['start'], end=item['end'], members=inner))
            return out

        def load_node_single(node_dict):
            contained = node_dict.get("contained", [])
            contained = load_contained(contained)
            node = IntervalTreeNode(
                node_dict['center'], left=None, contained=contained,
                right=None)
            node.start = node_dict['start']
            node.end = node_dict['end']
            return node
        root = load_node_single(data)

        work_queue = deque()

        work_queue.append((data['left'], root, 'left'))
        work_queue.append((data['right'], root, 'right'))

        while work_queue:
            node_dict, parent, side = work_queue.popleft()
            if node_dict is None:
                continue
            node = load_node_single(node_dict)
            node.parent = parent
            node.level = parent.level + 1
            if side == 'left':
                parent.left = node
            elif side == 'right':
                parent.right = node
            else:
                raise ValueError(side)
            work_queue.append((node_dict['left'], node, 'left'))
            work_queue.append((node_dict['right'], node, 'right'))
        return cls(root)


import urllib.request
import urllib.parse

url = 'https://hundsonvine.com/Goods/getGoodsList'

page = input("请输入查询的第几页：")
size = input("请输入查询多少条：")

form_data = {
	'page': page,
	'page_size': size
}

# 自己伪装头部
headers = {
	"User-Agnet":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
}

# 构建请求对象
request = urllib.request.Request(url=url,headers=headers)
# 处理表单数据
form_data = urllib.parse.urlencode(form_data).encode()

response = urllib.request.urlopen(request,data=form_data)

print(response.read().decode())


import serial

#global Prot2V3_PacketAvailable                  #Flags that a packet is available for use
#global Prot2V3_RemainingPacketBytesToRx         # Counter of how many bytes are expected within the payload section
#global Prot2V3_SumOfPacketBytesRxd_Actual       # Accumulated sum of the bytes Rx'd as part of the payload
#global Prot2V3_SumOfPacketBytesRxd_Expected     # Specified sum of the bytes to be Rx'd as part of the payload
#global Prot2V3_Packet                           # List of bytes that will contain the content of the payload
#global Prot2V3_RxEngineState                    # Decode engine states, reset to idle (0)
#Prot2V3_PacketAvailable = False             # Flags that a packet is available for use
#Prot2V3_RemainingPacketBytesToRx = 0        # Counter of how many bytes are expected within the payload section
#Prot2V3_SumOfPacketBytesRxd_Actual = 0      # Accumulated sum of the bytes Rx'd as part of the payload
#Prot2V3_SumOfPacketBytesRxd_Expected = 0    # Specified sum of the bytes to be Rx'd as part of the payload
#Prot2V3_Packet = bytes([])                  # List of bytes that will contain the content of the payload
#Prot2V3_RxEngineState = 0                   # Decode engine states are: 0:idle,

def OpenCommsPort(ComSettings):                 # Assign the port parameters and attempt to open it
    try:

        global CommsPort

        CommsPort = serial.Serial(port=ComSettings['Comport'],
                                  baudrate=ComSettings['ComSpeed'],
                                  timeout=1,    # do not block execution for more than 1 second
                                  # The following parameters are not alterable by the
                                  #   setup.ini file, by definition, the MCU is set to 8N1.
                                  bytesize=serial.EIGHTBITS,
                                  parity=serial.PARITY_NONE,
                                  stopbits=serial.STOPBITS_ONE,
                                  xonxoff=False)
#        CommsPort.open()                       # not needed in 3.8, port is opened by default above

        return True                             # Return true is all went well
    except:
        return False                            # Return false otherwise

def TestMessage(MessageToPrint):
    print(MessageToPrint)
    print('Bytes in serial buffer: '+str(CommsPort.inWaiting()))

def Encode2V3(PacketPayload):                   #Encode the PacketPaylod into Packet_EncodedAs2V3, based on the 2V3 packet structure

    try:
        Sum = 0                                 # Initialise the packet sum variable
        Length = len(PacketPayload)             # Set the packet length variable
        Position = 0                            # Zero based incoming string for Payload
        while Position < len(PacketPayload):    # Sum the ord value of all chars contained in the Payload
            Sum = Sum + ord(PacketPayload[Position])
            if (Sum>255):Sum = Sum - 256;       # Adjust the value of the variable as if it were a byte variable
            Position = Position + 1

        # Form the encoded packet by adding the head, length, sum and tail bytes
        Packet_EncodedAs2V3 = chr(2) + chr(Length) + chr(Sum) + PacketPayload + chr(3)

        # The encoded packet is now ready, send it through the serial port, one byte at a time
        for i in range(len(Packet_EncodedAs2V3)):
            CommsPort.write((ord(Packet_EncodedAs2V3[i])).to_bytes(1, byteorder='big'))

        return True
    except:
        return False

def Decode2V3_InitialiseRxEngine():             # Initialise the 2V3 protocol decoding engine

    global Prot2V3_PacketAvailable
    global Prot2V3_RemainingPacketBytesToRx
    global Prot2V3_SumOfPacketBytesRxd_Actual
    global Prot2V3_SumOfPacketBytesRxd_Expected
    global Prot2V3_Packet
    global Prot2V3_RxEngineState

    Prot2V3_PacketAvailable = False             # Flags that a packet is available for use
    Prot2V3_Packet = bytes([])                  # List of bytes that will contain the content of the payload
    Prot2V3_RxEngineState = 0                   # Decode engine states are: 0:idle,
                                                #   1:Rx'd header (0x02),
                                                #   2:Rx'd length,
                                                #   3:Rxing payload,
                                                #   4: Expecting tail (0x03),

def Decode2V3():                                # Process the incoming byte stream in order decode a 2V3 packet that might be sent by the MCU

    global Prot2V3_PacketAvailable
    global Prot2V3_RemainingPacketBytesToRx
    global Prot2V3_SumOfPacketBytesRxd_Actual
    global Prot2V3_SumOfPacketBytesRxd_Expected
    global Prot2V3_Packet
    global Prot2V3_RxEngineState

    while ((CommsPort.inWaiting() > 0) and not(Prot2V3_PacketAvailable)): # Only proceed if at least a byte is available in the comms buffer and a packet is not available from a previous case
        IncomingChar = CommsPort.read()         # Read one char off the serial buffer
#        print("Rx'd char: "+str(IncomingChar))
#        print("State: "+str(Prot2V3_RxEngineState))

        if ((Prot2V3_RxEngineState == 0) and (ord(IncomingChar) == 2)): # Idle state: exit to state 1 if an 0x02 byte (header) was Rx'd
            Prot2V3_RxEngineState = 1
#            print("Rx'd header")

        elif (Prot2V3_RxEngineState == 1): # State 1: Rx'd header, now waiting to Rx the length of the payload
            Prot2V3_RemainingPacketBytesToRx = ord(IncomingChar) # Rx'd the length of the expected payload
            Prot2V3_RxEngineState = 2
#            print("Rx'd length: "+str(Prot2V3_RemainingPacketBytesToRx))

        elif (Prot2V3_RxEngineState == 2): # State 2: Rx'd length, now waiting to Rx the sum of the payload
            Prot2V3_SumOfPacketBytesRxd_Expected = ord(IncomingChar) # Rx'd the sum that the payload bytes are expected to have
            Prot2V3_SumOfPacketBytesRxd_Actual = 0 # Zero the accumulated sum of the bytes Rx'd as part of the payload
            Prot2V3_RxEngineState = 3
#            print("Rx'd expected sum: "+str(Prot2V3_SumOfPacketBytesRxd_Expected))

        elif (Prot2V3_RxEngineState == 3): # State 3: accumulating the payload, remain within this state until all payload chars have been rx'd
            Prot2V3_SumOfPacketBytesRxd_Actual += ord(IncomingChar) # add the rx'd byte to the actual sum
            if (Prot2V3_SumOfPacketBytesRxd_Actual>255):
                Prot2V3_SumOfPacketBytesRxd_Actual = Prot2V3_SumOfPacketBytesRxd_Actual - 256; # Adjust the value of the variable as if it were a byte variable
            Prot2V3_Packet += bytearray(IncomingChar) # add the rx'd byte to the packet bytearray
            Prot2V3_RemainingPacketBytesToRx -= 1 # one less payload byte to expect
#            print("Rx'd payload: "+str(Prot2V3_Packet, 'utf-8'))
#            print("RemainingPacketBytesToRx: "+str(Prot2V3_RemainingPacketBytesToRx))
            if (Prot2V3_RemainingPacketBytesToRx == 0): # if all payload bytes have been rx'd...
                if (Prot2V3_SumOfPacketBytesRxd_Actual == Prot2V3_SumOfPacketBytesRxd_Expected): # then compare the calculated to the expected payload sum
#                    print("Sums match success")
                    Prot2V3_RxEngineState = 4 # they are equal, success, go to wait for the tail
                else:
#                    print("Sums match failure")
                    Decode2V3_InitialiseRxEngine() # they are not equal, failure, do not bother to wait for the tail byte

        elif (Prot2V3_RxEngineState == 4): # State 4: a valid payload has been rx'd.  Waiting for the tail byte to be rx'd, confirming proper packet rx
            if (ord(IncomingChar) == 3): # check whether the tail byte 0x03 has indeed been rx'd
#                print("Succeeded to rx the tail character")
#                print("A packet is available for use")
                Prot2V3_PacketAvailable = True # it has, success, a packet is available for processing
            else:
#                print("Failed to rx the tail character")
                Decode2V3_InitialiseRxEngine() # it has not, failure, reset the state machine, ready to rx another packet

        else:
            Decode2V3_InitialiseRxEngine() # no state was matched.  This is an abnormal behaviour, please reset the state machine, ready to rx another packet

2V3 Packet based protocol_Python example/Comms_Routines.py


#Gerekli kütüphanelerin kurulumu
import pandas as pd
import numpy as np

#Excel dosyasını tanıtıyorum.
df=pd.read_excel('last4.xlsx')

#İndex değerlerini temizliyorum.
df=df.iloc[:,1:]

data=df.copy()

#Fiyat sütunundaki her verinin sonundaki Tl,dolar ve euro simgesini kaldırıyorum.
data['Fiyat']=data['Fiyat'].apply(lambda x: x[:-2])

#'.' karakterini silip integer değere dönüştürüyorum.
data['Fiyat']=data['Fiyat'].apply(lambda x: int(x.replace('.','')))

#Fiyat sütunundaki her verinin sonundaki ' x' karakterini kaldırıyorum ve integer değere dönüştürüyorum.
data['Kilometre']=data['Kilometre'].apply(lambda x: x[:-2])
data['Kilometre']=data['Kilometre'].apply(lambda x: int(x.replace('.','')))

#Marka, model ve motor değerlerindeki gereksiz boşlukları temizliyorum.
data['Marka']=data['Marka'].apply(lambda x: x.strip())
data['Model']=data['Model'].apply(lambda x: x.strip())
data['Motor']=data['Motor'].apply(lambda x: str(x).strip())

motor2=[]
#'Bilmiyorum' değerine np.nan değerini veriyorum.Diğerlerini float veri tipine dönüştürerek kaydediyorum.
for i in data.Motor:
    try:
        i=float(i)
        if i<10:
            motor2.append(i)
        else:motor2.append(np.nan)
    except:
        motor2.append(np.nan)

#Motor sütununu motor2 listesi ile güncelliyorum.
data['Motor']=motor2

#MOTOR GUCU
#Motor Gücü içerisindeki değerleri integer veri tipine dönüştürüyorum ve motorGucu listesine ekliyorum.
motorGucu=[]
for i in data['Motor Gucu']:

    if i=='Bilmiyorum' or i=='-':
        i=np.NaN

    elif 'altı' in i:
        i=i[:-11]
        i=round(int(i)-(int(i)/5))

    elif 'üzeri' in i:
        i=i[:-11]
        i=round(int(i)+(int(i)/5))

    else:
        i=i[:-3]
        x1,x2=i.split('-')
        i=int((int(x1)+int(x2))/2)

    motorGucu.append(i)

#MOTOR HACMİ
#Motor Hacmi içerisindeki değerleri integer veri tipine dönüştürüyorum ve motorHacmi listesine ekliyorum.
motorHacmi=[]
for i in data['Motor Hacmi']:

    if i=='Bilmiyorum' or  i == '-':
        i=np.nan

    elif 'altı' in i:
        i=i[:-11]
        i=round(int(i)-(int(i)/5))
        i=i/1000

    else:

        i=i[:-3]
        x3,x4=i.split('-')
        i=int((int(x3)+int(x4))/2)
        i=i/1000

    motorHacmi.append(i)

#Motor Hacmi ve Motor Gücü sütununu güncelliyorum.
data['Motor Gucu']=motorGucu
data['Motor Hacmi']=motorHacmi

motor3=[]

#Motor sütunundaki np.nan değerleri motor hacmindeki değerleri 1000'e bölerek dolduruyorum.
for x,y in zip(data['Motor'],data['Motor Hacmi']):

    x=str(x)
    if 'nan' in x:
        motor3.append(y)
    else:motor3.append(x)

#Motor Sütununu güncelliyorum.
data['Motor']=motor3

ilanNo=data['İlan No']
#DataFrame'i düzeltek için ikiye ayırıyorum.
part_2=data.iloc[:,5:17]
part_1=data.iloc[:,:5]

data=pd.concat([ilanNo,part_1],axis=1)
data=pd.concat([data,part_2],axis=1)

#nan değerleri temizliyorum.
data=data.dropna(axis=0,how='any')

#Fiyat üzerinde herhangi bir etki sağlamayacak veri sütunlarını temizliyorum.
data.drop('İlan Tarihi',axis=1,inplace=True)
data.drop('Motor Hacmi',axis=1,inplace=True)
data.drop('Kimden',axis=1,inplace=True)

#Sütun isimlerini düzenliyorum.
data.columns = ['İlan No', 'Fiyat', 'Marka', 'Model', 'Motor', 'Paket', 'xxx',
       'Model Yili', 'Kilometre', 'Yakit Turu', 'Vites Tipi', 'Motor Gucu',
       'Renk', 'Kasa Tipi', 'Durumu']

data.drop('xxx',axis=1,inplace=True)

data['Motor']=data['Motor'].astype(float)

#get_dummies fonksiyonu ile bütün verileri integer'a dönüştürüyorum.
data=pd.get_dummies(data,columns=['Marka', 'Model', 'Motor', 'Paket',
       'Model Yili', 'Yakit Turu', 'Vites Tipi', 'Motor Gucu',
       'Renk', 'Kasa Tipi', 'Durumu'])

#Excel dosyası olarak kaydediyorum.
data.to_excel('output2.xlsx')