from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import collections
import io
import json
import re

from googlecloudsdk.api_lib.container import util as c_util
from googlecloudsdk.command_lib.compute.instance_templates import service_proxy_aux_data
from googlecloudsdk.command_lib.container.fleet import api_util
from googlecloudsdk.command_lib.container.fleet import kube_util as hub_kube_util
from googlecloudsdk.command_lib.projects import util as project_util
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import files

_EXPANSION_GATEWAY_NAME = 'istio-eastwestgateway'
_SERVICE_PROXY_BUCKET_NAME = (
    'gs://gce-service-proxy/service-proxy-agent/releases/'
    'service-proxy-agent-asm-{}-stable.tgz')
_SERVICE_PROXY_INSTALLER_BUCKET_NAME = (
    'gs://gce-service-proxy/service-proxy-agent-installer/'
    'releases/installer.tgz')
_GCE_SERVICE_PROXY_ASM_VERSION_METADATA = 'gce-service-proxy-asm-version'
_GCE_SERVICE_PROXY_INSTALLER_BUCKET_METADATA = (
    'gce-service-proxy-installer-bucket')
_GCE_SERVICE_PROXY_AGENT_BUCKET_METADATA = 'gce-service-proxy-agent-bucket'

_ISTIO_CANONICAL_SERVICE_NAME_LABEL = 'service.istio.io/canonical-name'
_ISTIO_CANONICAL_SERVICE_REVISION_LABEL = 'service.istio.io/canonical-revision'
_KUBERNETES_APP_NAME_LABEL = 'app.kubernetes.io/name'
_KUBERNETES_APP_VERSION_LABEL = 'app.kubernetes.io/version'

_ISTIO_META_CLOUDRUN_ADDR_KEY = 'ISTIO_META_CLOUDRUN_ADDR'
_CLOUDRUN_ADDR_KEY = 'CLOUDRUN_ADDR'

_ISTIO_DISCOVERY_PORT = '15012'

_WORKLOAD_PATTERN = r'(.*)\/(.*)'
# Allow non-prod GKE Hub memberships to be specified.
_GKEHUB_PATTERN = r'\/\/gkehub(.*).googleapis.com\/(.*)'
_ASM_VERSION_PATTERN = r':(.*)-'

_INCLUSTER_WEBHOOK_PREFIX = 'istio-sidecar-injector'
_MCP_WEBHOOK_PREFIX = 'istiod'

_MCP_ADDRESS = 'meshconfig.googleapis.com:443'

ServiceProxyMetadataArgs = collections.namedtuple('ServiceProxyMetadataArgs', [
    'asm_version', 'project_id', 'expansionagateway_ip',
    'service_proxy_api_server', 'identity_provider', 'service_account',
    'asm_proxy_config', 'mcp_env_config', 'trust_domain', 'mesh_id', 'network',
    'asm_labels', 'workload_name', 'workload_namespace', 'canonical_service',
    'canonical_revision', 'asm_revision', 'root_cert'
])

def ParseWorkload(workload):
  """Parses the workload value to workload namespace and name.

  Args:
    workload: The workload value with namespace/name format.

  Returns:
    workload namespace and workload name.

  Raises:
    Error: if the workload value is invalid.
  """
  workload_match = re.search(_WORKLOAD_PATTERN, workload)
  if workload_match:
    return workload_match.group(1), workload_match.group(2)
  raise exceptions.Error(
      'value workload: {} is invalid. Workload value should have the format'
      'namespace/name.'.format(workload))

def _ParseMembershipName(owner_id):
  """Get membership name from an owner id value.

  Args:
    owner_id: The owner ID value of a membership. e.g.,
    //gkehub.googleapis.com/projects/123/locations/global/memberships/test.

  Returns:
    The full resource name of the membership, e.g.,
      projects/foo/locations/global/memberships/name.

  Raises:
    Error: if the membership name cannot be parsed.
  """
  membership_match = re.search(_GKEHUB_PATTERN, owner_id)
  if membership_match:
    return membership_match.group(2)
  raise exceptions.Error(
      'value owner_id: {} is invalid.'.format(owner_id))

def _GetVMIdentityProvider(membership_manifest, workload_namespace):
  """Get the identity provider for the VMs.

  Args:
    membership_manifest: The membership manifest from the cluster.
    workload_namespace: The namespace of the VM workload.

  Returns:
    The identity provider value to be used on the VM connected to the cluster.

  Raises:
    ClusterError: If the membership manifest cannot be read.
  """
  if not membership_manifest:
    raise ClusterError('Cannot verify an empty membership from the cluster')

  try:
    membership_data = yaml.load(membership_manifest)
  except yaml.Error as e:
    raise exceptions.Error(
        'Invalid membership from the cluster {}'.format(membership_manifest), e)

  owner_id = _GetNestedKeyFromManifest(
      membership_data, 'spec', 'owner', 'id')
  if not owner_id:
    raise ClusterError('Invalid membership does not have an owner id. Please '
                       'make sure your cluster is correctly registered and '
                       'retry.')

  membership_name = _ParseMembershipName(owner_id)
  membership = api_util.GetMembership(membership_name)
  if not membership.uniqueId:
    raise exceptions.Error('Invalid membership {} does not have a unique_Id '
                           'field. Please make sure your cluster is correctly '
                           'registered and retry.'.format(membership_name))

  return '{}@google@{}'.format(membership.uniqueId, workload_namespace)

def _RetrieveProxyConfig(is_mcp, mesh_config):
  """Retrieve proxy config from a mesh config.

  Args:
    is_mcp: Whether the control plane is managed or not.
    mesh_config: A mesh config from the cluster.

  Returns:
    proxy_config: The proxy config from the mesh config.
  """
  try:
    proxy_config = mesh_config['defaultConfig']
  except (KeyError, TypeError):
    if is_mcp:
      return {}
    raise exceptions.Error(
        'Proxy config cannot be found in the Anthos Service Mesh.')

  return proxy_config

def _RetrieveTrustDomain(is_mcp, mesh_config):
  """Retrieve trust domain from a mesh config.

  Args:
    is_mcp: Whether the control plane is managed or not.
    mesh_config: A mesh config from the cluster.

  Returns:
    trust_domain: The trust domain from the mesh config.
  """
  try:
    trust_domain = mesh_config['trustDomain']
  except (KeyError, TypeError):
    if is_mcp:
      return None
    raise exceptions.Error(
        'Trust Domain cannot be found in the Anthos Service Mesh.')

  return trust_domain

def _RetrieveMeshId(is_mcp, mesh_config):
  """Retrieve mesh id from a mesh config.

  Args:
    is_mcp: Whether the control plane is managed or not.
    mesh_config: A mesh config from the cluster.

  Returns:
    mesh_id: The mesh id from the mesh config.
  """
  proxy_config = _RetrieveProxyConfig(is_mcp, mesh_config)

  try:
    mesh_id = proxy_config['meshId']
  except (KeyError, TypeError):
    if is_mcp:
      return None
    raise exceptions.Error(
        'Mesh ID cannot be found in the Anthos Service Mesh.')

  return mesh_id

def _RetrieveDiscoveryAddress(mesh_config):
  """Get the discovery address used in the MCP installation.

  Args:
    mesh_config: A mesh config from the cluster.

  Returns:
    The discovery address.
  """
  proxy_config = _RetrieveProxyConfig(is_mcp=True, mesh_config=mesh_config)

  if proxy_config is None:
    proxy_config = {}

  # When user does not provide a discoveryAddress in the proxy config, the
  # default MCP discovery address is used here. We expect mesh agent to get
  # the correct discoveryAddress during bootstrap.
  return proxy_config.get('discoveryAddress', _MCP_ADDRESS)

def _IsMCP(kube_client, revision):
  """Check if ASM control plane is managed.

  Args:
    kube_client: A kubernetes client for the cluster.
    revision: The ASM revision to check for control plane type.

  Returns:
    True if the control plane is MCP, otherwise False.
  """
  # MCP uses the url field in the webhook ClientConfig and In-cluster CP uses
  # the service field instead.
  # TODO(b/195018417): Further validate the URL format when it is using
  # meshconfig endpoint.
  url = kube_client.RetrieveMutatingWebhookURL(revision)

  if url:
    return True
  return False

def _GetWorkloadLabels(workload_manifest):
  """Get the workload labels from a workload manifest.

  Args:
    workload_manifest: The manifest of the workload.

  Returns:
    The workload labels.

  Raises:
    WorkloadError: If the workload manifest cannot be read.
  """
  if not workload_manifest:
    raise WorkloadError('Cannot verify an empty workload from the cluster')

  try:
    workload_data = yaml.load(workload_manifest)
  except yaml.Error as e:
    raise exceptions.Error(
        'Invalid workload from the cluster {}'.format(workload_data), e)

  workload_labels = _GetNestedKeyFromManifest(workload_data, 'spec', 'metadata',
                                              'labels')

  return workload_labels

def _GetCanonicalServiceName(workload_name, workload_manifest):
  """Get the canonical service name of the workload.

  Args:
    workload_name: The name of the workload.
    workload_manifest: The manifest of the workload.

  Returns:
    The canonical service name of the workload.
  """
  workload_labels = _GetWorkloadLabels(workload_manifest)

  return _ExtractCanonicalServiceName(workload_labels, workload_name)

