from collections import OrderedDict
from typing import Any, Dict, List, Optional, Type, Set, cast, Tuple

from phidata.k8s.args import K8sArgs
from phidata.k8s.api_client import K8sApiClient
from phidata.k8s.resource.base import K8sResource
from phidata.k8s.create.group import CreateK8sResourceGroup
from phidata.k8s.resource.group import (
    K8sResourceGroup,
    K8sBuildContext,
)
from phidata.k8s.resource.utils import (
    get_k8s_resources_from_group,
    filter_and_flatten_k8s_resource_groups,
)
from phidata.utils.cli_console import (
    print_info,
    print_heading,
    print_subheading,
    confirm_yes_no,
)
from phidata.utils.log import logger

class K8sWorker:
    """This class interacts with the Kubernetes API."""

    def __init__(self, k8s_args: K8sArgs) -> None:
        self.k8s_args: K8sArgs = k8s_args
        self.k8s_api_client: K8sApiClient = K8sApiClient(
            context=self.k8s_args.context,
            kubeconfig_path=self.k8s_args.kubeconfig_path,
        )
        logger.debug(f"**-+-** K8sWorker created")

    def is_client_initialized(self) -> bool:
        return self.k8s_api_client.is_initialized()

    def are_resources_active(self) -> bool:
        # TODO: fix this
        return False

    ######################################################
    ## Build Resources
    ######################################################

    def build_k8s_resource_groups(
        self,
        app_filter: Optional[str] = None,
    ) -> Optional[Dict[str, K8sResourceGroup]]:
        """
        Build the K8sResourceGroups for the requested apps

        Step 1: Convert each PhidataApp to K8sResourceGroups.
        Step 2: Convert each CreateK8sResourceGroup to K8sResourceGroups.
        Step 3: Add all K8sResourceGroups to the self.k8s_resources dict
        """
        logger.debug("-*- Initializing K8sResourceGroups")

        k8s_resource_groups: Optional[Dict[str, K8sResourceGroup]] = None

        # track the total number of K8sResourceGroups to build for validation
        apps_to_build = self.k8s_args.apps
        resources_to_build = self.k8s_args.resources
        create_resources_to_build = self.k8s_args.create_resources

        num_apps = len(apps_to_build) if apps_to_build is not None else 0
        num_resources = len(resources_to_build) if resources_to_build is not None else 0
        num_create_resources = (
            len(create_resources_to_build)
            if create_resources_to_build is not None
            else 0
        )
        num_rgs_to_build = num_apps + num_resources + num_create_resources
        num_rgs_built = 0

        # Step 1: Convert each PhidataApp to K8sResourceGroups.
        if apps_to_build is not None and isinstance(apps_to_build, list):
            for app in apps_to_build:
                if app.args is None:
                    logger.error("Args for App {} are None".format(app))
                    num_rgs_built += 1
                    continue

                if not app.enabled:
                    logger.debug(f"{app.name} disabled")
                    num_rgs_built += 1
                    continue

                # skip apps not matching app_filter if provided
                if app_filter is not None:
                    if app_filter.lower() not in app.name:
                        logger.debug(f"Skipping {app.name}")
                        num_rgs_built += 1
                        continue

                logger.debug("-*- App: {}".format(app.name))

                ######################################################################
                # NOTE: VERY IMPORTANT TO GET RIGHT
                # Pass down parameters from K8sArgs -> PhidataApp
                # The K8sConfig inherits these params from the WorkspaceConfig
                # 1. Pass down the paths from the WorkspaceConfig
                # 2. Pass down k8s_env
                # 3. Pass down common cloud configuration. eg: aws_region, aws_profile
                # This should match phidata.infra.prep_infra_config.prep_infra_config()
                ######################################################################

                # -*- Path parameters
                app.scripts_dir = self.k8s_args.scripts_dir
                app.storage_dir = self.k8s_args.storage_dir
                app.meta_dir = self.k8s_args.meta_dir
                app.products_dir = self.k8s_args.products_dir
                app.notebooks_dir = self.k8s_args.notebooks_dir
                app.workflows_dir = self.k8s_args.workflows_dir
                # The ws_root_path is the ROOT directory for the workspace
                app.workspace_root_path = self.k8s_args.workspace_root_path
                app.workspace_config_dir = self.k8s_args.workspace_config_dir
                app.workspace_config_file_path = (
                    self.k8s_args.workspace_config_file_path
                )

                # -*- Environment parameters
                # only update the params if they are not available on the app.
                # so we can prefer the app param if provided
                if app.k8s_env is None and self.k8s_args.k8s_env is not None:
                    app.k8s_env = self.k8s_args.k8s_env

                # -*- AWS parameters
                # only update the params if they are not available on the app.
                # so we can prefer the app param if provided
                if app.aws_region is None and self.k8s_args.aws_region is not None:
                    app.aws_region = self.k8s_args.aws_region
                if app.aws_profile is None and self.k8s_args.aws_profile is not None:
                    app.aws_profile = self.k8s_args.aws_profile
                if (
                    app.aws_config_file is None
                    and self.k8s_args.aws_config_file is not None
                ):
                    app.aws_config_file = self.k8s_args.aws_config_file
                if (
                    app.aws_shared_credentials_file is None
                    and self.k8s_args.aws_shared_credentials_file is not None
                ):
                    app.aws_shared_credentials_file = (
                        self.k8s_args.aws_shared_credentials_file
                    )

                app_rgs: Optional[
                    Dict[str, K8sResourceGroup]
                ] = app.get_k8s_resource_groups(
                    k8s_build_context=K8sBuildContext(
                        namespace=self.k8s_args.namespace,
                        context=self.k8s_args.context,
                        service_account_name=self.k8s_args.service_account_name,
                        labels=self.k8s_args.common_labels,
                    )
                )
                if app_rgs is not None:
                    if k8s_resource_groups is None:
                        k8s_resource_groups = OrderedDict()
                    k8s_resource_groups.update(app_rgs)
                    num_rgs_built += 1

        # Step 2: Convert each CreateK8sResourceGroup to K8sResourceGroups.
        if create_resources_to_build is not None and isinstance(
            create_resources_to_build, list
        ):
            for create_resource in create_resources_to_build:
                if not create_resource.enabled:
                    logger.debug(f"{create_resource.name} disabled")
                    num_rgs_built += 1
                    continue

                # skip groups not matching app_filter if provided
                if app_filter is not None:
                    if app_filter.lower() not in create_resource.name:
                        logger.debug(f"Skipping {create_resource.name}")
                        num_rgs_built += 1
                        continue

                logger.debug("-*- Resource: {}".format(create_resource.name))

                if isinstance(create_resource, CreateK8sResourceGroup):
                    # Create the K8sResourceGroup
                    k8s_resource_group: Optional[
                        K8sResourceGroup
                    ] = create_resource.create()
                    if k8s_resource_group is not None:
                        if k8s_resource_groups is None:
                            k8s_resource_groups = OrderedDict()
                        k8s_resource_groups[
                            k8s_resource_group.name
                        ] = k8s_resource_group
                    num_rgs_built += 1

        # Step 3: Add all K8sResourceGroups to the k8s_resource_groups dict
        if resources_to_build is not None and isinstance(resources_to_build, list):
            for resource in resources_to_build:
                if not resource.enabled:
                    logger.debug(f"{resource.name} disabled")
                    num_rgs_built += 1
                    continue

                # skip groups not matching app_filter if provided
                if app_filter is not None:
                    if app_filter.lower() not in resource.name:
                        logger.debug(f"Skipping {resource.name}")
                        num_rgs_built += 1
                        continue

                logger.debug("-*- Resource: {}".format(resource.name))

                if isinstance(resource, K8sResourceGroup):
                    if k8s_resource_groups is None:
                        k8s_resource_groups = OrderedDict()
                    k8s_resource_groups[resource.name] = resource
                    num_rgs_built += 1

        logger.debug(f"# K8sResourceGroups built: {num_rgs_built}/{num_rgs_to_build}")
        return k8s_resource_groups

    ######################################################
    ## Create Resources
    ######################################################

    def create_resources(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
        auto_confirm: Optional[bool] = False,
    ) -> bool:
        logger.debug("-*- Creating K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return True

        # Create a list of tuples with 3 parts
        #   1. Resource group name
        #   2. Resource group weight
        #   3. List of Resources in group after filters
        k8s_resources_to_create: List[Tuple[str, int, List[K8sResource]]] = []
        for k8s_rg_name, k8s_rg in k8s_resource_groups.items():
            logger.debug(f"Processing: {k8s_rg_name}")

            k8s_resources_in_group = get_k8s_resources_from_group(
                k8s_resource_group=k8s_rg,
                name_filter=name_filter,
                type_filter=type_filter,
            )
            num_resources_in_group = len(k8s_resources_in_group)
            logger.debug(f"Matched {num_resources_in_group} resources")
            if num_resources_in_group > 0:
                k8s_resources_to_create.append(
                    (k8s_rg_name, k8s_rg.weight, k8s_resources_in_group)
                )

        if len(k8s_resources_to_create) == 0:
            print_subheading("No K8sResources to create")
            return True

        k8s_resources_to_create_sorted: List[
            Tuple[str, int, List[K8sResource]]
        ] = sorted(
            k8s_resources_to_create,
            key=lambda x: x[1],
        )

        # Validate resources to be created
        group_number = 1
        resource_number = 0
        if not auto_confirm:
            print_heading("--**-- Confirm resources:")
            for (
                group_name,
                group_weight,
                resource_list,
            ) in k8s_resources_to_create_sorted:
                print_subheading(f"\n{group_number}. {group_name}")
                resource_number += len(resource_list)
                group_number += 1
                for resource in resource_list:
                    print_info(
                        f"  -+-> {resource.get_resource_type()}: {resource.get_resource_name()}"
                    )
            print_info(f"\nNamespace: {self.k8s_args.namespace}")
            print_info(f"Context: {self.k8s_args.context}")
            print_info(f"\nTotal {resource_number} resources")
            confirm = confirm_yes_no("\nConfirm deploy")
            if not confirm:
                print_info("-*-")
                print_info("-*- Skipping deploy")
                print_info("-*-")
                exit(0)

        # track the total number of K8sResources to create for validation
        num_resources_to_create: int = 0
        num_resources_created: int = 0

        for (group_name, group_weight, resource_list) in k8s_resources_to_create_sorted:
            print_subheading(f"\n-*- {group_name}")
            num_resources_to_create += len(resource_list)
            for resource in resource_list:
                if resource:
                    print_info(
                        f"\n-==+==- {resource.get_resource_type()}: {resource.get_resource_name()}"
                    )
                    logger.debug(f"Resource: {resource}")
                    try:
                        _resource_created = resource.create(
                            k8s_client=self.k8s_api_client
                        )
                        if _resource_created:
                            num_resources_created += 1
                            logger.info("Resource created")
                            logger.debug(
                                f"continue_on_create_failure: {self.k8s_args.continue_on_create_failure}"
                            )
                        else:
                            logger.error(
                                f"Resource {resource.get_resource_type()}: {resource.get_resource_name()} could not be created."
                            )
                            if self.k8s_args.continue_on_create_failure is False:
                                return False
                    except Exception as e:
                        logger.error(
                            f"-==+==--> Resource {resource.get_resource_type()}: {resource.get_resource_name()} could not be created."
                        )
                        logger.error("Error: {}".format(e))
                        logger.error(
                            "Skipping resource creation, please fix and try again..."
                        )

        print_info(
            f"\n# Resources created: {num_resources_created}/{num_resources_to_create}"
        )
        if num_resources_to_create == num_resources_created:
            return True

        logger.error(
            f"Resources created: {num_resources_created} do not match resources required: {num_resources_to_create}"
        )
        return False

    def create_resources_dry_run(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
        auto_confirm: Optional[bool] = False,
    ) -> None:
        logger.debug("-*- Creating K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return

        # Create a list of tuples with 3 parts
        #   1. Resource group name
        #   2. Resource group weight
        #   3. List of Resources in group after filters
        k8s_resources_to_create: List[Tuple[str, int, List[K8sResource]]] = []
        for k8s_rg_name, k8s_rg in k8s_resource_groups.items():
            logger.debug(f"Processing: {k8s_rg_name}")

            k8s_resources_in_group = get_k8s_resources_from_group(
                k8s_resource_group=k8s_rg,
                name_filter=name_filter,
                type_filter=type_filter,
            )
            num_resources_in_group = len(k8s_resources_in_group)
            logger.debug(f"Matched {num_resources_in_group} resources")
            if num_resources_in_group > 0:
                k8s_resources_to_create.append(
                    (k8s_rg_name, k8s_rg.weight, k8s_resources_in_group)
                )

        if len(k8s_resources_to_create) == 0:
            print_info("No K8sResources to create")
            return

        k8s_resources_to_create_sorted: List[
            Tuple[str, int, List[K8sResource]]
        ] = sorted(
            k8s_resources_to_create,
            key=lambda x: x[1],
        )

        group_number = 1
        num_resources_to_create: int = 0
        print_heading("--**-- Kubernetes resources:")
        for (group_name, group_weight, resource_list) in k8s_resources_to_create_sorted:
            print_subheading(f"\n{group_number}. {group_name}")
            num_resources_to_create += len(resource_list)
            group_number += 1
            for resource in resource_list:
                print_info(
                    f"  -+-> {resource.get_resource_type()}: {resource.get_resource_name()}"
                )
        print_info(f"\nNamespace: {self.k8s_args.namespace}")
        print_info(f"Context: {self.k8s_args.context}")
        print_info(f"\nTotal {num_resources_to_create} resources")

    ######################################################
    ## Get Resources
    ######################################################

    def get_resources(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
    ) -> Optional[List[K8sResource]]:

        logger.debug("-*- Getting K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return None

        k8s_resources: Optional[
            List[K8sResource]
        ] = filter_and_flatten_k8s_resource_groups(
            k8s_resource_groups=k8s_resource_groups,
            name_filter=name_filter,
            type_filter=type_filter,
            app_filter=app_filter,
        )

        return k8s_resources

    ######################################################
    ## Delete Resources
    ######################################################

    def delete_resources(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
        auto_confirm: Optional[bool] = False,
    ) -> bool:
        logger.debug("-*- Deleting K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return True

        # Create a list of tuples with 3 parts
        #   1. Resource group name
        #   2. Resource group weight
        #   3. List of Resources in group after filters
        k8s_resources_to_delete: List[Tuple[str, int, List[K8sResource]]] = []
        for k8s_rg_name, k8s_rg in k8s_resource_groups.items():
            logger.debug(f"Processing: {k8s_rg_name}")

            k8s_resources_in_group = get_k8s_resources_from_group(
                k8s_resource_group=k8s_rg,
                name_filter=name_filter,
                type_filter=type_filter,
                sort_order="delete",
            )
            num_resources_in_group = len(k8s_resources_in_group)
            logger.debug(f"Matched {num_resources_in_group} resources")
            if num_resources_in_group > 0:
                k8s_resources_to_delete.append(
                    (k8s_rg_name, k8s_rg.weight, k8s_resources_in_group)
                )

        if len(k8s_resources_to_delete) == 0:
            print_subheading("No K8sResources to delete")
            return True

        k8s_resources_to_delete_sorted: List[
            Tuple[str, int, List[K8sResource]]
        ] = sorted(
            k8s_resources_to_delete,
            key=lambda x: x[1],
            reverse=True,
        )

        # Validate resources to be created
        group_number = 1
        resource_number = 0
        if not auto_confirm:
            print_heading("--**-- Confirm resources:")
            for (
                group_name,
                group_weight,
                resource_list,
            ) in k8s_resources_to_delete_sorted:
                print_subheading(f"\n{group_number}. {group_name}")
                resource_number += len(resource_list)
                group_number += 1
                for resource in resource_list:
                    print_info(
                        f"  -+-> {resource.get_resource_type()}: {resource.get_resource_name()}"
                    )
            print_info(f"\nNamespace: {self.k8s_args.namespace}")
            print_info(f"Context: {self.k8s_args.context}")
            print_info(f"\nTotal {resource_number} resources")
            confirm = confirm_yes_no("\nConfirm delete")
            if not confirm:
                print_info("-*-")
                print_info("-*- Skipping delete")
                print_info("-*-")
                exit(0)

        # track the total number of K8sResources to create for validation
        num_resources_to_delete: int = 0
        num_resources_deleted: int = 0

        for (group_name, group_weight, resource_list) in k8s_resources_to_delete_sorted:
            print_subheading(f"\n-*- {group_name}")
            num_resources_to_delete += len(resource_list)
            for resource in resource_list:
                if resource:
                    print_info(
                        f"\n-==+==- {resource.get_resource_type()}: {resource.get_resource_name()}"
                    )
                    # logger.debug(resource)
                    try:
                        _resource_deleted = resource.delete(
                            k8s_client=self.k8s_api_client
                        )
                        if _resource_deleted:
                            num_resources_deleted += 1
                            print_info("Resource deleted")
                        else:
                            logger.error(
                                f"Resource {resource.get_resource_type()}: {resource.get_resource_name()} could not be deleted."
                            )
                            if not self.k8s_args.continue_on_delete_failure:
                                return False
                    except Exception as e:
                        logger.error(
                            f"-==+==--> Resource {resource.get_resource_type()}: {resource.get_resource_name()} could not be created."
                        )
                        logger.error("Error: {}".format(e))
                        logger.error(
                            "Skipping resource creation, please fix and try again..."
                        )

        print_info(
            f"\n# Resources deleted: {num_resources_deleted}/{num_resources_to_delete}"
        )
        if num_resources_to_delete == num_resources_deleted:
            return True

        logger.error(
            f"Resources deleted: {num_resources_deleted} do not match resources required: {num_resources_to_delete}"
        )
        return False

    def delete_resources_dry_run(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
        auto_confirm: Optional[bool] = False,
    ) -> None:
        logger.debug("-*- Deleting K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return None

        # Create a list of tuples with 3 parts
        #   1. Resource group name
        #   2. Resource group weight
        #   3. List of Resources in group after filters
        k8s_resources_to_delete: List[Tuple[str, int, List[K8sResource]]] = []
        for k8s_rg_name, k8s_rg in k8s_resource_groups.items():
            logger.debug(f"Processing: {k8s_rg_name}")

            k8s_resources_in_group = get_k8s_resources_from_group(
                k8s_resource_group=k8s_rg,
                name_filter=name_filter,
                type_filter=type_filter,
                sort_order="delete",
            )
            num_resources_in_group = len(k8s_resources_in_group)
            logger.debug(f"Matched {num_resources_in_group} resources")
            if num_resources_in_group > 0:
                k8s_resources_to_delete.append(
                    (k8s_rg_name, k8s_rg.weight, k8s_resources_in_group)
                )

        if len(k8s_resources_to_delete) == 0:
            print_info("No K8sResources to create")
            return

        k8s_resources_to_delete_sorted: List[
            Tuple[str, int, List[K8sResource]]
        ] = sorted(
            k8s_resources_to_delete,
            key=lambda x: x[1],
            reverse=True,
        )

        group_number = 1
        num_resources_to_delete: int = 0
        print_heading("--**-- Kubernetes resources:")
        for (group_name, group_weight, resource_list) in k8s_resources_to_delete_sorted:
            print_subheading(f"\n{group_number}. {group_name}")
            num_resources_to_delete += len(resource_list)
            group_number += 1
            for resource in resource_list:
                print_info(
                    f"  -+-> {resource.get_resource_type()}: {resource.get_resource_name()}"
                )
        print_info(f"\nNamespace: {self.k8s_args.namespace}")
        print_info(f"Context: {self.k8s_args.context}")
        print_info(f"\nTotal {num_resources_to_delete} resources")

    ######################################################
    ## Patch Resources
    ######################################################

    def patch_resources(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
        auto_confirm: Optional[bool] = False,
    ) -> bool:
        logger.debug("-*- Patching K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return True

        # Create a list of tuples with 3 parts
        #   1. Resource group name
        #   2. Resource group weight
        #   3. List of Resources in group after filters
        k8s_resources_to_patch: List[Tuple[str, int, List[K8sResource]]] = []
        for k8s_rg_name, k8s_rg in k8s_resource_groups.items():
            logger.debug(f"Processing: {k8s_rg_name}")

            k8s_resources_in_group = get_k8s_resources_from_group(
                k8s_resource_group=k8s_rg,
                name_filter=name_filter,
                type_filter=type_filter,
            )
            num_resources_in_group = len(k8s_resources_in_group)
            logger.debug(f"Matched {num_resources_in_group} resources")
            if num_resources_in_group > 0:
                k8s_resources_to_patch.append(
                    (k8s_rg_name, k8s_rg.weight, k8s_resources_in_group)
                )

        if len(k8s_resources_to_patch) == 0:
            print_subheading("No K8sResources to patch")
            return True

        k8s_resources_to_patch_sorted: List[
            Tuple[str, int, List[K8sResource]]
        ] = sorted(
            k8s_resources_to_patch,
            key=lambda x: x[1],
        )

        # Validate resources to be patched
        group_number = 1
        resource_number = 0
        if not auto_confirm:
            print_heading("--**-- Confirm resources:")
            for (
                group_name,
                group_weight,
                resource_list,
            ) in k8s_resources_to_patch_sorted:
                print_subheading(f"\n{group_number}. {group_name}")
                resource_number += len(resource_list)
                group_number += 1
                for resource in resource_list:
                    print_info(
                        f"  -+-> {resource.get_resource_type()}: {resource.get_resource_name()}"
                    )
            print_info(f"\nNamespace: {self.k8s_args.namespace}")
            print_info(f"Context: {self.k8s_args.context}")
            print_info(f"\nTotal {resource_number} resources")
            confirm = confirm_yes_no("\nConfirm patch")
            if not confirm:
                print_info("-*-")
                print_info("-*- Skipping patch")
                print_info("-*-")
                exit(0)

        # track the total number of K8sResources to patch for validation
        num_resources_to_patch: int = 0
        num_resources_patched: int = 0

        for (group_name, group_weight, resource_list) in k8s_resources_to_patch_sorted:
            print_subheading(f"\n-*- {group_name}")
            num_resources_to_patch += len(resource_list)
            for resource in resource_list:
                if resource:
                    print_info(
                        f"\n-==+==- {resource.get_resource_type()}: {resource.get_resource_name()}"
                    )
                    # logger.debug(resource)
                    try:
                        _resource_patched = resource.update(
                            k8s_client=self.k8s_api_client
                        )
                        if _resource_patched:
                            num_resources_patched += 1
                            print_info("Resource patched")
                        else:
                            logger.error(
                                f"Resource {resource.get_resource_type()}: {resource.get_resource_name()} could not be patched."
                            )
                            if not self.k8s_args.continue_on_patch_failure:
                                return False
                    except Exception as e:
                        logger.error(
                            f"-==+==--> Resource {resource.get_resource_type()}: {resource.get_resource_name()} could not be patched."
                        )
                        logger.error("Error: {}".format(e))
                        logger.error(
                            "Skipping resource creation, please fix and try again..."
                        )

        print_info(
            f"\n# Resources patched: {num_resources_patched}/{num_resources_to_patch}"
        )
        if num_resources_to_patch == num_resources_patched:
            return True

        logger.error(
            f"Resources patched: {num_resources_patched} do not match resources required: {num_resources_to_patch}"
        )
        return False

    def patch_resources_dry_run(
        self,
        name_filter: Optional[str] = None,
        type_filter: Optional[str] = None,
        app_filter: Optional[str] = None,
        auto_confirm: Optional[bool] = False,
    ) -> None:
        logger.debug("-*- Patching K8sResources")

        k8s_resource_groups: Optional[
            Dict[str, K8sResourceGroup]
        ] = self.build_k8s_resource_groups(app_filter=app_filter)

        if k8s_resource_groups is None:
            print_info("No resources available")
            return None

        # Create a list of tuples with 3 parts
        #   1. Resource group name
        #   2. Resource group weight
        #   3. List of Resources in group after filters
        k8s_resources_to_patch: List[Tuple[str, int, List[K8sResource]]] = []
        for k8s_rg_name, k8s_rg in k8s_resource_groups.items():
            logger.debug(f"Processing: {k8s_rg_name}")

            k8s_resources_in_group = get_k8s_resources_from_group(
                k8s_resource_group=k8s_rg,
                name_filter=name_filter,
                type_filter=type_filter,
            )
            num_resources_in_group = len(k8s_resources_in_group)
            logger.debug(f"Matched {num_resources_in_group} resources")
            if num_resources_in_group > 0:
                k8s_resources_to_patch.append(
                    (k8s_rg_name, k8s_rg.weight, k8s_resources_in_group)
                )

        if len(k8s_resources_to_patch) == 0:
            print_info("No K8sResources to patch")
            return

        k8s_resources_to_patch_sorted: List[
            Tuple[str, int, List[K8sResource]]
        ] = sorted(
            k8s_resources_to_patch,
            key=lambda x: x[1],
        )

        group_number = 1
        num_resources_to_patch: int = 0
        print_heading("--**-- Kubernetes resources:")
        for (group_name, group_weight, resource_list) in k8s_resources_to_patch_sorted:
            print_subheading(f"\n{group_number}. {group_name}")
            num_resources_to_patch += len(resource_list)
            group_number += 1
            for resource in resource_list:
                print_info(
                    f"  -+-> {resource.get_resource_type()}: {resource.get_resource_name()}"
                )
        print_info(f"\nNamespace: {self.k8s_args.namespace}")
        print_info(f"Context: {self.k8s_args.context}")
        print_info(f"\nTotal {num_resources_to_patch} resources")

    ######################################################
    ## End
    ######################################################

    ######################################################
    ## List Resources
    ######################################################

    # def get_active_resource_classes(
    #     self,
    #     type_filters: Optional[List[str]] = None,
    #     name_filters: Optional[List[str]] = None,
    # ) -> Optional[Set[Type[K8sResource]]]:
    #     """This function scans all K8sResourceGroups under this client and
    #     returns a set of different types of K8sResource(s) as a set of classes.
    #
    #     When do we need this:
    #     Let's say your app has 1 Deployment, 1 Pod and 1 Service. You deploy it and now want
    #     to see the different types of objects running on your cluster on your cluster.
    #     To do that you need to query the API for each and every resource type to see if there are any running objects of that type.
    #     We use this function to filter & dedup the resource types this K8s client is managing.
    #     This way we don't end up scanning everything.
    #
    #     Also super useful when you want to just see Deployments running, faster responses.
    #     """
    #     if self.k8s_resources is None:
    #         logger.debug("No K8sResourceGroups available")
    #         return None
    #
    #     _filtered_k8s_resources: Optional[
    #         List[K8sResource]
    #     ] = filter_and_flatten_k8s_resource_groups(
    #         k8s_resource_groups=self.k8s_resources,
    #         type_filters=type_filters,
    #         name_filters=name_filters,
    #         sort_order="create",
    #     )
    #     return dedup_resource_types(_filtered_k8s_resources)
    #
    # def get_active_resources(
    #     self,
    #     type_filters: Optional[List[str]] = None,
    #     name_filters: Optional[List[str]] = None,
    # ) -> Optional[Dict[str, List]]:
    #     """Reads the K8s Cluster and returns all active k8s_resources which satisfy the filters."""
    #
    #     from kubernetes.client.rest import ApiException
    #
    #     active_resource_classes: Optional[
    #         Set[Type[K8sResource]]
    #     ] = self.get_active_resource_classes(type_filters, name_filters)
    #
    #     if active_resource_classes is None:
    #         return None
    #
    #     _active_ns = self.get_k8s_namespace_to_use()
    #     active_k8s_objects: Dict[str, List] = defaultdict(list)
    #     for resource_class in active_resource_classes:
    #         resource_type: str = resource_class.__name__
    #         logger.debug(f"Resource Type: {resource_type}")
    #         try:
    #             _active_objects: Optional[List[Any]] = resource_class.get_from_cluster(
    #                 k8s_client=cast(K8sApiClient, self.k8s_api_client), namespace=_active_ns
    #             )
    #             if _active_objects is not None and isinstance(_active_objects, list):
    #                 active_k8s_objects[resource_type] = _active_objects
    #         except ApiException as e:
    #             logger.debug(
    #                 f"ApiException while getting {resource_type}, reason: {e.reason}"
    #             )
    #
    #     return active_k8s_objects
    #
    # def print(
    #     self,
    #     type_filters: Optional[List[str]] = None,
    #     name_filters: Optional[List[str]] = None,
    #     as_yaml: Optional[bool] = True,
    # ) -> None:
    #
    #     active_k8s_objects: Optional[Dict[str, List]] = self.get_active_resources(
    #         type_filters, name_filters
    #     )
    #     if active_k8s_objects:
    #         for _r_type, active_resources in active_k8s_objects.items():
    #             print("\n{}:".format(_r_type))
    #             for _r in active_resources:
    #                 print("\t- {}".format(_r))

    ######################################################
    ## Debug
    ######################################################

    # def debug(
    #     self,
    #     type_filters: Optional[List[str]] = None,
    #     name_filters: Optional[List[str]] = None,
    # ) -> None:
    #
    #     if self._k8s_client is None:
    #         logger.debug("No K8sApiClient available")
    #         return
    #     if self.k8s_resources is None:
    #         logger.debug("No K8sResourceGroups available")
    #         return
    #     _filtered_k8s_resources: Optional[
    #         List[K8sResource]
    #     ] = filter_and_flatten_k8s_resource_groups(
    #         k8s_resource_groups=self.k8s_resources,
    #         type_filters=type_filters,
    #         name_filters=name_filters,
    #         sort_order="create",
    #     )
    #     if _filtered_k8s_resources:
    #         for resource in _filtered_k8s_resources:
    #             logger.debug(f"Resource: {resource.metadata.name}")
    #             if resource and self.k8s_api_client:
    #                 resource.debug()

    ######################################################
    ## K8sApiClient
    ######################################################

    # def get_k8s_namespace_to_use(self) -> str:
    #     if self.k8s_resources is None:
    #         return DEFAULT_K8S_NAMESPACE
    #     ns_rg: K8sResourceGroup = self.k8s_resources[
    #         NAMESPACE_RESOURCE_GROUP_KEY
    #     ]
    #     if ns_rg and ns_rg.enabled and ns_rg.ns is not None:
    #         return ns_rg.ns.get_resource_name()
    #     return DEFAULT_K8S_NAMESPACE
    #
    # def get_k8s_service_account_to_use(self) -> str:
    #     if self.k8s_resources is None:
    #         return DEFAULT_K8S_SERVICE_ACCOUNT
    #     rbac_rg: K8sResourceGroup = self.k8s_resources[RBAC_RESOURCE_GROUP_KEY]
    #     if rbac_rg and rbac_rg.enabled and rbac_rg.sa is not None:
    #         return rbac_rg.sa.get_resource_name()
    #     return DEFAULT_K8S_SERVICE_ACCOUNT

    ######################################################
    ## Describe Resources
    ######################################################

    # def print_k8s_resources(
    #     self,
    #     type_filters: Optional[List[str]] = None,
    #     name_filters: Optional[List[str]] = None,
    #     op_format: str = "yaml",  # yaml or json
    # ) -> None:
    #
    #     if op_format not in ("yaml", "json"):
    #         print(f"Output format {op_format} not supported")
    #         return
    #
    #     if self.k8s_resources is None:
    #         logger.debug("No K8sResourceGroups available")
    #         return
    #
    #     self.print_k8s_resource_groups(
    #         k8s_resource_groups=self.k8s_resources,
    #         type_filters=type_filters,
    #         name_filters=name_filters,
    #         as_yaml=(op_format == "yaml"),
    #     )
    #
    # @staticmethod
    # def print_k8s_resource_groups(
    #     k8s_resource_groups: Dict[str, K8sResourceGroup],
    #     type_filters: Optional[List[str]] = None,
    #     name_filters: Optional[List[str]] = None,
    #     as_yaml: Optional[bool] = True,
    # ) -> None:
    #
    #     print(
    #         "Resources: {}/{}".format(
    #             type_filters if type_filters else "*",
    #             name_filters if name_filters else "*",
    #         )
    #     )
    #     _filtered_k8s_resources: Optional[
    #         List[K8sResource]
    #     ] = filter_and_flatten_k8s_resource_groups(
    #         k8s_resource_groups=k8s_resource_groups,
    #         type_filters=type_filters,
    #         name_filters=name_filters,
    #         sort_order="create",
    #     )
    #     _resources: List[str] = []
    #     if _filtered_k8s_resources:
    #         for resource in _filtered_k8s_resources:
    #             if resource:
    #                 if as_yaml:
    #                     _yml = resource.get_k8s_manifest_yaml()
    #                     if _yml:
    #                         _resources.append(_yml)
    #                 else:
    #                     _json = resource.get_k8s_manifest_json(indent=2)
    #                     if _json:
    #                         _resources.append(_json)
    #
    #     if _resources:
    #         if as_yaml:
    #             print("---")
    #             print("---\n".join(_resources))
    #         else:
    #             print("\n".join(_resources))

    ######################################################
    ## Helpers
    ######################################################

    # def _debug_log(self, msg: Optional[str] = None) -> None:
    #     if msg:
    #         logger.debug(msg)
    #     kubeconfig_avl = True if self._kubeconfig else False
    #     k8s_client_avl = True if self.k8s_api_client else False
    #     logger.debug(f"Kubeconfig Available  : {kubeconfig_avl}")
    #     logger.debug(f"K8s_api Available  : {k8s_client_avl}")
    #
    # def verify_client(self) -> None:
    #     """Helper method to verify that we are good to perform K8s operaitons.
    #
    #     Raises:
    #         K8sClientException if something is wrong
    #     """
    #     if self._kubeconfig and self._k8s_client:
    #         pass
    #     else:
    #         self._debug_log()
    #         raise exceptions.K8sWorkerException("K8sWorker unavailable")


import argparse
from os.path import join
import os
import time
import numpy as np
from tqdm import tqdm
from sklearn.manifold import TSNE

from config import Config
from utils import create_dirs

import matplotlib.pyplot as plt

config = Config()
def color_plot(x, label, save_path):
    # cdict = {1: 'red', 2: 'blue', 3: 'green', 4:'black', 5:'orange', 6:'yellow', 7:'purple'}

    fig, ax = plt.subplots()
    for g in np.unique(label):
        ix = np.where(label == g)
        ax.scatter(x[ix,0], x[ix,1], label=g, s=20)
    ax.legend()
    plt.show()
    plt.savefig(join(save_path, 'tSNE_plot.png'), dpi=1024)
    print('saving 2D vis to %s' % join(save_path, 'tSNE_plot.png'))

if __name__ == '__main__':
    argparser = argparse.ArgumentParser()
    argparser.add_argument(
        '--exp_id',
        required=True,
        help='experiment id')
    argparser.add_argument(
        '--datasets',
        required=True,
        help='dataset_name')

    args = argparser.parse_args()
    log_dir = 'logs/%s/%s' % (args.datasets, args.exp_id)
    res_dir = 'results/%s/%s' % (args.datasets, args.exp_id)
    create_dirs([res_dir])

    # X = np.loadtxt(join(data_root, dataset_name+'.txt'))
    y = np.loadtxt(join(config.data_root, args.datasets+'_label.txt'), dtype='int')

    if config.formt=='npy':
        x = np.load(join(res_dir, 'encoding.npy'))
    else:
        x = np.loadtxt(join(res_dir, 'encoding.txt'), dtype='float')
    x = x.squeeze()

    x_emb = TSNE(n_components=2).fit_transform(x)

    color_plot(x_emb, y, res_dir)


# %%
def psum(numbers):
    total = 0
    for i in range(len(numbers)):
        total += numbers[i]
    return total

def rsum(numbers):
    if not numbers:
        return 0
    return numbers[0] + rsum(numbers[1:])

print(psum([1, 2, 3, 4, 5]))
print(rsum([1, 2, 3, 4, 5]))

# %%
def dec2bin(d):
    if d < 0:
        raise ValueError("Must be a positive integer.")
    digit = str(d % 2)
    return dec2bin(d // 2) + digit if d > 1 else digit

print(dec2bin(42))
print(dec2bin(0))

def bin2dec(b):
    if b == "":
        return 0
    return int(b[-1]) + 2 * bin2dec(b[:-1])

print(bin2dec("101010"))  # 42
print(bin2dec("0"))  # 0
print(bin2dec("100"))  # 4

print(bin2dec(dec2bin(241212)))
print(dec2bin(241212))
print(dec2bin(bin2dec("111010111000111100")))

# %%
double = lambda x: 2 * x
print(double(12))

iseven = lambda x: x % 2 == 0
print(iseven(12))
print(iseven(13))

isodd = lambda x: not (iseven(x))
print(isodd(13))
print(isodd(12))

# %%
list(map(dec2bin, list(range(16))))

# %%
list(filter(isodd, list(range(16))))

# %%
list(map(dec2bin, (list(filter(isodd, list(range(16)))))))

# %%
from functools import reduce

reduce(lambda x, y: x + y, list(filter(isodd, list(range(100)))))

# %%
reduce(lambda x, y: x * y, list(range(1, 11)))
# %%


import requests
from bs4 import BeautifulSoup
def get_urls(url, vendor):
    r = requests.get(url)
    soup = BeautifulSoup(r.text, 'html.parser')
    product_title = soup.find_all('a', class_='product-title')
    product_price = soup.find_all('meta', itemprop='price')
    hrefs = []
    for a in range(len(product_title)):
        hrefs.append([product_title[a].contents, f'https://{vendor}/{product_title[a]["href"]}', product_price[a]["content"]])
    return hrefs


import itertools
soos = "SHHBNFBCKNHCNOSHHVFF"
kek = """
CK -> N
VP -> B
CF -> S
FO -> V
VC -> S
BV -> V
NP -> P
SN -> C
KN -> V
NF -> P
SB -> C
PC -> B
OB -> V
NS -> O
FH -> S
NK -> S
HO -> V
NV -> O
FV -> O
FB -> S
PS -> S
FN -> K
HS -> O
CB -> K
HV -> P
NH -> C
BO -> B
FF -> N
PO -> F
BB -> N
PN -> C
BP -> C
HN -> K
CO -> P
BF -> H
BC -> S
CV -> B
VV -> F
FS -> B
BN -> P
VK -> S
PV -> V
PP -> B
PH -> N
SS -> O
SK -> S
NC -> P
ON -> F
NB -> N
CC -> N
SF -> H
PF -> H
OV -> O
KH -> C
CP -> V
PK -> O
KC -> K
KK -> C
KF -> B
HP -> C
FK -> H
BH -> K
VN -> H
OO -> S
SC -> K
SP -> B
KO -> V
KV -> F
HK -> N
FP -> N
NN -> B
VS -> O
HC -> K
BK -> N
KS -> K
VB -> O
OH -> F
KB -> F
KP -> H
HB -> N
NO -> N
OF -> O
BS -> H
VO -> H
SH -> O
SV -> K
HF -> C
CS -> F
FC -> N
VH -> H
OP -> K
OK -> H
PB -> K
HH -> S
OC -> V
VF -> B
CH -> K
CN -> C
SO -> P
OS -> O
""".split("\n")
kek.remove("")
kek.remove("")

def a1(steps):
    global soos
    global r
    r = {}
    for k in kek:
        ss = k.split(" -> ")
        r[ss[0]] = str(ss[0][0]) + str(ss[1]) + str(ss[0][1])

    for i in range(0,steps):
        ss = [(r.get(soos[i:i+2])) for i in range(0, len(soos) - 1)]

        for s in range(0, len(ss) - 1):
            ss[s] = ss[s][0:2]

        soos = "".join(ss)

    qty = [soos.count(s) for s in soos]
    print(max(qty) - min(qty), end=",\n")

def a2(steps):
    r = {}
    for k in kek:
        ss = k.split(" -> ")
        r[ss[0]] = str(ss[0][0]) + str(ss[1]) + str(ss[0][1])

    count = {}
    [(count.__setitem__("%s%s" % (l[0], l[1]), 0), count.__setitem__("%s%s" % (l[1], l[0]), 0)) for l in list(itertools.combinations([x for x in list(set("".join(r.keys()))) for _ in range(2)], 2))]
    leer = dict(count)

    [count.__setitem__(soos[i:i+2], count[soos[i:i+2]] + 1) for i in range(len(soos) - 1)]

    for i in range(steps):
        out = dict(leer)
        for k in dict(count):
            [out.__setitem__(r.get(k)[z:z+2], out[r.get(k)[z:z+2]] + count[k]) for z in range(2)]
        count = dict(out)

    qty = {}
    [qty.__setitem__(k, 0) if k not in (soos[0], soos[-1]) else qty.__setitem__(k, 1) for k in list(set("".join(leer)))]

    for q in dict(qty):
        for k in count:
            if q in k:
                qty[q] += count[k] * k.count(q)

    print((max([qty[q] for q in qty]) - min([qty[q] for q in qty])) // 2)

a2(40)


# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html

from scrapy.exceptions import DropItem

class DulpicatesPipeline(object):

	def __init__(self):
		self.times_seen = set()

	def process_item(self, item, spider):
		if (item['Date'], item['Time']) in self.times_seen:
			raise DropItem("Duplicate time found: %s" % item)
		else:
			self.times_seen.add((item['Date'],item['Time']))
			return item

class TimePipeline(object):

	def process_item(self, item, spider):
		if '53' not in item['Time']:
			raise DropItem("Time does not end in 53")
		else:
			return item


import commands2
from wpilib import SmartDashboard
from subsystems.elevator import Elevator
from wpilib import DoubleSolenoid

class ElevatorMove(commands2.CommandBase):

    def __init__(self, container, elevator:Elevator, setpoint=None, direction=None, wait_to_finish=True,
                 drive_controls=False, enable_skip=False, decide_by_turret=False) -> None:
        super().__init__()
        self.setName('Elevator Move')
        self.container = container
        self.elevator = elevator
        self.setpoint = setpoint
        self.direction = direction
        self.tolerance = 10
        self.wait_to_finish = wait_to_finish  # determine how long we wait to end
        self.drive_controls = drive_controls
        self.enable_skip = enable_skip
        self.decide_by_turret = decide_by_turret

        self.addRequirements(self.elevator)  # commandsv2 version of requirements

    def initialize(self) -> None:
        self.print_start_message()
        position = self.elevator.get_height()

        positions = list(self.elevator.positions.values())  # default list of setpoints

        # only allow certain setpoints depending on if we have a game piece
        if self.drive_controls:
            if self.container.pneumatics.get_manipulator_state() == DoubleSolenoid.Value.kForward:
                positions = list(self.elevator.positions_open.values())
            else:
                positions = list(self.elevator.positions_close.values())

        # allow us to autonomously decide on how high to go based on turret position
        if self.decide_by_turret:
            turret_angle = self.container.turret.get_angle()
            if turret_angle > -20 and turret_angle < 120: # we are scoring mid or low
                self.setpoint = self.elevator.positions_close['low']  #  {'top': 950, 'low': 650}
            else:  # we are scoring high
                self.setpoint = self.elevator.positions_close['top']

        # tell the elevator to go to position
        if self.setpoint is None:
            if self.direction == 'up':
                allowed_positions = [x for x in sorted(positions) if x > position + 10 ]
                print(allowed_positions)
                temp_setpoint = sorted(allowed_positions)[0] if len(allowed_positions) > 0 else position

                if self.enable_skip and self.elevator.is_moving and len(allowed_positions) > 1:
                    temp_setpoint = sorted(allowed_positions)[1]
            else:
                allowed_positions = [x for x in sorted(positions) if x < position -10]
                print(allowed_positions)
                temp_setpoint = sorted(allowed_positions)[-1] if len(allowed_positions) > 0 else position

                if self.enable_skip and self.elevator.is_moving and len(allowed_positions) > 1:
                    temp_setpoint = sorted(allowed_positions)[-2]

            self.elevator.set_elevator_height(height=temp_setpoint, mode='smartmotion')
            print(f'Setting elevator from {position:.0f} to {temp_setpoint}')
        else:
            self.elevator.set_elevator_height(height=self.setpoint, mode='smartmotion')
            print(f'Setting elevator from {position:.0f} to {self.setpoint}')

    def execute(self) -> None:  # nothing to do, the sparkmax is doing all the work
        pass

    def isFinished(self) -> bool:
        if self.wait_to_finish:  # wait for the elevator to get within x mm
            return abs(self.elevator.get_height() - self.setpoint) < self.tolerance
        else:
            return True

    def end(self, interrupted: bool) -> None:
        end_time = self.container.get_enabled_time()
        message = 'Interrupted' if interrupted else 'Ended'
        print(f"** {message} {self.getName()} at {end_time:.1f} s at {self.elevator.sparkmax_encoder.getPosition():.1f} after {end_time - self.start_time:.1f} s **")
        # SmartDashboard.putString(f"alert", f"** {message} {self.getName()} at {end_time:.1f} s after {end_time - self.start_time:.1f} s **")

    def print_start_message(self):
        self.start_time = round(self.container.get_enabled_time(), 2)
        print("\n" + f"** Started {self.getName()} at {self.start_time} s **", flush=True)
        SmartDashboard.putString("alert", f"** Started {self.getName()} at {self.start_time - self.container.get_enabled_time():2.2f} s **")


if __name__ == "__main__":
    _, _, y = open("input.txt").read().strip().split("=")
    ymin, ymax = (int(i) for i in y.split(".."))

    # starts with 1 more speed on the way down than up.
    ymin += 1 if ymin < 0 else 0
    ymax += 1 if ymax < 0 else 0

    # max y_velocity is inf if 0 is in the y range.
    # it will always pass 0, no mather how hard you shoot it.
    if 0 in range(ymin, ymax+1):
        raise Exception("Infinite y_height")

    # max y_velocity when it reaches the end of the area 1
    # timestep after starting or passing 0.
    y_max_velocity = max(abs(ymin), abs(ymax))
    y_height = (y_max_velocity+1)/2*y_max_velocity
    print(int(y_height))


from appliances import CoffeeMaker, DishWasher, Refrigerator, Dryer, Washer, Stove, CanOpener

whirlpool_dishwasher = DishWasher("black")
whirlpool_dishwasher.wash_dishes()

samsung_washer = Washer("red", "normal")
samsung_dryer = Dryer("red", "gas")

lg_fridge = Refrigerator("stainless")
lg_fridge.make_ice()

mr_coffee = CoffeeMaker("white")
mr_coffee.make_coffee()

can = CanOpener("Green")
can.open_can()


from flask import Flask, render_template, request, redirect, session, url_for
app = Flask(__name__)
app.secret_key = 'ThisIsSecret'

def countRefresh(num):
    try:
        if (num>-1):
            session['counter'] += num
        else:
            session['counter'] = 0
    except KeyError:
        session['counter'] = 1

@app.route('/')
def index():
    countRefresh(1)
    return render_template("index.html")

@app.route('/poke', methods=['POST'])
def poke():
    if (request.form['poke'] == '2'):
        countRefresh(int(request.form['poke']))
    else:
        countRefresh(-1)
    return redirect(url_for('index'))
app.run(debug=True)


'''Escreva um programa que pergunte a quantidade de Km
percorridos por um carro alugado e a quantidade de dias pelos quais ele foi
alugado. Calcule o preço a pagar, sabendo que o carro custa R$60 por dia
e R$0,15 por Km rodado.'''

print("##### ALUGUEL DE CARROS #####")

km = float(input("Informe a KM percorrida com o carro: "))
dias = int(input("Informe quantos dias o carro foi alugado: "))

preco = (km * 0.15) + (dias * 60)

print("o preço do aluguel do carro é de R$:{}".format(preco))


# -*- coding: utf-8 -*-
"""
Homework 2: Question 3, Part 3
===============================================================================
Implement a kernelized version of the Pegasos algorithm. It should take in a
Gram matrix and should should output the support vector values, α, or a function
that makes a prediction for a new input. In this version, you do not need to
add a bias term.
"""

import numpy as np
import pylab as pl
from sklearn.svm import SVC
from plotBoundary import plotDecisionBoundary

def linear_kernel(x_a, x_b):
    return np.dot(x_a, x_b)

def make_gaussian_kernel(gamma):
    def gaussian_kernel(x_a, x_b):
        return np.exp(-np.linalg.norm(x_a - x_b)*gamma)
    return gaussian_kernel

def make_polynomial_kernel(degree):
    def polynomial_kernel(x_a, x_b):
        return np.power(x_a.dot(x_b),degree)
    return polynomial_kernel

def gram_matrix(x, kernel):
    num_samples, num_features = x.shape
    gram = np.zeros((num_samples, num_samples))
    for i, x_i in enumerate(x):
        for j, x_j in enumerate(x):
            gram[i,j] = kernel(x_i, x_j)
    return gram

class pegasosSVM:
    def __init__(self, L=1.0, kernel=linear_kernel):
        self._L = L
        self._kernel = kernel

    def fit(self, x, y):
        assert x.shape[0] == y.shape[0]
        num_samples, num_features = x.shape

        L = self._L
        gram = gram_matrix(x, self._kernel)

        t = 0.0
        alpha = np.zeros(num_samples)
        for epoch in range(1000):
            for i in range(num_samples):
                t += 1.0
                lr = 1.0 / (t * L)
                if y[i,0] * alpha.dot(gram[:,i]) < 1.0:
                    alpha[i] = (1.0 - lr * L) * alpha[i] + lr * y[i,0]
                else:
                    alpha[i] = (1.0 - lr * L) * alpha[i]
        self.sv_x = x
        self.sv_y = y
        self.sv_a = alpha

        print("sv", sum(abs(alpha) > max(alpha)*1e-6))

    def score(self, x, y):
        return sum(self.predict(x) * y > 0) / float(x.shape[0])

    def predict(self, x):
        num_samples, num_features = x.shape
        y = np.zeros((num_samples,1))
        for i, x_i in enumerate(x):
            for j, sv_x_i in enumerate(self.sv_x):
                y[i] += self.sv_a[j] * self._kernel(x_i, sv_x_i)
        return y

    def predictOne(self, x):
        return self.predict(np.array([x]))

dataset_id = "1"
train = np.loadtxt('data/data' + dataset_id + '_train.csv')
x_train, y_train = train[:,0:2], train[:,2:3]
test = np.loadtxt('data/data' + dataset_id + '_test.csv')
x_test, y_test = test[:,0:2], test[:,2:3]
val = np.loadtxt('data/data' + dataset_id + '_validate.csv')
x_val, y_val = val[:,0:2], val[:,2:3]

x_axis = [2**i for i in range(-5,5+1)]
y_axis = []
for gamma in x_axis:
#    svm = pegasosSVM(L=0.02, kernel=make_gaussian_kernel(gamma))
    svm = SVC(C=0.1,gamma=gamma)
    svm.fit(x_train, y_train.flatten())
    print(sum(svm.n_support_))
    print("pegasosSVM val", 1.0 - svm.score(x_val, y_val))
    print("pegasosSVM test", 1.0 - svm.score(x_test, y_test))
    print("")
#    plotDecisionBoundary(x_train, y_train, svm.predictOne, [-1,0,1], title = 'pegasosSVM - ' + str(gamma))

pl.show()


from collections import Counter

from django.shortcuts import render
counter_show = Counter()
counter_click = Counter()

def index(request):
    param = request.GET.get('from-landing')
    counter_click[param] += 1
    return render(request, 'index.html')

def landing(request):
    param = request.GET.get('ab-test-arg', 'original')
    if param == 'test':
        template = 'landing_alternate.html'
    else:
        template = 'landing.html'
    counter_show[param] += 1
    return render(request, template)

def stats(request):
    try:
        test_conversion = counter_click['test']/counter_show['test']
    except ZeroDivisionError:
        test_conversion = 'Нет данных'
    try:
        original_conversion = counter_click['original']/counter_show['original']
    except ZeroDivisionError:
        original_conversion = 'Нет данных'

    return render(request, 'stats.html', context={
        'test_conversion': test_conversion,
        'original_conversion': original_conversion,
    })


import re
import sys

def loan_calculator(amount, months, perc):
   plus = (amount / 100) * perc
   payment = (amount + plus) / months
   return payment

def loan():
   print("You just entered the loan calculator!\nPlease enter the amount that you want to loan in euros.")
   ans = int(input())
   if ans>1000000000:
      print("This amount is too big we can not loan this!\n")
      return
   print("Please enter the amount of months you want to reimburse your loan ")
   months = int(input())
   print("The amount you would need to pay each month is: " + str(loan_calculator(ans, months, (months /12))))

def interest_calculator(amount, year):
   return amount*(pow((1+2/2), 2*year))

def interest():
   print("You just entered the compound calculator!\nPlease enter the amount that you want invest in our portfolio.")
   ans = int(input())
   if ans>1000000000:
      print("This amount is too big we can not take this. That's what she said\n")
      interest()
   print("Please enter the amount of years that you want to take into account to see the result of the compounding")
   year = int(input())
   print("The amount you will have at the end of time period is: " + str(interest_calculator(ans, year) + "euros"))

def refund():
   while True:
      n = int(input('Please enter how old your purchase is in weeks: '))
      if n > 3:
         print('We cannot give refunds on purchases older than 3 weeks, sorry.')
         break
      elif n < 0:
         print('Please enter a valid input.')
         continue
      else:
         print('We will get back to you on that!')
         break

patterns = {
   'make transfer to' : 'transfer made',
   'transfer': {
      'problem' : 'A transfer can take up to 3 days. If you haven\'t received your transfer in 3 days please contact the support.',
      'make' : 'If you wish to make a transfer, please use the syntax: "make transfer to <receiver_name>'
   },
   'loan' : {
      'take' : loan,
      'information' : 'You can only take a loan up to 1.000.000.000 EUR'
   },
   'refund' : {
      'ask for a' : refund,
      'information' : 'You can ask for a refund on spendings that are not older than 3 weeks.'
   },
   'investment' : {
      'information' : "You can invest only less than a 1.000.000.000 EUR. Our compound interest rate is 2%" +
                  ", and it is done twice a year. YUou can use the compound/investment calculator calculate to profit over a certain period",
      'calculator' : interest,
   },
   'compound' : {
      'information' : "You can invest only less than a 1.000.000.000 EUR. Our compound interest rate is 2%" +
                  ", and it is done twice a year. YUou can use the compound/investment calculator calculate to profit over a certain period",
      'calculator' : interest
   }
}

def reg(string):
   return r"(.*)" + re.escape(string) + r"(.*)"

def match(user_input, dict):
   for pattern in dict:
         result = re.match(reg(pattern), user_input)
         if result:
            if type(dict[pattern]) == str:
               print(dict[pattern])
               break
            elif type(dict[pattern]) == type(dict):
               match(user_input, dict[pattern])
            else:
               dict[pattern]()
         # else:
         #    n = input('I\'m sorry, I did not understand that. Would you like to talk to a real person? (y/n)')
         #    if n == 'y':
         #       print('We will get back to you ASAP!')
         #       sys.exit()
         #    else:
         #       break

def main_loop():
   while True:
      user_input = input('what do you have to say: ').lower().strip('.!')
      match(user_input, patterns)

if __name__ == "__main__":
   main_loop()


import plotly.graph_objs as go
import requests
import datetime

def get_exchange_rate(date, currency_name):
    response = requests.get(
        f'https://api.privatbank.ua/p24api/exchange_rates?json&date={date}'
    )  # Делаем запрос на API Приватбанка и получаем Response - ответ, кладем его в переменную response
    dct = response.json()
    currencies = dct['exchangeRate']

    for currency_dict in currencies:
        if 'currency' in currency_dict:
            if currency_dict['currency'] == currency_name:
                return currency_dict["saleRateNB"]

    return False

currency_name = 'RUB'

dates = []
exchange_rates = []

year = 2014
while year <= 2020:
    print(f'Оброблюється {year} рік...')
    date = f"01.01.{year}"
    rate = get_exchange_rate(date, currency_name)
    dates.append(date)
    exchange_rates.append(rate)
    year += 1

print('Оброблюється сьогоднішнє число')
date_ = datetime.datetime.now()
date = f"{date_.day}.{date_.month}.{date_.year}"
rate = get_exchange_rate(date, currency_name)
dates.append(date)
exchange_rates.append(rate)

diag = go.Bar(x=dates, y=exchange_rates, name=f'Ціни валюти {currency_name} (у грн)')
go.Figure(data=[diag]).write_html('exchange_ratesRUB.html', auto_open=True)


import sys
from lib import timed_run
from copy import copy
import heapq

@timed_run
def read_input():
    lines = list()
    for line in sys.stdin:
        lines.append([int(x) for x in line.strip()])
    return lines

def dijkstra(graph):
    dist = dict()
    dist[(0, 0)] = 0
    frontier = []
    entry_finder = dict()

    entry = [0, (0, 0)]
    entry_finder[(0, 0)] = entry
    heapq.heappush(frontier, entry)
    DELETED = (-1, -1)

    max_r, max_c = len(graph), len(graph[0])

    end = (max_r - 1, max_c - 1)
    while True:
        cost, node = heapq.heappop(frontier)
        if node == DELETED:
            continue

        if node == end:
            return cost

        for neighbor in get_neighbors(node, max_r, max_c):
            alt = dist[node] + graph[neighbor[0]][neighbor[1]]
            dist_neighbor = dist.get(neighbor)

            if dist_neighbor is None or alt < dist_neighbor:
                dist[neighbor] = alt

                if dist_neighbor in entry_finder:
                    entry = entry_finder.pop(neighbor)
                    entry[-1] = DELETED

                new_entry = [alt, neighbor]
                entry_finder[neighbor] = new_entry
                heapq.heappush(frontier, new_entry)

def get_neighbors(coord, max_r, max_c):
    r, c = coord
    return tuple((r_, c_) for r_, c_ in ((r, c - 1), (r, c + 1), (r - 1, c), (r + 1, c)) if -1 < r_ < max_r and -1 < c_ < max_c)

def expand_graph(graph):
    new_graph = copy(graph)
    for inc in range(1, 5):  # copy down
        for row in graph:
            new_graph.append([x + inc if x + inc < 10 else x + inc - 9 for x in row])

    for row in new_graph:  # copy right
        original_row = copy(row)
        for inc in range(1, 5):
            row.extend([x + inc if x + inc < 10 else x + inc - 9 for x in original_row])

    return new_graph

@timed_run
def solve(graph):
    return dijkstra(graph)

if __name__ == '__main__':
    print(solve(expand_graph(read_input())))


#################################################################################################################
#                           Authors: Sara Starecheski    &   Clement Charles                                    #
#                                      -----Read Out Error Mitigation-----                                      #
#                                                                                #
#       Description: This python code creates the quibit states |00>, |01>, |10>, |11>, and counts the          #
#       states for a specified number shots from a quantum computer. Using this data a calibration matrix is    #
#       constructed. This matrix is manipulated such that the diagonal element are 1's.                         #
#                                                                                #
#                                               Dated: 3 Dec 2021                                               #
#################################################################################################################

#import modules here
import time
from qiskit import IBMQ, Aer, assemble, transpile, QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit.visualization import array_to_latex
from qiskit.tools.monitor import job_monitor
import numpy as np
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors import pauli_error, depolarizing_error
import matplotlib.pyplot as plt
import pytz
from pytz import timezone
from datetime import datetime
from csv import writer
from scipy.stats import norm
from collections import defaultdict
import json

###---------Global Variables----------#
times = []
matrix_array_for_noise = []
normalized_matrix_array_for_noise = []
calibration_det_for_noise = []
datet = []

#Simulation noise, (not required when backends in use)
def get_noise(p):                                                       #Not used with quantum backends, only with simulation
    error_meas = pauli_error([('X',p), ('I', 1 - p)])
    noise_model = NoiseModel()
    noise_model.add_all_qubit_quantum_error(error_meas, "measure")      # measurement error is applied to measurements

    return noise_model

noise_model = [0.01] #Not used

#IBMQ.save_account("d494816e01a737eb4ea87d4c9f6e2dcfdc7ed10d72ad68be30ecc7b5f66db445359005a848fc923f7ee96ba8909bffe76351ae2780cf53fd6a5f8d0ecc4fb491")
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q-education', group='fermilab-1', project='qjs-for-hep')
backend = provider.get_backend('ibm_perth')
#print("backend: ", backend)

shots = 5000

def time_of_interval(value,unit="s"):  #Function to create time interval depending on unit, set as seconds by default
    if unit == "s":
        return value
    if unit == "m" or "min":
        return value * 60
    if unit == "h" or "hr" or "hour":
        return value * 60 * 60

t_i = time_of_interval(1)   #time in specififed unit of each interval where the function will execute

num_of_intervals = 75        #number of interval to be loop in t_i

def calibration(noise):                                  #This function creates calibration matrix for a set of state vectors
    aer_sim = Aer.get_backend('aer_simulator')
    calibration_matrix = []
    for state in ['00','01','10','11']:                  #Four qubit states
                results = []
                qc = QuantumCircuit(2,2)
                if state[0]=='1':
                    qc.x(1)
                if state[1]=='1':
                    qc.x(0)
                qc.measure([0, 1],[0,1])

                #copied from summer school code
                transpiled_qc = transpile(qc, backend=backend)
                assembled_qc = assemble(transpiled_qc)
                job = backend.run(transpiled_qc, shots=shots)
                job_monitor(job, interval=2)
                results = job.result().get_counts()
                #results = aer_sim.run(assembled_qc, noise_model=get_noise(noise), shots=shots).result().get_counts()
                for state2 in ['00','01','10','11']:
                    check = state2 in results.keys()
                    if check == False:
                        results.update({state2 : 0})
                    all_results = []
                list00 = []
                all_results.append(results)
                for n in all_results:                               #Construct calibration matrix
                    for m in ['00','01','10','11']:
                        list00.append(n.get(m)/shots)
                calibration_matrix.append(list00)
    return(calibration_matrix)

for n in range(num_of_intervals):
    times.append(n*t_i)

#Run's code automatically on a stated time-interval
for noise_vals in noise_model:
    matrix_array = []
    for n in range(num_of_intervals):                              #Loop for the number of intervals dersired
        close_time = time.time() + t_i                             #Adds delay time
        run = True
        datet.append(time.localtime())                             #Appends times to a list
        while run == True:
            if time.time() > close_time:
                matrix_array.append(calibration(noise_vals))       #Runs function which return calibration matrix
                run = False
    matrix_array_for_noise.append(matrix_array)

for matrix_array2 in matrix_array_for_noise:                       #Loop runs once with no simulated noise
    #Loop over each matrix
    normalized_matrix_array2 = []
    for matrix in matrix_array2:
        normalized_matrix = []
        for i in range(len(matrix)):
            normalized_matrix00 = []
            for j in range(len(matrix[i])):
                normalized_matrix00.append(matrix[i][j]/np.sqrt(matrix[i][i]*matrix[j][j]))
            normalized_matrix.append(normalized_matrix00)
        normalized_matrix_array2.append(normalized_matrix)
    normalized_matrix_array_for_noise.append(normalized_matrix_array2)

#Save determinants
"""
for normalized_matrix_array2 in normalized_matrix_array_for_noise:
    calibration_det = []
    for mat in normalized_matrix_array2:
        calibration_det.append(1-np.linalg.det(np.array(mat)))
    calibration_det_for_noise.append(calibration_det)
"""
#off_diagonals = []

"""Defining arrays for each matrix element"""
all_off_diagonals = defaultdict(list)                                   #Instantiate defaultdict object

for matrix2 in normalized_matrix_array_for_noise:                       #Loop runs once with no simulated noise
    #Loop over each matrix
    normalized_matrix_array3 = []
    for matrix in matrix2:
        normalized_matrix = []
        for i in range(len(matrix)):
            normalized_matrix00 = []
            for j in range(len(matrix[i])):
                if i != j:
                    all_off_diagonals[str(i)+str(j)].append(matrix[i][j])
                    if matrix[i][j] == 0:
                        all_off_diagonals[str(i)+str(j)].append(0)

for key in all_off_diagonals:
    ###-----Normal Distribution-----###
    std = np.std(all_off_diagonals[key],ddof=1)
    mean = np.mean(all_off_diagonals[key])
    domain = np.linspace(np.min(all_off_diagonals[key]),np.max(all_off_diagonals[key]))
    #print(off_diagonals)
    ###-----Histogram Plotting-----###
    plt.plot(domain, norm.pdf(domain,mean,std))
    n, bins, patches = plt.hist(x=all_off_diagonals[key], bins=25, color='red',
                                alpha=0.7, rwidth=0.85,density=True)
    plt.grid(axis='y', alpha=0.75)
    plt.xlabel('Value')
    plt.ylabel('Frequency')
    plt.title('Histogram of $M_{i,j}$ elements where ij = '+key)
    maxfreq = n.max()
    plt.show()

### Save Matrix Data in txt File ###

with open('Documents/data.txt', 'w') as file:
    file.write(json.dumps(all_off_diagonals)) #Record dictionary
    file.writelines(["\n","times: ", str(datet)]) #Record times

### Code to Save Data to CSV ###

"""
with open("qc_data_histogram.csv", "a") as f_object:
    writer_object = writer(f_object)
    #writer_object.writerow(calibration_det_for_noise[0])
    #writer_object.writerow(datet)
    #writer_object.writerow(normalized_matrix_array_for_noise[0])
    writer_object.writerow(off_diagonals)
    #writer_object.writerow(all_off_diagonals)"""
"""
print(calibration_det_for_noise)
print(times)
"""
"""
with open("qc_data.csv", "a") as f_object:
    writer_object = writer(f_object)
    writer_object.writerow(calibration_det_for_noise[0])
    writer_object.writerow(datet)
    writer_object.writerow(normalized_matrix_array_for_noise[0])
    writer_object.writerow(matrix_array_for_noise[0])
    writer_object.writerow(times)
    f_object.close()

for dets in calibration_det_for_noise:
    i = i + 1
    plt.plot(datet,dets)
plt.title("Variation of Norm-Calibration Matrix Determinants Over Time")
plt.xlabel("Time (hours:minutes:seconds)")
plt.ylabel("Matrix determinant")
#plt.legend()
plt.show()

"""


import numpy as np
import cv2
import os
import time
import requests
import shutil
import json

def main():
    inputPath = '../Data/Tiles/green_tiles'
    outputPath = '../Data/Tiles/green_tiles_scaled'

    leftBottomTile13 = {"x": 5975, "y": 2598};
    rightUpperTile13 = {"x": 5990, "y": 2582};

    leftBottomTile12 = {"x": 2988, "y": 1298};
    rightUpperTile12 = {"x": 2994, "y": 1291};

    leftBottomTile11 = {"x": 1494, "y": 648};
    rightUpperTile11 = {"x": 1496, "y": 646};

    print(inputPath)
    print(outputPath)
    print(leftBottomTile13)
    print(rightUpperTile13)
    print(leftBottomTile12)
    print(rightUpperTile12)
    print(leftBottomTile11)
    print(rightUpperTile11)

    for i in range(leftBottomTile13["x"], rightUpperTile13["x"] + 1):
        for j in range(rightUpperTile13["y"], leftBottomTile13["y"] + 1):
            img = cv2.imread(os.path.join(inputPath, str(i) + "_" + str(j) + ".png"), cv2.IMREAD_UNCHANGED)
            cv2.imwrite(os.path.join(outputPath, str(i) + "_" + str(j) + "_13.png"), img)

    for i in range(leftBottomTile12["x"], rightUpperTile12["x"] + 1):
        for j in range(rightUpperTile12["y"], leftBottomTile12["y"] + 1):
            img00 = cv2.imread(os.path.join(inputPath, str(i * 2) + "_" + str(j * 2) + ".png"), cv2.IMREAD_UNCHANGED)
            img01 = cv2.imread(os.path.join(inputPath, str(i * 2) + "_" + str(j * 2 + 1) + ".png"), cv2.IMREAD_UNCHANGED)
            img10 = cv2.imread(os.path.join(inputPath, str(i * 2 + 1) + "_" + str(j * 2) + ".png"), cv2.IMREAD_UNCHANGED)
            img11 = cv2.imread(os.path.join(inputPath, str(i * 2 + 1) + "_" + str(j * 2 + 1) + ".png"), cv2.IMREAD_UNCHANGED)
            img0 = np.concatenate((img00, img01), axis=0)
            img1 = np.concatenate((img10, img11), axis=0)
            img = np.concatenate((img0, img1), axis=1)
            cv2.imwrite(os.path.join(outputPath, str(i) + "_" + str(j) + "_12.png"), img)

    for i in range(leftBottomTile11["x"], rightUpperTile11["x"] + 1):
        for j in range(rightUpperTile11["y"], leftBottomTile11["y"] + 1):
            img00 = cv2.imread(os.path.join(outputPath, str(i * 2) + "_" + str(j * 2) + "_12.png"), cv2.IMREAD_UNCHANGED)
            img01 = cv2.imread(os.path.join(outputPath, str(i * 2) + "_" + str(j * 2 + 1) + "_12.png"), cv2.IMREAD_UNCHANGED)
            img10 = cv2.imread(os.path.join(outputPath, str(i * 2 + 1) + "_" + str(j * 2) + "_12.png"), cv2.IMREAD_UNCHANGED)
            img11 = cv2.imread(os.path.join(outputPath, str(i * 2 + 1) + "_" + str(j * 2 + 1) + "_12.png"), cv2.IMREAD_UNCHANGED)
            img0 = np.concatenate((img00, img01), axis=0)
            img1 = np.concatenate((img10, img11), axis=0)
            img = np.concatenate((img0, img1), axis=1)
            cv2.imwrite(os.path.join(outputPath, str(i) + "_" + str(j) + "_11.png"), img)

if __name__ == '__main__':
    main()


from day01 import sum_to
from collections import deque

def part1(nums):
    d = deque()
    it = iter(nums)

    for _ in range(25):
        d.append(next(it))

    for x in it:
        if sum_to(d, x, 2) is None:
            return x

        d.popleft()
        d.append(x)

def part2(nums, invalid):
    for i in range(len(nums)):
        for j in range(i, len(nums)):
            subarray = nums[i:j]
            if sum(subarray) == invalid:
                return min(subarray) + max(subarray)

with open("day09_input.txt") as f:
    nums = list(int(x) for x in f)
    invalid = part1(nums)
    print(invalid)
    print(part2(nums, invalid))


# 오큰수
#  Ai의 오큰수는 오른쪽에 있으면서 Ai보다 큰 수 중에서 가장 왼쪽에 있는 수를 의미한다.
# 그러한 수가 없는 경우에 오큰수는 -1이다
# 총 N개의 수의 오큰수를 공백으로 구분해 출력
'''
4 N(1~1_000_000)
3 5 2 7 Ai(1~1_000_000), i 1~
'''

N = int(input())
arr = list(map(int, input().split()))

# print(arr)

# 3. 결과값을 리스트에 넣도록 하니까 pass
from collections import deque
stack = deque()
s = [0] * N
# 뒤에서부터 스택에 넣자
for i in range(N-1, -1, -1):
    if not stack:
        s[i] = -1
    else:
        if arr[i] < stack[-1]:
            s[i] = stack[-1]
        else:
            while stack and stack[-1] <= arr[i]:
                stack.pop()
            if not stack:
                s[i] = -1
            else:
                s[i] = stack[-1]
    # push
    stack.append(arr[i])
    print(i, stack)
print(*s)

# 2.
# 더 큰수를 어떻게 찾는게 효율적일까?
# 이진탐색? ㄴㄴ 정렬되어있지않음
# 스택 ㄱ, 결과값을 문자열로 저장해서 프린트
# 이렇게 하면 시간초과
# 2-1
# from collections import deque
# stack = deque()
# s = ''
# # 뒤에서부터 스택에 넣자
# for i in range(N-1, -1, -1):
#     if not stack:
#         s = '-1 ' + s
#     else:
#         if arr[i] < stack[-1]:
#             s = str(stack[-1]) + ' ' + s
#         else:
#             while stack and stack[-1] <= arr[i]:
#                 stack.pop()
#             if not stack:
#                 s = '-1 ' + s
#             else:
#                 s = str(stack[-1]) + ' ' + s
#     # push
#     stack.append(arr[i])
# print(s)

# 2-2. 스택 구현 > 시간 초과
# stack = [0]*(N+2)
# top = -1
# s = '' # 뒤에서부터 넣을거니까 답을 여기에 적어줄거임
# # 뒤에서부터 스택에 넣자
# for i in range(N-1, -1, -1):
#     if top == -1: # 빈 스택일때
#         s = '-1 ' + s
#     else: # 빈 스택 아닐때
#         if arr[i] < stack[top]: # 탑값이 더 클때
#             s = str(stack[top]) + ' ' + s
#         else:
#             while top != -1 and stack[top] <= arr[i]:
#                 top -= 1
#             if top == -1:
#                 s = '-1 ' + s
#             else:
#                 s = str(stack[top]) + ' ' + s
#     # push
#     top += 1
#     stack[top] = arr[i]
#     # print(i, top, stack)
# print(s)

# 1.
# 배열을 돌면서, 왼쪽에 나보다 큰수가 있는지 확인하고, 그거 저장
# 맨 마지막은 무조건 -1

# 배열 완전 탐색하는데
# 그거의 오른쪽 애들을 하나씩 탐색
# 시간초과
# for i in range(N-1):
#     for j in range(i, N):
#         # 큰 수 찾으면 프린트 하고 바로 다음 i 검색
#         if arr[i] < arr[j]:
#             print(arr[j], end=' ')
#             break # for j
#     else:
#         print(-1, end=' ')
# print(-1)


from flask import Blueprint, current_app, jsonify

from api.request import Request
from api.schemas import CMEPayloadSchema
from api.utils import get_jwt, form_cme_data, get_params

cme_api = Blueprint('cme', __name__)

@cme_api.route('/cme', methods=['GET'])
def cme():
    url = current_app.config['CME_URL']

    params = {'api_key': get_jwt(),
              **get_params(CMEPayloadSchema())}

    cme_request = Request(url, params=params)

    response = cme_request.get().json()

    cme_info = form_cme_data(response)

    return jsonify(cme_info)


# Declare a control variable
cables_removed = 0

# Ask user how many cables to remove
cables = int(input("How many cables should I remove?\n"))

# take action and remove how many cables user told you to remove
print()

while (cables_removed < cables):
    cables_removed += 1
    print("Removed cable.")
    #put the condition until the condition is going to be false and the function to stop running

'''cables_to_remove = int(input("\nHow many cables should I remove?\n"))
cables_removed = 0
i = cables_removed
j = cables_to_remove
print()
while i < j:
  i += 1
  print("Removed cable.")'''


# -*- coding: utf-8 -*-
"""
Created on Sun Apr 29 04:11:29 2018

@author: hjh83
"""

def SMA(A,n,m):
    # 设置alpha的比例
    alpha = m/n
    #通过ewm计算递归函数
    return A.ewm(alpha=alpha, adjust=False).mean()

def run_formula(dv,param=None):
    default_param = {'t1':14}
    if not param:
        param = default_param
    factor3=dv.add_formula('factor3','-SMA(close-Delay(close,%s),%s,1)'%(param['t1'],param['t1']),
                           is_quarterly=False,add_data=True,register_funcs={"SMA":SMA})
    return factor3


from collections import Counter

with open('../Input_files/14.txt') as file:
    input = file.read().strip()

template,rules = input.split('\n\n')
rules = rules.split('\n')
# rule_dict = {}
# for rule in rules:
#     pattern,insert = rule.split(' -> ')
#     rule_dict[pattern] = insert

couple_dict = {}
for rule in rules:
    pattern, insert = rule.split(' -> ')
    couple_dict[pattern] = [pattern[0]+insert,insert+pattern[1]]
in_str = template

#pt1 approach
# for step in range(10):
#     n_insert = 0
#     out_str = in_str
#     for ind in range(len(in_str)):
#         pattern = in_str[ind:ind+2]
#         if pattern in rule_dict.keys():
#             #pattern[0] + rule_dict[pattern] + pattern[1]
#             out_str = out_str[:2*n_insert+1] + rule_dict[pattern] + in_str[ind+1:]
#             n_insert += 1
#     in_str = out_str
#     if step == 9:
#         count_list = [out_str.count(letter) for letter in set(out_str)]
#         count_list.sort()
#         print(f"Part 1: {count_list[-1] - count_list[0]}")

#pt2 approach
def dict_to_counts(couple_count,first,last):
    letter_value = Counter()
    for key,val in couple_count.items():
        #we are counting the first key (val-1) times twice and once correct, similar for last key
        if key == first:
            letter_value[key[0]] += 0.5 * (val - 1) + 1
            letter_value[key[1]] += 0.5*val
        elif key == last:
            letter_value[key[0]] += 0.5 *val
            letter_value[key[1]] += 0.5 * (val - 1) + 1
        else:
            letter_value[key[0]] += 0.5 * val
            letter_value[key[1]] += 0.5 * val
    ans = max(letter_value.values()) - min(letter_value.values())
    return ans

couple_count = Counter()
for ind in range(len(template)-1):
    couple_count.update([template[ind:ind+2]])
first = template[0:2]
last = template[-2:]
for step in range(40):
    if first in couple_dict.keys():
        first = couple_dict[first][0]
    if last in couple_dict.keys():
        last = couple_dict[last][1]
    #issue += means couples before splitting are kept, issue = means couples from different originating couples are not added
    new_count = Counter()
    for key,val in list(couple_count.items()):
        if key in couple_dict.keys():
            l,r = couple_dict[key]
            new_count[l] += val
            new_count[r] += val
        else:
             new_count.update(key)
    #update couple count with the results from the new iteration
    couple_count = new_count
    if step == 9:
        print(f"Part 1: {dict_to_counts(couple_count, first, last)}")
    if step == 39:
        print(f"Part 2: {dict_to_counts(couple_count, first, last)}")


import turtle
#Create the screen instance
s = turtle.getscreen()
#Create Turtle instance
t = turtle.Turtle()
t.speed(15)
s.bgcolor("black")

cycle=1
while cycle<=72:
    count=1
    if cycle%5==0:
        t.pencolor("purple")
    elif cycle%5==1:
        t.pencolor("blue")
    elif cycle%5==2:
        t.pencolor("green")
    elif cycle%5==3:
        t.pencolor("yellow")
    else:
        t.pencolor("red")
    while count<=6:
        t.forward(100)
        t.right(60)
        count+=1
    cycle+=1
    t.right(5)
#Wait untill close the screen
turtle.done()