def _GetCanonicalServiceRevision(workload_manifest):
  """Get the canonical service revision of the workload.

  Args:
    workload_manifest: The manifest of the workload.

  Returns:
    The canonical service revision of the workload.
  """
  workload_labels = _GetWorkloadLabels(workload_manifest)

  return _ExtractCanonicalServiceRevision(workload_labels)

def _ExtractCanonicalServiceName(workload_labels, workload_name):
  """Get the canonical service name of the workload.

  Args:
    workload_labels: A map of workload labels.
    workload_name: The name of the workload.

  Returns:
    The canonical service name of the workload.
  """
  if not workload_labels:
    return workload_name

  svc = workload_labels.get(_ISTIO_CANONICAL_SERVICE_NAME_LABEL)
  if svc:
    return svc

  svc = workload_labels.get(_KUBERNETES_APP_NAME_LABEL)
  if svc:
    return svc

  svc = workload_labels.get('app')
  if svc:
    return svc

  return workload_name

def _ExtractCanonicalServiceRevision(workload_labels):
  """Get the canonical service revision of the workload.

  Args:
    workload_labels: A map of workload labels.

  Returns:
    The canonical service revision of the workload.
  """
  if not workload_labels:
    return 'latest'

  rev = workload_labels.get(_ISTIO_CANONICAL_SERVICE_REVISION_LABEL)
  if rev:
    return rev

  rev = workload_labels.get(_KUBERNETES_APP_VERSION_LABEL)
  if rev:
    return rev

  rev = workload_labels.get('version')
  if rev:
    return rev

  return 'latest'

def VerifyWorkloadSetup(workload_manifest):
  """Verify VM workload setup in the cluster."""
  if not workload_manifest:
    raise WorkloadError('Cannot verify an empty workload from the cluster')

  try:
    workload_data = yaml.load(workload_manifest)
  except yaml.Error as e:
    raise exceptions.Error(
        'Invalid workload from the cluster {}'.format(workload_manifest), e)

  identity_provider_value = _GetNestedKeyFromManifest(
      workload_data, 'spec', 'metadata', 'annotations',
      'security.cloud.google.com/IdentityProvider')
  if identity_provider_value != 'google':
    raise WorkloadError('Unable to find the GCE IdentityProvider in the '
                        'specified WorkloadGroup. Please make sure the '
                        'GCE IdentityProvider is specified in the '
                        'WorkloadGroup.')

def RetrieveWorkloadRevision(namespace_manifest):
  """Retrieve the Anthos Service Mesh revision for the workload."""
  if not namespace_manifest:
    raise WorkloadError('Cannot verify an empty namespace from the cluster')

  try:
    namespace_data = yaml.load(namespace_manifest)
  except yaml.Error as e:
    raise exceptions.Error(
        'Invalid namespace from the cluster {}'.format(namespace_manifest), e)

  workload_revision = _GetNestedKeyFromManifest(namespace_data, 'metadata',
                                                'labels', 'istio.io/rev')
  if not workload_revision:
    raise WorkloadError('Workload namespace does not have an Anthos Service '
                        'Mesh revision label. Please make sure the namespace '
                        'is labeled and try again.')

  return workload_revision

def _RetrieveWorkloadServiceAccount(workload_manifest):
  """Retrieve the service account used for the workload."""
  if not workload_manifest:
    raise WorkloadError('Cannot verify an empty workload from the cluster')

  try:
    workload_data = yaml.load(workload_manifest)
  except yaml.Error as e:
    raise exceptions.Error(
        'Invalid workload from the cluster {}'.format(workload_manifest), e)

  service_account = _GetNestedKeyFromManifest(workload_data, 'spec', 'template',
                                              'serviceAccount')
  return service_account

def _RetrieveServiceProxyMetadata(args, is_mcp, kube_client, project_id,
                                  network_resource, workload_namespace,
                                  workload_name, workload_manifest,
                                  membership_manifest, asm_revision,
                                  mesh_config):
  """Retrieve the necessary metadata to configure the service proxy."""

  if is_mcp:
    asm_version = None
    expansionagateway_ip = None
    root_cert = None
    service_proxy_api_server = _RetrieveDiscoveryAddress(mesh_config)
    env_config = kube_client.RetrieveEnvConfig(asm_revision)
  else:
    if _GCE_SERVICE_PROXY_ASM_VERSION_METADATA in args.metadata:
      asm_version = args.metadata[_GCE_SERVICE_PROXY_ASM_VERSION_METADATA]
    else:
      asm_version = kube_client.RetrieveASMVersion(asm_revision)
    expansionagateway_ip = kube_client.RetrieveExpansionGatewayIP()
    root_cert = kube_client.RetrieveKubernetesRootCert()
    service_proxy_api_server = '{}:{}'.format(expansionagateway_ip,
                                              _ISTIO_DISCOVERY_PORT)
    env_config = None

  identity_provider = _GetVMIdentityProvider(membership_manifest,
                                             workload_namespace)

  service_account = _RetrieveWorkloadServiceAccount(workload_manifest)

  asm_proxy_config = _RetrieveProxyConfig(is_mcp, mesh_config)

  trust_domain = _RetrieveTrustDomain(is_mcp, mesh_config)

  mesh_id = _RetrieveMeshId(is_mcp, mesh_config)

  network = network_resource.split('/')[-1]

  asm_labels = _GetWorkloadLabels(workload_manifest)

  canonical_service = _GetCanonicalServiceName(workload_name, workload_manifest)

  canonical_revision = _GetCanonicalServiceRevision(workload_manifest)

  return ServiceProxyMetadataArgs(
      asm_version, project_id, expansionagateway_ip, service_proxy_api_server,
      identity_provider, service_account, asm_proxy_config, env_config,
      trust_domain, mesh_id, network, asm_labels, workload_name,
      workload_namespace, canonical_service, canonical_revision, asm_revision,
      root_cert)

def _ModifyInstanceTemplate(args, is_mcp, metadata_args):
  """Modify the instance template to include the service proxy metadata."""

  if metadata_args.asm_labels:
    asm_labels = metadata_args.asm_labels
  else:
    asm_labels = collections.OrderedDict()

  asm_labels[
      _ISTIO_CANONICAL_SERVICE_NAME_LABEL] = metadata_args.canonical_service
  asm_labels[
      _ISTIO_CANONICAL_SERVICE_REVISION_LABEL] = metadata_args.canonical_revision

  asm_labels_string = json.dumps(asm_labels, sort_keys=True)

  service_proxy_config = collections.OrderedDict()
  service_proxy_config['mode'] = 'ON'

  service_proxy_config['proxy-spec'] = {
      'network': metadata_args.network,
      'api-server': metadata_args.service_proxy_api_server,
      'log-level': 'info',
  }

  service_proxy_config['service'] = {}

  proxy_config = metadata_args.asm_proxy_config
  if not proxy_config:
    proxy_config = collections.OrderedDict()
  if 'proxyMetadata' not in proxy_config:
    proxy_config['proxyMetadata'] = collections.OrderedDict()
  else:
    proxy_config['proxyMetadata'] = collections.OrderedDict(
        proxy_config['proxyMetadata'])

  proxy_metadata = proxy_config['proxyMetadata']
  proxy_metadata['ISTIO_META_WORKLOAD_NAME'] = metadata_args.workload_name
  proxy_metadata['POD_NAMESPACE'] = metadata_args.workload_namespace
  proxy_metadata['USE_TOKEN_FOR_CSR'] = 'true'
  proxy_metadata['ISTIO_META_DNS_CAPTURE'] = 'true'
  proxy_metadata['ISTIO_META_AUTO_REGISTER_GROUP'] = metadata_args.workload_name
  proxy_metadata['SERVICE_ACCOUNT'] = metadata_args.service_account
  proxy_metadata[
      'CREDENTIAL_IDENTITY_PROVIDER'] = metadata_args.identity_provider
  if metadata_args.trust_domain:
    proxy_metadata['TRUST_DOMAIN'] = metadata_args.trust_domain
  if metadata_args.mesh_id:
    proxy_metadata['ISTIO_META_MESH_ID'] = metadata_args.mesh_id
  proxy_metadata['ISTIO_META_NETWORK'] = '{}-{}'.format(
      metadata_args.project_id, metadata_args.network)
  proxy_metadata['CANONICAL_SERVICE'] = metadata_args.canonical_service
  proxy_metadata['CANONICAL_REVISION'] = metadata_args.canonical_revision
  proxy_metadata['ISTIO_METAJSON_LABELS'] = asm_labels_string

  if metadata_args.asm_revision == 'default':
    proxy_metadata['ASM_REVISION'] = ''
  else:
    proxy_metadata['ASM_REVISION'] = metadata_args.asm_revision

  gce_software_declaration = collections.OrderedDict()
  service_proxy_agent_recipe = collections.OrderedDict()

  service_proxy_agent_recipe['name'] = 'install-gce-service-proxy-agent'
  service_proxy_agent_recipe['desired_state'] = 'INSTALLED'

  if is_mcp:
    service_proxy_agent_recipe['installSteps'] = [{
        'scriptRun': {
            'script':
                service_proxy_aux_data
                .startup_script_for_asm_service_proxy_installer
        }
    }]
    proxy_metadata.update(metadata_args.mcp_env_config)
    # ISTIO_META_CLOUDRUN_ADDR must be set to generate node metadata on VM.
    if _CLOUDRUN_ADDR_KEY in proxy_metadata:
      proxy_metadata[_ISTIO_META_CLOUDRUN_ADDR_KEY] = proxy_metadata[
          _CLOUDRUN_ADDR_KEY]
    if 'gce-service-proxy-installer-bucket' not in args.metadata:
      args.metadata['gce-service-proxy-installer-bucket'] = (
          _SERVICE_PROXY_INSTALLER_BUCKET_NAME)
  else:
    service_proxy_agent_recipe['installSteps'] = [{
        'scriptRun': {
            'script':
                service_proxy_aux_data.startup_script_for_asm_service_proxy
                .format(
                    ingress_ip=metadata_args.expansionagateway_ip,
                    asm_revision=metadata_args.asm_revision)
        }
    }]
    proxy_metadata['ISTIO_META_ISTIO_VERSION'] = metadata_args.asm_version
    args.metadata['rootcert'] = metadata_args.root_cert
    if _GCE_SERVICE_PROXY_AGENT_BUCKET_METADATA not in args.metadata:
      args.metadata[
          _GCE_SERVICE_PROXY_AGENT_BUCKET_METADATA] = (
              _SERVICE_PROXY_BUCKET_NAME.format(metadata_args.asm_version))

  gce_software_declaration['softwareRecipes'] = [service_proxy_agent_recipe]

  service_proxy_config['asm-config'] = proxy_config

  args.metadata['enable-osconfig'] = 'true'
  args.metadata['enable-guest-attributes'] = 'true'
  args.metadata['osconfig-disabled-features'] = 'tasks'
  args.metadata['gce-software-declaration'] = json.dumps(
      gce_software_declaration)
  args.metadata['gce-service-proxy'] = json.dumps(
      service_proxy_config, sort_keys=True)

  if args.labels is None:
    args.labels = collections.OrderedDict()
  args.labels['asm_service_name'] = metadata_args.canonical_service
  args.labels['asm_service_namespace'] = metadata_args.workload_namespace
  if metadata_args.mesh_id:
    args.labels['mesh_id'] = metadata_args.mesh_id
  else:
    # This works for now as we only support adding VM to the Fleet project. But
    # it should be the Fleet project instead.
    project_number = project_util.GetProjectNumber(metadata_args.project_id)
    args.labels['mesh_id'] = 'proj-{}'.format(project_number)
  # For ASM VM usage tracking.
  args.labels['gce-service-proxy'] = 'asm-istiod'

def ConfigureInstanceTemplate(args, kube_client, project_id, network_resource,
                              workload_namespace, workload_name,
                              workload_manifest, membership_manifest,
                              asm_revision, mesh_config):
  """Configure the provided instance template args with ASM metadata."""
  is_mcp = _IsMCP(kube_client, asm_revision)

  service_proxy_metadata_args = _RetrieveServiceProxyMetadata(
      args, is_mcp, kube_client, project_id, network_resource,
      workload_namespace, workload_name, workload_manifest, membership_manifest,
      asm_revision, mesh_config)

  _ModifyInstanceTemplate(args, is_mcp, service_proxy_metadata_args)

class KubernetesClient(object):
  """Kubernetes client for access Kubernetes APIs."""

  def __init__(self, gke_cluster=None):
    """KubernetesClient constructor.

    Args:
      gke_cluster: the location/name of the GKE cluster.
    """
    self.kubectl_timeout = '20s'

    self.temp_kubeconfig_dir = files.TemporaryDirectory()
    self.processor = hub_kube_util.KubeconfigProcessor(
        api_adapter=None, gke_uri=None, gke_cluster=gke_cluster,
        kubeconfig=None, internal_ip=False, cross_connect_subnetwork=None,
        private_endpoint_fqdn=None, context=None)
    self.kubeconfig, self.context = self.processor.GetKubeconfigAndContext(
        self.temp_kubeconfig_dir)

  def __enter__(self):
    return self

  def __exit__(self, *_):
    # delete temp directory
    if self.temp_kubeconfig_dir is not None:
      self.temp_kubeconfig_dir.Close()

  def HasNamespaceReaderPermissions(self, *namespaces):
    """Check to see if the user has read permissions in the namespaces.

    Args:
      *namespaces: The namespaces to verify reader permissions.

    Returns:
      true, if reads can be performed on all of the specified namespaces.

    Raises:
      Error: if failing to get check for read permissions.
      Error: if read permissions are not found.
    """
    for ns in namespaces:
      out, err = self._RunKubectl(
          ['auth', 'can-i', 'get', '*', '-n', ns], None)
      if err:
        raise exceptions.Error(
            'Failed to check if the user can read resources in {} namespace: {}'
            .format(ns, err))
      if 'yes' not in out:
        raise exceptions.Error(
            'Missing permissions to read resources in {} namespace'.format(ns))
    return True

  def NamespacesExist(self, *namespaces):
    """Check to see if the namespaces exist in the cluster.

    Args:
      *namespaces: The namespaces to check.

    Returns:
      true, if namespaces exist.

    Raises:
      Error: if failing to verify the namespaces.
      Error: if at least one of the namespaces do not exist.
    """
    for ns in namespaces:
      _, err = self._RunKubectl(['get', 'namespace', ns], None)
      if err:
        if 'NotFound' in err:
          raise exceptions.Error('Namespace {} does not exist: {}'.format(
              ns, err))
        raise exceptions.Error(
            'Failed to check if namespace {} exists: {}'.format(ns, err))
    return True

  def GetNamespace(self, namespace):
    """Get the YAML output of the specified namespace."""
    out, err = self._RunKubectl([
        'get', 'namespace', namespace, '-o', 'yaml'], None)
    if err:
      raise exceptions.Error(
          'Error retrieving Namespace {}: {}'.format(namespace, err))
    return out

  def GetMembershipCR(self):
    """Get the YAML output of the Membership CR."""
    if not self._MembershipCRDExists():
      raise ClusterError(
          'Membership CRD is not found in the cluster. Please make sure your '
          'cluster is registered and retry.')

    out, err = self._RunKubectl(
        ['get',
         'memberships.hub.gke.io',
         'membership', '-o', 'yaml'], None)
    if err:
      if 'NotFound' in err:
        raise ClusterError(
            'The specified cluster is not registered to a fleet. '
            'Please make sure your cluster is registered and retry.')
      raise exceptions.Error(
          'Error retrieving the Membership CR: {}'.format(err))
    return out

  def _MembershipCRDExists(self):
    """Verifies if GKE Hub membership CRD exists."""
    _, err = self._RunKubectl(
        ['get',
         'customresourcedefinitions.v1.apiextensions.k8s.io',
         'memberships.hub.gke.io'], None)
    if err:
      if 'NotFound' in err:
        return False
      raise exceptions.Error(
          'Error retrieving the Membership CRD: {}'.format(err))
    return True

  def GetIdentityProviderCR(self):
    """Get the YAML output of the IdentityProvider CR."""
    if not self._IdentityProviderCRDExists():
      raise ClusterError(
          'IdentityProvider CRD is not found in the cluster. Please install '
          'Anthos Service Mesh with VM support and retry.')

    out, err = self._RunKubectl(
        ['get',
         'identityproviders.security.cloud.google.com',
         'google', '-o', 'yaml'], None)
    if err:
      if 'NotFound' in err:
        raise ClusterError(
            'GCE identity provider is not found in the cluster. '
            'Please install Anthos Service Mesh with VM support.')
      raise exceptions.Error(
          'Error retrieving IdentityProvider google in default namespace: {}'
          .format(err))
    return out

  def _IdentityProviderCRDExists(self):
    """Verifies if Identity Provider CRD exists."""
    _, err = self._RunKubectl(
        ['get',
         'customresourcedefinitions.v1.apiextensions.k8s.io',
         'identityproviders.security.cloud.google.com'], None)
    if err:
      if 'NotFound' in err:
        return False
      raise exceptions.Error(
          'Error retrieving the Identity Provider CRD: {}'.format(err))
    return True

  def GetWorkloadGroupCR(self, workload_namespace, workload_name):
    """Get the YAML output of the specified WorkloadGroup CR."""
    if not self._WorkloadGroupCRDExists():
      raise ClusterError(
          'WorkloadGroup CRD is not found in the cluster. Please install '
          'Anthos Service Mesh and retry.')

    out, err = self._RunKubectl([
        'get', 'workloadgroups.networking.istio.io', workload_name, '-n',
        workload_namespace, '-o', 'yaml'
    ], None)
    if err:
      if 'NotFound' in err:
        raise WorkloadError(
            'WorkloadGroup {} in namespace {} is not found in the '
            'cluster. Please create the WorkloadGroup and retry.'.format(
                workload_name, workload_namespace))
      raise exceptions.Error(
          'Error retrieving WorkloadGroup {} in namespace {}: {}'.format(
              workload_name, workload_namespace, err))
    return out

  def _WorkloadGroupCRDExists(self):
    """Verifies if WorkloadGroup CRD exists."""
    _, err = self._RunKubectl(
        ['get',
         'customresourcedefinitions.v1.apiextensions.k8s.io',
         'workloadgroups.networking.istio.io'], None)
    if err:
      if 'NotFound' in err:
        return False
      raise exceptions.Error(
          'Error retrieving the WorkloadGroup CRD: {}'.format(err))
    return True

  def ExpansionGatewayDeploymentExists(self):
    """Verifies if the ASM Expansion Gateway deployment exists."""
    _, err = self._RunKubectl(
        ['get', 'deploy', _EXPANSION_GATEWAY_NAME, '-n', 'istio-system'], None)
    if err:
      if 'NotFound' in err:
        return False
      raise exceptions.Error(
          'Error retrieving the expansion gateway deployment: {}'.format(err))
    return True

  def ExpansionGatewayServiceExists(self):
    """Verifies if the ASM Expansion Gateway service exists."""
    _, err = self._RunKubectl(
        ['get', 'service', _EXPANSION_GATEWAY_NAME, '-n', 'istio-system'], None)
    if err:
      if 'NotFound' in err:
        return False
      raise exceptions.Error(
          'Error retrieving the expansion gateway service: {}'.format(err))
    return True

  def RetrieveExpansionGatewayIP(self):
    """Retrieves the expansion gateway IP from the cluster."""
    if not self.ExpansionGatewayDeploymentExists():
      raise ClusterError(
          'The gateway {} deployment is not found in the cluster. Please '
          'install Anthos Service Mesh with VM support and retry.'.format(
              _EXPANSION_GATEWAY_NAME))

    if not self.ExpansionGatewayServiceExists():
      raise ClusterError(
          'The gateway {} service is not found in the cluster. Please '
          'install Anthos Service Mesh with VM support and retry.'.format(
              _EXPANSION_GATEWAY_NAME))

    out, err = self._RunKubectl([
        'get', 'svc', _EXPANSION_GATEWAY_NAME, '-n', 'istio-system', '-o',
        'jsonpath={.status.loadBalancer.ingress[0].ip}'
    ], None)
    if err:
      raise exceptions.Error(
          'Error retrieving expansion gateway IP: {}'.format(err))
    return out

  def RetrieveKubernetesRootCert(self):
    """Retrieves the root cert from the cluster."""
    out, err = self._RunKubectl(
        ['get', 'configmap', 'kube-root-ca.crt', '-o',
         r'jsonpath="{.data.ca\.crt}"'], None)
    if err:
      if 'NotFound' in err:
        raise ClusterError(
            'Cluster root certificate is not found.')
      raise exceptions.Error(
          'Error retrieving Kubernetes root cert: {}'.format(err))
    return out.strip('\"')

  def RetrieveASMVersion(self, revision):
    """Retrieves the version of ASM."""
    image, err = self._RunKubectl([
        'get', 'deploy', '-l', 'istio.io/rev={},app=istiod'.format(revision),
        '-n', 'istio-system', '-o',
        'jsonpath="{.items[0].spec.template.spec.containers[0].image}"'
    ], None)
    if err:
      if 'NotFound' in err:
        raise ClusterError(
            'Anthos Service Mesh revision {} is not found in '
            'the cluster. Please install Anthos Service Mesh and '
            'try again.'.format(revision))
      raise exceptions.Error(
          'Error retrieving the version of Anthos Service Mesh: {}'.format(err))

    if not image:
      raise ClusterError('Anthos Service Mesh revision {} does not have an '
                         'image property. Please re-install Anthos Service '
                         'Mesh.'.format(revision))

    version_match = re.search(_ASM_VERSION_PATTERN, image)
    if version_match:
      return version_match.group(1)
    raise exceptions.Error(
        'Value image: {} is invalid.'.format(image))

  def RetrieveEnvConfig(self, revision):
    """Retrieves the env-* config map for MCP."""
    if revision == 'default':
      env_config_name = 'env'
    else:
      env_config_name = 'env-{}'.format(revision)

    out, err = self._RunKubectl(
        ['get', 'configmap', env_config_name, '-n', 'istio-system',
         '-o', 'jsonpath={.data}'], None)
    if err:
      if 'NotFound' in err:
        raise ClusterError(
            'Managed Control Plane revision {} is not found in '
            'the cluster. Please install Managed Control Plane and '
            'try again.'.format(revision))
      raise exceptions.Error(
          'Error retrieving the config map {} from the cluster: {}'.format(
              env_config_name, err))

    try:
      env_config = yaml.load(out)
    except yaml.Error:
      raise exceptions.Error(
          'Invalid config map from the cluster: {}'.format(out))

    return env_config

  def RetrieveMeshConfig(self, revision):
    """Retrieves the MeshConfig for the ASM revision."""
    if revision == 'default':
      mesh_config_name = 'istio'
    else:
      mesh_config_name = 'istio-{}'.format(revision)

    out, err = self._RunKubectl(
        ['get', 'configmap', mesh_config_name, '-n', 'istio-system',
         '-o', 'jsonpath={.data.mesh}'], None)
    if err:
      if 'NotFound' in err:
        raise ClusterError(
            'Anthos Service Mesh revision {} is not found in '
            'the cluster. Please install Anthos Service Mesh and '
            'try again.'.format(revision))
      raise exceptions.Error(
          'Error retrieving the mesh config from the cluster: {}'.format(
              err))

    try:
      mesh_config = yaml.load(out)
    except yaml.Error:
      raise exceptions.Error(
          'Invalid mesh config from the cluster: {}'.format(out))

    return mesh_config

  def RetrieveMutatingWebhookURL(self, revision):
    """Retrieves the Mutating Webhook URL used for a revision."""
    # There are two patterns of mutating webhook name in ASM. We check both in
    # case they are being used interchaeably.
    if revision == 'default':
      incluster_webhook = _INCLUSTER_WEBHOOK_PREFIX
    else:
      incluster_webhook = '{}-{}'.format(_INCLUSTER_WEBHOOK_PREFIX, revision)

    mcp_webhook = '{}-{}'.format(_MCP_WEBHOOK_PREFIX, revision)

    incluster_out, incluster_err = self._RunKubectl(
        ['get', 'mutatingwebhookconfiguration', incluster_webhook,
         '-o', 'jsonpath={.webhooks[0].clientConfig.url}'], None)
    mcp_out, mcp_err = self._RunKubectl(
        ['get', 'mutatingwebhookconfiguration', mcp_webhook,
         '-o', 'jsonpath={.webhooks[0].clientConfig.url}'], None)

    if incluster_err and mcp_err:
      if 'NotFound' in incluster_err and 'NotFound' in mcp_err:
        raise ClusterError(
            'Anthos Service Mesh revision {} is not found in '
            'the cluster. Please install Anthos Service Mesh and '
            'try again.'.format(revision))
      raise exceptions.Error(
          'Error retrieving the mutating webhook configuration from the cluster'
          ': {}'.format(incluster_err))

    if incluster_out:
      return incluster_out
    return mcp_out

  def _RunKubectl(self, args, stdin=None):
    """Runs a kubectl command with the cluster referenced by this client.

    Args:
      args: command line arguments to pass to kubectl
      stdin: text to be passed to kubectl via stdin

    Returns:
      The contents of stdout if the return code is 0, stderr (or a fabricated
      error if stderr is empty) otherwise
    """
    cmd = [c_util.CheckKubectlInstalled()]
    if self.context:
      cmd.extend(['--context', self.context])

    if self.kubeconfig:
      cmd.extend(['--kubeconfig', self.kubeconfig])

    cmd.extend(['--request-timeout', self.kubectl_timeout])
    cmd.extend(args)
    out = io.StringIO()
    err = io.StringIO()
    returncode = execution_utils.Exec(
        cmd, no_exit=True, out_func=out.write, err_func=err.write, in_str=stdin
    )

    if returncode != 0 and not err.getvalue():
      err.write('kubectl exited with return code {}'.format(returncode))

    return out.getvalue() if returncode == 0 else None, err.getvalue(
    ) if returncode != 0 else None

def _GetNestedKeyFromManifest(manifest, *keys):
  """Get the value of a key path from a dict.

  Args:
    manifest: the dict representation of a manifest
    *keys: an ordered list of items in the nested key

  Returns:
    The value of the nested key in the manifest. None, if the nested key does
    not exist.
  """
  for key in keys:
    if not isinstance(manifest, dict):
      return None
    try:
      manifest = manifest[key]
    except KeyError:
      return None
  return manifest

class PermissionsError(exceptions.Error):
  """Class for errors raised when verifying permissions."""

class ClusterError(exceptions.Error):
  """Class for errors raised when verifying cluster setup."""

class WorkloadError(exceptions.Error):
  """Class for errors raised when verifying workload setup."""


# -*- coding: utf-8 -*-
"""
Created on Sun Apr  7 10:28:24 2019

@author: JungWoo
"""

#5.(For) Given two integers A and B (A ≤ B). Print all numbers from A to B inclusively.
#
#- Example input: 1 10
#- Example output: 1 2 3 4 5 6 7 8 9 10
#
x, y = input().split()
x, y = int(x), int(y)
for i in range(x, y+1):
    print(i, end=" ")


import sys
from socket import *
import threading

clients = []
def threadfunction(conn,addr,data):
    print("New User Joined")
    while True:
        UserName = conn.recv(1024)
        data = conn.recv(1024)
        if data:
            broadcasttoall(data,UserName,conn)
            print(UserName, data)

def broadcasttoall(data,UserName, conn):
    for client in clients:
        if client != conn:
            client.send((str(UserName) + str(data)).encode())
        else:
            client.send(("You: " + data).encode())

if __name__ == "__main__":
    serverPort = int(sys.argv[1])
    serverSocket = socket(AF_INET,SOCK_STREAM)
    serverSocket.bind(('',serverPort))
    serverSocket.listen(1)
    data=""
    while True:
        conn,addr = serverSocket.accept()
        clients.append(conn)
        print("New request recieved from", addr)
        t1 = threading.Thread(target = threadfunction, args = (conn, addr,data))
        t1.start()
    serverSocket.close()


def isPrime(a):
    i = 2
    while i<=a/2 :
        if a%i == 0 :
            return False
        i+=1
    return True

A = int(input("a = "))
if isPrime(A):
    print('소수')
else:
    print('소수 아님')

1-2 소수여부판별.py


list_int = []
ln = int(input("Enter length of the list: "))

for i in range(ln):
    list_int.append(int(input(f"Enter {i} element of the list: ")))

op = int(input("Enter : 1 - reverse, 2 - sort"))
if op == 1:
    print(list(reversed(list_int)))
if op == 2:
    print(sorted(list_int))


import socket
import time
import argparse

HOST = 'localhost'
PORT = 5555
HEADER_SIZE = 2

class Client:
    def __init__(self, host, port, name):
        self._host = host
        self._port = port
        self._name = name
        self._client_socket = None

    def connect(self):
        self._client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            self._client_socket.connect((self._host, self._port))
        except ConnectionRefusedError as e:
            print("Cannot connect to the server!")
            return
        print(f"Client is connected to: {(HOST, PORT)}")
        self._send_name()

    def close(self):
        self._client_socket.close()
        print("Client is closed!")

    def _send_name(self):
        self._send(self._create_msg(self._name))

    def _send(self, msg):
        totalsent = 0
        while totalsent < len(msg):
            sent = self._client_socket.send(msg[totalsent:])
            if sent == 0:
                raise RuntimeError("socket connection broken")
            totalsent = totalsent + sent

    def _send_all(self, msg):
        self._client_socket.sendall(msg)

    def _create_msg(self, msg):
        msg = bytearray(msg.encode())
        header = len(msg).to_bytes(HEADER_SIZE, byteorder='big')
        ba_header = bytearray(header)
        return ba_header + msg

def main():
    args = parse_args()
    client = Client(HOST, PORT, args.name)
    client.connect()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        client.close()

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("name")
    return parser.parse_args()

if __name__ == "__main__":
    main()


#Web scraping script that takes a user's input, googles it and prints descriptions of top results!, User is allowed multiple inputs respectively.

#importing presents, the chrome web driver, the Keys library to press keys, and the "By" library to select elements by class name.
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
chrome_options = Options()

chrome_path = r"C:\Users\abdal\Downloads\chromedriver.exe" #ensure to select proper directory of your chrome driver
chrome_options.headless = True
driver = webdriver.Chrome(options=chrome_options)

def searcher1(): #subroutine to allow for the user to repeat actions depending on later inputs
    driver.get("https://www.google.com")
    search = input("Search> ")
    searcher = driver.find_element("xpath","""/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/input""") #locate the search bar and define it under "searcher"
    searcher.send_keys(search) #input the user's input from 'search'
    searcher.send_keys(Keys.ENTER) #press enter
    posts = driver.find_elements(By.CLASS_NAME, "VwiC3b") #locate web descriptions using their class names
    for post in posts:
        print("\033[32m", post.text, "\n") #print each of them respectively, colored green and each seperated by a line

    while True:
        print("\033[0m")
        again = input("search again?(y/n)> ") #asking for repetition in a loop to ensure wrong inputs are given second chances with a try again input
        if again == "y" or again == "Y":
            searcher1() #goes back to the subroutine
        elif again == "n" or again == "N":
            print("\033[0m")
            break #breaks and exits the program
        else:
            print("\033[31m","Please try again!","\033[0m")
            continue # prints error message and requests a reinput

    exit()

searcher1()


import matplotlib.pyplot as plt
import csv
import numpy as np
import os
import re
import random

#defs

def getCleanPowerTen(crappyPower):
	valueLength = 1 + 3;
	if('E-' in crappyPower):
		key = re.sub(r'[0-9.]+', '', crappyPower.split('E')[0])
		print(key)
		value = crappyPower.split(key)[1].split('E')[0];
		if(len(value) > valueLength):
			value = value[0:valueLength]
		print(value)
		return key + value + 'E' + crappyPower.split('E')[1]
	else:
		return crappyPower;

def getListFromExpeDict(task, dict):
	theList = [];
	for timestep in dict:
		theList.append(dict[timestep][task]);

	return theList;

#dataPerBees[beeID][index]["HJ"]
#dataPerBees[beeID][index]["EO"]
def getTimeStepsLivedByBee(dict, beeIndex):
	convertInDays = True;
	theList = [];
	offset = -1;
	for timestep in dict[beeIndex]:
		v = timestep;
		if(convertInDays):
			v /= 60*60*24;

		if(offset == -1):
			offset = v;

		theList.append(v-offset);

	return theList;

def getTimeStepsListFromExpeDict(dict):
	convertInDays = True;
	theList = [];
	for timestep in dict:
		v = timestep;
		if(convertInDays):
			timestep /= 60*60*24
		theList.append(timestep);

	return theList;

def smoothExpe(smoothIntensity, dic, key):
	theKeys = list(dic.keys())
	for i in range(0, len(theKeys)):
		startSmooth = i - (int)(smooth/2);
		if(startSmooth < 0):
			startSmooth = 0;
		endSmooth = startSmooth+smooth
		if(endSmooth >= len(theKeys)):
			endSmooth = len(theKeys)-1;
		value = 0;
		#print("start " + str(startSmooth))
		#print("end " + str(endSmooth) + " " + str(len(theKeys)))
		for s in range(startSmooth, endSmooth):
			value += dic[theKeys[s]][key] / (endSmooth-startSmooth)
		dic[theKeys[i]][key] = value;

#PROGRAM

allBroodTag = "Brood"
nympheaTask = "NympheaTask"
eggTask = "EggTask"
larvaTask = "LarvaTask";
nurseTask = "FeedLarvae";
foragerTask = "Foraging";
giveFoodTask = "GiveFood";
askFoodTask = "AskingFood";
otherTask = "Other";
meanHJKey = "MeanHJ";
meanEOKey = "MeanEO"
keys = ["FeedLarvae","Foraging", "Other"];

folderName = "expeDivOK";
#folderName = "expe";
path = '../' + folderName + '/';

files = []
# r=root, d=directories, f = files
exclude = set(["bees", "beesTest"]);
for r, dirs, f in os.walk(path):
	dirs[:] = [d for d in dirs if d not in exclude]
	for file in f:
		if '.csv' in file:
			files.append(os.path.join(r, file))
fileIndex = 1

allParsedExpes = {}
allDataPerBees = {}

print("Found " + str(len(files)) + " files.")

linesEntry = input("ColumnNumber ");
rowsEntry = input("RowNumber ");
printBeesEntry = input("printBees ");
smoothEntry = input("Smooth Strength ");

if(linesEntry != ""):
	linesEntry = int(linesEntry)
else:
	linesEntry = 0

if(rowsEntry != ""):
	rowsEntry = int(rowsEntry)
else:
	rowsEntry = 0

if(smoothEntry != ""):
	smoothEntry = int(smoothEntry)
else:
	smoothEntry = 0

smooth = 0;
displayLINES = 3
displayROWS = 2
printManyBees = True;

if(linesEntry > 0 and linesEntry < 10):
	displayLINES = linesEntry

if(rowsEntry > 0 and rowsEntry < 10):
	displayROWS = rowsEntry

if(printBeesEntry == "False" or printBeesEntry == "false" or printBeesEntry == "0" or printBeesEntry == ""):
	printManyBees = False;

if(smoothEntry > 1):
	smooth = smoothEntry

#parsing
for f in files:
	print(str(fileIndex) + "/" + str(len(files)) + " " + f);
	fileIndex += 1
	fileName = f;
	fileTags = f.split(folderName+"/")[1]
	fileTags = fileTags.split(".csv")[0]
	fileTags = fileTags.split("_");

	#A_Classic_NewBorn_1000_0_518400_HJInc1.8518518518518519E-6
	#fileImportantName = fileTags[1] + " " + fileTags[2] + " " + fileTags[3] + " " + fileTags[6];

	fileImportantName = fileTags[0] + ": starter " + fileTags[2] + " - " + fileTags[3] + " Adults & " + fileTags[4] + " Larvae"# | " + fileTags[7]

	#fileImportantName = fileTags[0] + ": Acc" + fileTags[6]
	#fileImportantName = fileTags[0] + ": " + fileTags[7]
	#if(len(fileTags) >= 9):
	#	fileImportantName += " " + fileTags[8]

	parsedExpe = {}
	dataPerBees = {}

	barSize = 40
	barsCount = 0;

	print("|", end="");
	for i in range(barSize-2):
		print("-", end = "");
	print("|");

	size = 0
	with open(fileName) as csv_file:
		csv_reader = csv.reader(csv_file, delimiter=',')
		for row in csv_reader:
			size += 1;

	with open(fileName) as csv_file:
		csv_reader = csv.reader(csv_file, delimiter=',')
		line_count = 0;
		prevIndex = -1
		#print(f'\tturn {row[0]}, Bee ID {row[1]} was doing {row[2]} and had {row[3]} HJTiter ans row[4] EO.')
		for row in csv_reader:

			if(line_count * 1.0 / size > barsCount / barSize):
				print("|", end="", flush=True);
				barsCount+=1;

			if(line_count != 0 and len(row) > 4):
				index = int(row[0]);
				if(prevIndex != index):
					#push changes
					if(prevIndex != -1):
						parsedExpe[prevIndex][larvaTask] = larvaCount;
						parsedExpe[prevIndex][eggTask] = eggCount;
						parsedExpe[prevIndex][nympheaTask] = nympheaCount;
						parsedExpe[prevIndex][nurseTask] = nurseCount;
						parsedExpe[prevIndex][foragerTask] = foragerCount + otherCount;
						parsedExpe[prevIndex][giveFoodTask] = giveFoodCount;
						parsedExpe[prevIndex][askFoodTask] = askFoodCount;
						parsedExpe[prevIndex][otherTask] = otherCount + giveFoodCount + askFoodCount;
						parsedExpe[prevIndex][meanEOKey] = meanEO / (parsedExpe[prevIndex][otherTask] + foragerCount + nurseCount);
						parsedExpe[prevIndex][meanHJKey] = meanHJ / (parsedExpe[prevIndex][otherTask] + foragerCount + nurseCount);
						parsedExpe[prevIndex]["Total"] = nurseCount + foragerCount + parsedExpe[prevIndex][otherTask];
						parsedExpe[prevIndex][allBroodTag] = eggCount + nympheaCount + larvaCount;

						#parsedExpe[INDEX][BeeIndex] ["HJ"] / ["EO"]
					#newTimeStep, reset counters
					foragerCount = 0
					nurseCount = 0
					otherCount = 0
					larvaCount = 0
					nympheaCount = 0
					eggCount = 0
					giveFoodCount = 0
					askFoodCount = 0
					meanHJ = 0.0
					meanEO = 0.0
					parsedExpe[index] = {}

				prevIndex = index
				task = row[2];
				beeID = row[1];
				if(task == larvaTask):
					larvaCount += 1;
				elif (task == nympheaTask):
					nympheaCount += 1;
				elif (task == eggTask):
					eggCount += 1;
				else:
					meanHJ += float(row[3]);
					meanEO += float(row[4]);
					if(task == nurseTask):
						nurseCount +=1;
					elif(task == foragerTask):
						foragerCount +=1;
					elif(task == askFoodTask):
						askFoodCount += 1;
					elif(task == giveFoodTask):
						giveFoodCount += 1;
					else:
						otherCount += 1;

					if(beeID not in dataPerBees):
						dataPerBees[beeID] = {}
					dataPerBees[beeID][index] = {}
					dataPerBees[beeID][index]["HJ"] = float(row[3])
					dataPerBees[beeID][index]["EO"] = float(row[4])

			line_count+=1;

	if(prevIndex != -1):
		parsedExpe[prevIndex][larvaTask] = larvaCount;
		parsedExpe[prevIndex][eggTask] = eggCount;
		parsedExpe[prevIndex][nympheaTask] = nympheaCount;
		parsedExpe[prevIndex][nurseTask] = nurseCount;
		parsedExpe[prevIndex][foragerTask] = foragerCount;
		parsedExpe[prevIndex][giveFoodTask] = giveFoodCount;
		parsedExpe[prevIndex][askFoodTask] = askFoodCount;
		parsedExpe[prevIndex][otherTask] = otherCount + giveFoodCount + askFoodCount;
		parsedExpe[prevIndex][meanEOKey] = meanEO / (parsedExpe[prevIndex][otherTask] + foragerCount + nurseCount);
		parsedExpe[prevIndex][meanHJKey] = meanHJ / (parsedExpe[prevIndex][otherTask] + foragerCount + nurseCount);
		parsedExpe[prevIndex]["Total"] = nurseCount + foragerCount + parsedExpe[prevIndex][otherTask];
		parsedExpe[prevIndex][allBroodTag] = eggCount + nympheaCount + larvaCount;

	allDataPerBees[fileImportantName] = dataPerBees

	allParsedExpes[fileImportantName] = parsedExpe;

	print("")

	if(smooth > 1):
		#def smoothExpe(smoothIntensity, dic, key):
		smoothExpe(smooth, parsedExpe, foragerTask);
		smoothExpe(smooth, parsedExpe, otherTask);
		smoothExpe(smooth, parsedExpe, larvaTask);
		smoothExpe(smooth, parsedExpe, eggTask);
		smoothExpe(smooth, parsedExpe, nympheaTask);
		smoothExpe(smooth, parsedExpe, nurseTask);
		smoothExpe(smooth, parsedExpe, allBroodTag);
		smoothExpe(smooth, parsedExpe, "Total");

	#print(parsedExpe)
	#print("\n")

	#ploting that expe
	#plt.subplot(2,2, fileIndex-1, title=fileImportantName);
	#plt.plot(getTimeStepsListFromExpeDict(parsedExpe), getListFromExpeDict(nurseTask, parsedExpe), label=nurseTask);
	#plt.plot(getTimeStepsListFromExpeDict(parsedExpe), getListFromExpeDict(foragerTask, parsedExpe), label=foragerTask);
	#plt.plot(getTimeStepsListFromExpeDict(parsedExpe), getListFromExpeDict("Other", parsedExpe), label="Other");
	#plt.plot(getTimeStepsListFromExpeDict(parsedExpe), getListFromExpeDict(larvaTask, parsedExpe), label=larvaTask);

	#if(fileIndex-1 == 1):
		#plt.legend()

#print(allParsedExpes)
#plt.show()

figureNumber = 0

plt.figure(figureNumber, figsize=(25,15))
index = 1;
for key in allParsedExpes:
	expe = allParsedExpes[key];
	#         row, col
	plt.subplot(displayROWS,displayLINES, index, title=key);

	plt.axhline(y=0, color="k", linestyle = ":")
	plt.axhline(y=1000, color="k", linestyle = ":")

	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(nurseTask, expe), label=nurseTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(foragerTask, expe), label=foragerTask);
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(giveFoodTask, expe), label=giveFoodTask);
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(askFoodTask, expe), label=askFoodTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(otherTask, expe), label=otherTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(allBroodTag, expe), label=allBroodTag);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict("Total", expe), label="Total", linestyle=":");

	#print(getListFromExpeDict("Total", expe))
	#print("\n")

	if(index == 1):
		plt.legend()

	index += 1
#plt.show()
plt.savefig("Tasks.png");

figureNumber+=1
plt.figure(figureNumber, figsize=(25,15))
index = 1;
for key in allParsedExpes:
	expe = allParsedExpes[key];
	#         row, col
	plt.subplot(displayROWS,displayLINES, index, title=key);

	plt.axhline(y=0, color="k", linestyle = ":")
	#plt.axhline(y=1000, color="k", linestyle = ":")

	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(nurseTask, expe), label="Nourrices");
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(foragerTask, expe), label="Butineuses");
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(giveFoodTask, expe), label=giveFoodTask);
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(askFoodTask, expe), label=askFoodTask);
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(otherTask, expe), label=otherTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(allBroodTag, expe), label="Couvain");
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict("Total", expe), label="AdultesTotaux", linestyle="--");

	#print(getListFromExpeDict("Total", expe))
	#print("\n")

	if(index == 1):
		plt.legend()

	index += 1
#plt.show()
plt.savefig("TasksAPI.png");

vlines = [0,11,22,27,43,58]
titles = ["","",""]
titles[1] = "T0+22 : Tout le couvain a émergé, la reine commence à pondre\nT0+27 : Pic de mortalité chez les butineuses.\nT0+43 : Premières pontes de la nouvelle reine commencent à émerger."
titles[0] = "Retrouvez la période entre T0+11 et T0+20 : selon vous que se passe-t-il et pourquoi ?"
titles[2] = "Retrouvez la période entre T0+11 et T0+20, ainsi que les jours autour de T0+58 :\nselon vous que se passe-t-il et pourquoi ?"

index = 0
for key in allParsedExpes:
	expe = allParsedExpes[key];
	figureNumber+=1
	plt.figure(figureNumber, figsize=(12,7))
	#plt.suptitle(titles[index])

	plt.axhline(y=0, color="k", linestyle = ":")

	for x in vlines:
		plt.vlines(x,0,1100, linestyle = ":")
		plt.text(x-2, -40, "T0+" + str(x))

	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(nurseTask, expe), label="Nourrices");
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(foragerTask, expe), label="Butineuses");
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(giveFoodTask, expe), label=giveFoodTask);
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(askFoodTask, expe), label=askFoodTask);
	#plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(otherTask, expe), label=otherTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(allBroodTag, expe), label="Couvain");
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict("Total", expe), label="AdultesTotaux", linestyle="--");

	plt.xlabel("Jours")
	plt.ylabel("Populations")

	#print(getListFromExpeDict("Total", expe))
	#print("\n")

	plt.legend()

	plt.savefig("TasksAPI_" + key[0] + ".png", bbox_inches='tight');
	plt.show()

	index += 1

index = 1;
figureNumber += 1
plt.figure(figureNumber, figsize=(25,15))
for key in allParsedExpes:
	expe = allParsedExpes[key];

	plt.subplot(displayROWS,displayLINES, index, title=key);

	plt.axhline(y=0, color="k", linestyle = ":")

	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(meanHJKey, expe), label=meanHJKey);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(meanEOKey, expe), label=meanEOKey);

	if(index == 1):
		plt.legend()
	index += 1

#plt.show()
plt.savefig("HJ.png");

figureNumber += 1
plt.figure(figureNumber, figsize=(25,15))
index = 1;
for key in allParsedExpes:
	expe = allParsedExpes[key];
	#         row, col
	plt.subplot(displayROWS,displayLINES, index, title=key);

	plt.axhline(y=0, color="k", linestyle = ":")

	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(eggTask, expe), label=eggTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(larvaTask, expe), label=larvaTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(nympheaTask, expe), label=nympheaTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(allBroodTag, expe), label=allBroodTag, linestyle=":");

	#print(getListFromExpeDict("Total", expe))
	#print("\n")

	if(index == 1):
		plt.legend()

	index += 1
#plt.show()
plt.savefig("Brood.png");

if printManyBees:

	print("\nprintingBees:");

	findex = 1;
	for key in allParsedExpes:

		print(key);

		figureNumber+=1
		plt.figure(figureNumber, figsize=(25,15))

		expe = allParsedExpes[key];
		#get list of random beesIndex
		beesIndexes = random.sample(list(allDataPerBees[key].keys()), 40);

		index = 1
		for beeIndex in beesIndexes:

			ax = plt.subplot(4,10, index, title=key[0] + " Bee" + beeIndex);
			plt.plot(getTimeStepsLivedByBee(allDataPerBees[key], beeIndex), getListFromExpeDict("EO", allDataPerBees[key][beeIndex]), label="EO");
			plt.plot(getTimeStepsLivedByBee(allDataPerBees[key], beeIndex), getListFromExpeDict("HJ", allDataPerBees[key][beeIndex]), label="HJ");

			ax.set_ylim(ymin=0, ymax=2)

			if(index == 1):
				plt.legend()
			index += 1

		findex += 1
		plt.savefig("perBees" + key[0] + ".png");

'''
index = 1;
plt.figure(2, figsize=(25,15))
for key in allParsedExpes:
	expe = allParsedExpes[key];

	plt.subplot(4,1, index, title=key);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(askFoodTask, expe), label=askFoodTask);
	plt.plot(getTimeStepsListFromExpeDict(expe), getListFromExpeDict(giveFoodTask, expe), label=giveFoodTask);

	if(index == 1):
		plt.legend()

	index += 1

plt.savefig("Food.png");

'''


'''
https://uva.onlinejudge.org/index.php?option=onlinejudge&page=show_problem&problem=734
DONE
'''

class PathCompression:
    def __init__(self, n):
        self.data = [int(x) for x in range(0, n)]
        self.weight = [1] * n
        self.components = n

    # path compression
    def find(self, p):
        while p != self.data[p]:
            self.data[p] = self.data[self.data[p]]
            p = self.data[p]
        return p

    def connected(self, p, q):
        return self.find(p) == self.find(q)

    def union(self, p, q):
        rootP = self.find(p)
        rootQ = self.find(q)
        if rootP == rootQ:
            return
        elif self.weight[rootP] < self.weight[rootQ]:
            self.data[rootP] = rootQ
            self.weight[rootQ] += self.weight[rootP]
        else:
            self.data[rootQ] = rootP
            self.weight[rootP] += self.weight[rootQ]
        self.components -= 1

def run():
    cases = int(input().strip())
    input()
    for i in range(cases):
        s, f = 0, 0
        enters = int(input().strip())
        path_c = PathCompression(enters + 1)
        while True:
            try:
                c, p, q = list(input().strip().split(" "))
            except Exception:
                break
            if c == 'c':
                path_c.union(int(p), int(q))
            else:
                if path_c.connected(int(p), int(q)):
                    s += 1
                else:
                    f += 1

        if i > 0:
            print()
        print("%d,%d" % (s, f))
        cases -= 1

if __name__ == '__main__':
    run()


class Node:
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next

class LinkedList:
    def __init__(self):
        self.head = None

    def insert_at_begining(self, data):
        node = Node(data, self.head)
        self.head = node

    def print(self):
        if self.head is None:
            print("Linked List is empty")

        itr = self.head
        llstr = ""
        while itr:
            llstr += str(itr.data) + "-->"
            itr = itr.next
        print(llstr)

    def insert_at_end(self, data):
        if self.head is None:
            self.head = Node(data, None)

        itr = self.head
        while itr.next:
            itr = itr.next

        itr.next = Node(data, None)

    def insert_list_values(self, datas):
        for data in datas:
            self.insert_at_end(data)

    def length(self):
        count = 0
        itr = self.head
        while itr:
            itr = itr.next
            count+=1
        return count

    def remove_at_index(self, index):
        if index == 0:
            self.head = self.head.next

        if index<0 or index>self.length():
            raise Exception("Invalid Index")

        count = 0
        itr = self.head
        while itr:
            if count == index - 1:
                itr.next = itr.next.next
                break
            count+=1
            itr = itr.next

    def insert_at_index(self, index, data):
        if index < 0 or index >= self.length():
            raise Exception("Invalid Index")

        if index == 0:
            self.insert_at_begining(data)

        count = 0
        itr = self.head
        while itr:
            if count == index - 1:
                itr.next = Node(data, itr.next)
                break

            count+=1
            itr = itr.next

    def remove_the_value(self, data):
        itr = self.head

        if itr is None:
            raise Exception("Remove Operation on Empty Linked List is not Valid")

        while itr:
            if itr.data == data:
                itr.data = itr.next.data
                itr.next = itr.next.next
                break
            itr = itr.next

if __name__ == '__main__':

    ll = LinkedList()
    ll.insert_at_begining(5)
    ll.insert_at_begining(4)
    ll.insert_at_end(6)
    ll.insert_at_end(7)
    ll.insert_list_values([8,9])

    ll.insert_at_index(3,7)
    ll.remove_at_index(3)
    ll.remove_the_value(7)
    ll.remove_the_value(5)

    print(ll.length())
    ll.print()


## Author: Ankita Pasad
# Created on: 22.09.2016
# Implementation of zero frequency filtering of speech signal for epoch detection
# Reference: http://speech.iiit.ac.in/svlpubs/article/MurtyK.S.R.Yegna2008.pdf

import numpy as np
import scipy.signal as sigpy
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import math
import scipy.io.wavfile as wav
import sys
import pandas.tools.plotting
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt
from mic_test import mic_remov

def findZffs(signal,f0):

    numerator = np.array([1,-1])
    denominator = np.array([1,-3,3,-1])
    signal = signal/np.float(np.max(signal))
# filtering the speech signal
    zffsSigRaw = sigpy.lfilter(numerator,denominator,signal)
    # zffsSigRaw = zffsSigRaw/np.float(np.max(zffsSigRaw)) # normalizing
# removing the trend
    # 2N+1 = number of samples in one pitch period
    N = int(math.ceil(((fs/f0)-1)/2))
    noSamples = int((2*N)+1)

    # appending zeros for mean computation
    before = np.zeros(N)
    after = np.zeros(N)
    zffsSig_appended = np.concatenate([before,zffsSigRaw])
    zffsSig_appended = np.concatenate([zffsSig_appended,after])
    size = len(zffsSig_appended)

    # array to be subtracted from the original array
    # moving mean, considering the window of size (2N+1) around each sample
    ma_zffsSig = pd.rolling_mean(zffsSig_appended,noSamples,center=True)
    # extracting out the relevant part
    ma_zffsSig = ma_zffsSig[N:size-N]
    # subtracting the mean
    zffsSig = zffsSigRaw - ma_zffsSig
    length = len(zffsSig)
    zffsSig = zffsSig[0:length-N]
    # taking signal to be of same length
    signal = signal[0:length-N]
    # zffsSig = zffsSig/np.float(np.max(zffsSig))

    return zffsSig,signal,zffsSigRaw[0:length-N]

def findExcitation(zffs):

    # Excitation: defined as slope at positive zero crossings for zff signal
    # finding positive zero crossings
    indices = np.where(np.diff(np.sign(zffs))>0)
    # converting to 1-dim array
    indices = indices[0]
    excitation = np.zeros(len(zffs))
    for i in range (0,len(indices)):
        # approximating the slope at positive zero crossings
        excitation[indices[i]]=(zffs[indices[i]+1]-zffs[indices[i]])

    return excitation

def findCorrCoef(corr):
    peakLoc = np.where(np.diff(np.sign(np.diff(corr)))<0)[0] # positions of peak locations
    if(len(peakLoc)==0):
        corrCoef=0
    else:
        peakLoc = peakLoc + np.ones(len(peakLoc))
        peakAmplitude = np.zeros(len(peakLoc))
        for i in range(0,len(peakLoc)):
            peakAmplitude[i] = corr[peakLoc[i]]
        peakAmplitudeSort = np.sort(peakAmplitude)
        # peakAmplitudeSortArg =np.argsort(peakAmplitude)
        corrCoef = peakAmplitudeSort[-1]

    return corrCoef

def findFeatures(zffsSig,winSize,fs): # When the whole signal is provided
    winLen = winSize*fs # winSize in seconds
    # noWindows = math.floor((len(zffsSig)/(winLen/2)) - 1)
    noWindows = int((len(zffsSig)/np.ceil(float(winLen)/2)) - 1)
    # noWindows = int(noWindows)
    excitation  = findExcitation(zffsSig)
    zffsSig = zffsSig/30000.0
    corrCoef = np.zeros(noWindows)
    for i in range (0,noWindows):
#        print i
        windowedSig = zffsSig[i*winLen/2: int(i*winLen/2 + winLen)]
#        print len(windowedSig)
        corr = np.correlate(windowedSig,windowedSig,mode='same')
#        print len(corr)
        corr = corr/np.float(np.max(corr))
        if(len(np.where(corr==1)[0])!=0):
            index = np.where(corr==1)[0][0]
            index = index+1
            corrCoef[i] = findCorrCoef(corr[0:index])

    return zffsSig, excitation, corrCoef

def lpfilter(sig,order):
    # order: N
    # winSize = 2N + 1
    winSize = 2*order+1
    before = np.zeros(order)
    after = np.zeros(order)
    sig_appended = np.concatenate([before,sig])
    sig_appended = np.concatenate([sig_appended,after])
    size = len(sig_appended)
    ma_sig = pd.rolling_mean(sig_appended,winSize,center=True)
    # extracting out the relevant part
    ma_sig = ma_sig[order:size-order]

    return ma_sig

if __name__ == '__main__':

    noise1 = sys.argv[1]
    sigName2 = noise1
#    noise1 = 'test3'
    # txtName = "data/zffs/"+noise1+"_zffs.txt"
    txtName = "/home/swar/swar/shreeharsha/VAD/Ankita_VAD_results/zffs/" + noise1.split('s/')[1] + "_zffs.txt"
    # sigName2 = "data/"+noise1+".wav"
    zffcoeffName="/home/swar/swar/shreeharsha/VAD/Ankita_VAD_results/zffs/"+noise1.split('s/')[1]+"_zffcoeff.txt"
    [fs, signal1] = wav.read(sigName2)  # read wav file
    signal1 = signal1/(0.0+np.max(np.abs(signal1)))
    signal1 = mic_remov(signal1,fs)*signal1
    #signal1 = signal1/30000.0 # normalizing in order to deal with over-flow issue
    [zffsSig1,signal1,zffsSigRaw1] = findZffs(signal1,300.0)
    # index = np.where(np.max(np.abs(signal1))/np.abs(signal1)>=10**1.5)
    # signal1[index] = 0
    winsize=0.02
    [zffsSig1,excitation1,corrCoef1] = findFeatures(zffsSig1,winsize,fs)
#    corrCoeff = corrCoef1[0::2]
#    corrCoeff = np.repeat(corrCoeff,nrep)
    np.savetxt(zffcoeffName,corrCoef1,fmt='%5.4f')
#
    thresh = np.max(corrCoef1)*0.75
### Dynamic threshold
#    # Emax = np.float(np.max(corrCoef1))
#    # Emin = np.float(np.min(corrCoef1))
#    # I1 = 0.03*(Emax - Emin) + Emin
#    # I2 = 4*Emin
#    # Thl = np.min([I1,I2])
#    # Thu = 2*Thl
#    # Thl = 2*I1
#    # thresh = Thl
#    # print thresh
#
    indices = np.where(corrCoef1<=thresh)[0]
    # corrCoef1[indices] = np.zeros(len(indices))
    decision = np.ones(len(corrCoef1))
    decision[indices] = np.zeros(len(indices))
    zffsSig1 = np.abs(zffsSig1)/np.max(zffsSig1)
#
    # converting 50 ms window to 10 ms window
    decision = decision[0::2] # 25 to 50
    nrep = 5
    decision = np.repeat(decision,nrep) # 50 to 10
    np.savetxt(txtName,decision,fmt='%.0f')
#
#
#
#      # for plotting
#    nrep = np.int(fs*0.025)
#    index1 = len(corrCoef1)*nrep
#    xaxis = np.linspace(0,index1-1,index1)/16000.0
##    decision = np.repeat(decision,nrep)
#
## Plotting the speech signal and zero frequency filtered signal
#    # length = len(signal) # xaxis in seconds
#    # xaxis = np.arange(0,np.float(length/float(fs)),(1/float(fs)))
#
#    plt.figure()
#    plt.subplot(2,1,1)
#    plt.plot(xaxis, signal1[0:index1],xaxis,(np.repeat(corrCoef1,nrep)))
#    plt.title('Audio signal '+noise1)
##    plt.subplot(2,1,2)
##    plt.plot(xaxis,np.repeat(corrCoef1,nrep))
##    plt.title('Normalized Correlation Coefficient',fontsize=11)
#    # # plt.subplot(3,1,3)
#    # # plt.plot(decision)
#    # # plt.ylim([-0.5,1.5])
#    # # # plt.title('Correlation Coefficient',fontsize=11)
#    # plt.xlabel('Number of samples')
##############################plots for dual y-axis######################################
##    A = signal1[0:index1]
##    B = (np.repeat(corrCoef1,nrep))
##
##    host = host_subplot(111, axes_class=AA.Axes)
##    # plt.subplots_adjust(right=0.75)
##
##    par1 = host.twinx()
##    # par2 = host.twinx()
##
##    # offset = 60
##    # new_fixed_axis = par2.get_grid_helper().new_fixed_axis
##    # par2.axis["right"] = new_fixed_axis(loc="right",axes=par2,offset=(offset, 0))
##
##    # par2.axis["right"].toggle(all=True)
##
##    # host.set_xlim(0, 2)
##    host.set_ylim(-1.5,3)
##
##    host.set_xlabel("Duration (seconds)",fontsize=14)
##    host.set_ylabel("Normalized Audio Signal",fontsize=14)
##    par1.set_ylabel("Normalized Correlation Coefficient",fontsize=14)
##    # par2.set_ylabel("Velocity")
##
##    p1, = host.plot(xaxis, A, label="Audio Signal")
##    p2, = par1.plot(xaxis, B, label="Correlation Coefficient")
##    # p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
##
##    par1.set_ylim(-2.2,1.8)
##    # par2.set_ylim(1, 65)
##
##    host.legend()
##
##    host.axis["left"].label.set_color(p1.get_color())
##    par1.axis["right"].label.set_color(p2.get_color())
##    # par2.axis["right"].label.set_color(p3.get_color())
##
##    plt.title("Normalized Correlation Coefficient of ZFF signal")
##    plt.draw()
##    plt.show()
##############################plots for dual y-axis######################################
#    # C = np.zeros([index1,2])
#    # C[:,0] = A
#    # C[:,1] = B
#    # df = pd.DataFrame(C, index=xaxis, columns=list('AB'))
#    # df.A.plot(label="Audio Signal", legend=True, ylim=(-1.5,3))
#    # df.B.plot(secondary_y=True, label="Correlation Coefficient", legend=True)
#
#    # plt.figure()
#    # plt.plot(xaxis, (signal1[0:index1]-1), label = "Audio Signal")
#    # plt.plot(xaxis,(np.repeat(corrCoef1,nrep)+1),label = "Correlation Coefficient")
#    # plt.legend(loc="best")
#    # plt.title("Correlation Coefficient of ZFF signal")
#    # plt.xlabel('Sample Number')
#    # plt.ylabel('')
#
#    # plt.show()


with open('./inputs/18.txt', 'r') as infile:
    first_row = infile.read()

def count_safes(row, total_lines):
    safe = 0
    for i in range(total_lines):
        if i == 40:
            first_solution = safe
        safe += row.count('.')
        old = '.' + row + '.'
        row = ''
        for left, right in zip(old, old[2:]):
            row += '^' if left != right else '.'
    return first_solution, safe

first, second = count_safes(first_row, 400000)

print("Oh, so many traps here! Let me make 40 steps and count safe tiles.")
print(f"There are about {first} tiles here.")
print("....")
print("This room is 400,000 steps long?! What a large room!")
print(f"And there are {second} safe tiles in total.")