from collections import deque
from threading import Thread

class Sequencer:
    def __init__(self, name):
        self.name = name
        self.file = open(name, "wb")
        self.queue = deque()
        self.running = False
        self.byte_sequence = []
        self.thread = Thread(target=self._writer_thread)

    def add(self, byte):
        index = byte['index']
        self.queue.append(byte)
        # self.byte_sequence.append(byte)
        # self.arrange()
        pass

    def close(self):
        """
        Wait for the writer thread to finish and close the open file.
        """
        self.running = False
        self.thread.join()
        self.file.close()

    def _writer_thread(self):
        """
        Runs until a stop is requested and the queue is exhausted.
        """
        self.running = True
        while self.running or len(self.queue):
            if len(self.queue):
                byte = self.queue.popleft()
                chunk = byte['index']
                data = byte['data']
                chunk_size = len(data)
                self.file.seek(chunk*chunk_size)
                self.file.write(data)

    def arrange(self):
        self.byte_sequence.sort(key=lambda x: x['index'])
        i = 0
        while i != len(self.byte_sequence):
            current = self.byte_sequence[i]
            previous = self.byte_sequence[i - 1]
            current_sequence = current.get('index')
            previous_sequence = previous.get('last', previous.get('index'))
            if (current_sequence - 1) == previous_sequence and (current_sequence - 1) > -1:
                current = self.byte_sequence.pop(i)
                previous['data'] = previous['data'] + current.get('data', b'')
                last_sequence = current.get('last', current.get('index'))
                previous['last'] = last_sequence
                continue
            i += 1
        print(self.byte_sequence)

if __name__ == '__main__':
    obj = Sequencer('test')
    obj.add({'index': 0, 'data': b'0'})
    obj.add({'index': 1, 'data': b'1'})
    obj.add({'index': 2, 'data': b'2'})
    obj.add({'index': 3, 'data': b'3'})
    obj.add({'index': 4, 'data': b'4'})
    obj.add({'index': 5, 'data': b'5'})
    obj.add({'index': 6, 'data': b'6'})
    obj.add({'index': 7, 'data': b'7'})
    obj.add({'index': 8, 'data': b'8'})
    obj.add({'index': 9, 'data': b'9'})
    obj.add({'index': 10, 'data': b'10'})
    obj.add({'index': 11, 'data': b'11'})


import rdiffweb.test
from rdiffweb.core.model import RepoObject, UserObject

class SettingsTest(rdiffweb.test.WebCase):
    login = True

    def test_page(self):
        self.getPage("/settings/" + self.USERNAME + "/" + self.REPO)
        self.assertInBody("General Settings")
        self.assertStatus(200)

    def test_page_encoding_none(self):
        # Given a repo where encoding is not defined.
        RepoObject.query.update({RepoObject.encoding: None})
        repo = RepoObject.query.first()
        repo.commit()
        # When browsing settings pages
        self.getPage("/settings/" + self.USERNAME + "/" + self.REPO)
        # Then not error is report.
        self.assertStatus(200)

    def test_as_another_user(self):
        # Create a nother user with admin right
        user_obj = UserObject.add_user('anotheruser', 'password')
        user_obj.user_root = self.testcases
        user_obj.refresh_repos()
        user_obj.commit()
        self.getPage("/settings/anotheruser/testcases")
        self.assertInBody("General Settings")
        self.assertStatus('200 OK')

        # Remove admin right
        admin = UserObject.get_user('admin')
        admin.role = UserObject.USER_ROLE
        admin.commit()

        # Browse admin's repos
        self.getPage("/settings/anotheruser/testcases")
        self.assertStatus('403 Forbidden')

    def test_set_maxage(self):
        # When updating maxage
        self.getPage("/settings/" + self.USERNAME + "/" + self.REPO + "/", method="POST", body={'maxage': '4'})
        self.assertStatus(303)
        # Then a succes message is displayed to user
        self.getPage("/settings/" + self.USERNAME + "/" + self.REPO + "/")
        self.assertStatus(200)
        self.assertInBody("Settings modified successfully.")
        # Then repo get updated
        repo_obj = RepoObject.query.filter(RepoObject.repopath == self.REPO).first()
        self.assertEqual(4, repo_obj.maxage)

    def test_set_maxage_method_get(self):
        # When trying to update maxage with GET method
        self.getPage("/settings/" + self.USERNAME + "/" + self.REPO + "/?maxage=4")
        # Then page return without error
        self.assertStatus(200)
        # Then database is not updated
        repo_obj = RepoObject.query.filter(RepoObject.repopath == self.REPO).first()
        self.assertEqual(0, repo_obj.maxage)

    def test_does_not_exists(self):
        # Given an invalid repo
        repo = 'invalid'
        # When trying to get settings from it
        self.getPage("/settings/" + self.USERNAME + "/" + repo)
        # Then a 404 error is return
        self.assertStatus(404)

    def test_browser_with_failed_repo(self):
        # Given a failed repo
        admin = UserObject.get_user('admin')
        admin.user_root = '/invalid/'
        admin.commit()
        # When querying the logs
        self.getPage("/settings/" + self.USERNAME + "/" + self.REPO)
        # Then the page is return with an error message
        self.assertStatus(200)
        self.assertInBody('The repository cannot be found or is badly damaged.')


import pandas as pd
import geopandas as gpd
import osmnx as ox
from h3 import h3
from rich.progress import track
from urbanpy.utils import geo_boundary_to_polygon
from typing import Sequence, Union

__all__ = [
    "merge_geom_downloads",
    "filter_population",
    "remove_features",
    "gen_hexagons",
    "merge_shape_hex",
    "overlay_polygons_hexs",
    "resolution_downsampling",
    "osmnx_coefficient_computation",
]

def merge_geom_downloads(
    gdfs: Sequence[gpd.GeoDataFrame], crs: str = "EPSG:4326"
) -> gpd.GeoDataFrame:
    """
    Merge several GeoDataFrames from OSM download_osm

    Parameters
    ----------
    dfs: array_like
        Array of GeoDataFrames to merge. Assumes equal CRS.
    crs: str
        Valid string to pass to crs param of the geopandas.GeoDataFrame constructor function.

    Returns
    -------

    concat: GeoDataFrame
        Output from concatenation and unary union of geometries, providing a single geometry database for the city

    Examples
    --------
    >>> lima = urbanpy.download.nominatim_osm("Lima, Peru", 2)
    >>> callao = urbanpy.download.nominatim_osm("Callao, Peru", 1)
    >>> lima = urbanpy.geom.merge_geom_downloads([lima, callao])
    >>> lima.head()
    geometry
    MULTIPOLYGON (((-76.80277 -12.47562, -76.80261...)))
    """
    concat = gpd.GeoDataFrame(geometry=[pd.concat(gdfs).unary_union], crs=crs)
    return concat

def filter_population(
    pop_df: pd.DataFrame, polygon_gdf: gpd.GeoDataFrame
) -> gpd.GeoDataFrame:
    """
    Filter an HDX database download to the polygon bounds

    Parameters
    ----------
    pop_df: DataFrame
        Result from download_hdx

    polygon_gdf: GeoDataFrame
        Result from download_osm or merge_geom_downloads

    Returns
    -------
    filtered_points_gdf: GeoDataFrame
        Population DataFrame filtered to polygon bounds

    Examples
    --------
    >>> lima = urbanpy.download.nominatim_osm("Lima, Peru", 2)
    >>> callao = urbanpy.download.nominatim_osm("Callao, Peru", 1)
    >>> lima = urbanpy.geom.merge_geom_downloads([lima, callao])
    >>> pop = urbanpy.download.hdx_fb_population('peru', 'full')
    >>> urbanpy.geom.filter_population(pop, lima)
    latitude   | longitude  | population_2015 | population_2020 | geometry
    -12.519861 | -76.774583 | 2.633668        | 2.644757        | POINT (-76.77458 -12.51986)
    -12.519861 | -76.745972 | 2.633668        | 2.644757        | POINT (-76.74597 -12.51986)
    -12.519861 | -76.745694 | 2.633668        | 2.644757        | POINT (-76.74569 -12.51986)
    -12.519861 | -76.742639 | 2.633668        | 2.644757        | POINT (-76.74264 -12.51986)
    -12.519861 | -76.741250 | 2.633668        | 2.644757        | POINT (-76.74125 -12.51986)
    """
    minx, miny, maxx, maxy = polygon_gdf.geometry.total_bounds
    limits_filter = pop_df["longitude"].between(minx, maxx) & pop_df[
        "latitude"
    ].between(miny, maxy)
    filtered_points = pop_df[limits_filter]

    geometry_ = gpd.points_from_xy(
        filtered_points["longitude"], filtered_points["latitude"]
    )
    filtered_points_gdf = gpd.GeoDataFrame(
        filtered_points, geometry=geometry_, crs="EPSG:4326"
    )

    return filtered_points_gdf

def remove_features(gdf: gpd.GeoDataFrame, bounds: Sequence[float]) -> gpd.GeoDataFrame:
    """
    Remove a set of features based on bounds

    Parameters
    ----------
    gdf: GeoDataFrame
        Input GeoDataFrame containing the point features filtered with filter_population

    bounds: array_like
        Array input following [minx, miny, maxx, maxy] for filtering (GeoPandas total_bounds method output)

    Returns
    -------
    gdf: GeoDataFrame
        Input DataFrame but without the desired features

    Examples
    --------
    >>> lima = urbanpy.geom.filter_population(pop_lima, poly_lima)
    >>> removed = urbanpy.geom.remove_features(lima, [-12.2,-12, -77.2,-77.17]) #Remove San Lorenzo Island
    >>> print(lima.shape, removed.shape)
    (348434, 4) (348427, 4)

    """
    minx, miny, maxx, maxy = bounds
    drop_ix = gdf.cx[minx:maxx, miny:maxy].index

    return gdf.drop(drop_ix)

def gen_hexagons(resolution: int, city: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
    """
    Converts an input multipolygon layer to H3 hexagons given a resolution.

    Parameters
    ----------
    resolution: int, 0:15
        Hexagon resolution, higher values create smaller hexagons.

    city: GeoDataFrame
        Input city polygons to transform into hexagons.

    Returns
    -------
    city_hexagons: GeoDataFrame
        Hexagon geometry GeoDataFrame (hex_id, geom).

    Examples
    --------
    >>> lima = urbanpy.geom.filter_population(pop_lima, poly_lima)
    >>> lima_hex = urbanpy.geom.gen_hexagons(8, lima)
    hex	            | geometry
    888e620e41fffff | POLYGON ((-76.80007 -12.46917, -76.80439 -12.4...))
    888e62c809fffff | POLYGON ((-77.22539 -12.08663, -77.22971 -12.0...))
    888e62c851fffff | POLYGON ((-77.20708 -12.08484, -77.21140 -12.0...))
    888e62c841fffff | POLYGON ((-77.22689 -12.07104, -77.23122 -12.0...))
    888e62c847fffff | POLYGON ((-77.23072 -12.07929, -77.23504 -12.0...))
    """
    # Polyfill the city boundaries
    h3_polygons = list()
    h3_indexes = list()

    # Get every polygon in Multipolygon shape
    city_poly = city.explode(index_parts=True).reset_index(drop=True)

    for _, geo in city_poly.iterrows():
        hexagons = h3.polyfill(
            geo["geometry"].__geo_interface__, res=resolution, geo_json_conformant=True
        )
        for hexagon in hexagons:
            h3_polygons.append(geo_boundary_to_polygon(hexagon))
            h3_indexes.append(hexagon)

    # Create hexagon dataframe
    city_hexagons = gpd.GeoDataFrame(h3_indexes, geometry=h3_polygons).drop_duplicates()
    city_hexagons.crs = "EPSG:4326"
    city_hexagons = city_hexagons.rename(
        columns={0: "hex"}
    )  # Format column name for readability

    return city_hexagons

def merge_shape_hex(
    hexs: gpd.GeoDataFrame,
    shape: gpd.GeoDataFrame,
    agg: dict,
    how="inner",
    predicate="intersects",
) -> gpd.GeoDataFrame:
    """
    Merges a H3 hexagon GeoDataFrame with a Point GeoDataFrame and aggregates the
    point gdf data.

    Parameters
    ----------
    hexs: GeoDataFrame
        Input GeoDataFrame containing hexagon geometries

    shape: GeoDataFrame
        Input GeoDataFrame containing points and features to be aggregated

    agg: dict
        A dictionary with column names as keys and values as aggregation
        operations. The aggregation must be one of {'sum', 'min', 'max'}.

    how: str. One of {'inner', 'left', 'right'}. Default 'inner'.
        Determines how to merge data:
            'left' uses keys from left and only retains geometry from left
            'right' uses keys from right and only retains geometry from right
            'inner': use intersection of keys from both dfs; retain only left geometry column

    op: str. One of {'intersects', 'contains', 'within'}. Default 'intersects'
        Determines how geometries are queried for merging.

    Returns
    -------
    hexs: GeoDataFrame
        Result of a spatial join within hex and points. All features are aggregated
        based on the input parameters

    Examples
    --------
    >>> lima = urbanpy.download.nominatim_osm('Lima, Peru', 2)
    >>> pop_lima = urbanpy.download.hdx_fb_population('peru', 'full')
    >>> pop_df = urbanpy.filter_population(pop_lima, lima)
    >>> hexs = urbanpy.geom.gen_hexagons(8, lima)
    >>> urbanpy.geom.merge_point_hex(hexs, pop_df, 'inner', 'within', {'population_2020':'sum'})
    0               | geometry                                          | population_2020
    888e628d8bfffff | POLYGON ((-76.66002 -12.20371, -76.66433 -12.2... | NaN
    888e62c5ddfffff | POLYGON ((-76.94564 -12.16138, -76.94996 -12.1... | 14528.039097
    888e62132bfffff | POLYGON ((-76.84736 -12.17523, -76.85167 -12.1... | 608.312696
    888e628debfffff | POLYGON ((-76.67982 -12.18998, -76.68413 -12.1... | NaN
    888e6299b3fffff | POLYGON ((-76.78876 -11.97286, -76.79307 -11.9... | 3225.658803
    """
    joined = gpd.sjoin(shape, hexs, how=how, predicate=predicate)

    # Uses index right based on the order of points and hex. Right takes hex index
    hex_merge = joined.groupby("index_right").agg(agg)

    # Avoid SpecificationError by copying the DataFrame
    ret_hex = hexs.copy()

    for key in agg:
        ret_hex.loc[hex_merge.index, key] = hex_merge[key].values

    return ret_hex

def overlay_polygons_hexs(
    polygons: gpd.GeoDataFrame,
    hexs: gpd.GeoDataFrame,
    hex_col: str,
    columns: Sequence[str],
) -> gpd.GeoDataFrame:
    """
    Overlays a Polygon GeoDataFrame with a H3 hexagon GeoDataFrame and divide the 'columns' the values proportionally to the overlayed area.

    Parameters
    ----------
    polygons: GeoDataFrame
                Input GeoDataFrame containing polygons and columns to be processed

    hexs: GeoDataFrame
             Input GeoDataFrame containing desired output hexagon resolution geometries

    hex_col: str
            Determines the column with the hex id.

    columns: list
             A list with column names of the columns that are going to be proportionally adjusted

    Returns
    -------

    hexs: GeoDataFrame
           Result of a spatial join within hex and points. All columns are adjusted
           based on the overlayed area.

    Examples
    --------

    >>> urbanpy.geom.overlay_polygons_hexs(zonas_pob, hex_lima, 'hex', pob_vulnerable)
                hex |  POB_TOTAL |  geometry
    898e6200493ffff | 193.705376 |  POLYGON ((-76.80695 -12.35199, -76.80812 -12.3...
    898e6200497ffff | 175.749780 |  POLYGON ((-76.80412 -12.35395, -76.80528 -12.3...
    898e620049bffff |  32.231078 |  POLYGON ((-76.81011 -12.35342, -76.81127 -12.3...
    898e62004a7ffff |  74.154973 |  POLYGON ((-76.79911 -12.36468, -76.80027 -12.3...
    898e62004b7ffff |  46.989828 |  POLYGON ((-76.79879 -12.36128, -76.79995 -12.3...

    """
    polygons_ = polygons.copy()  # Preserve data state
    polygons_["poly_area"] = polygons_.geometry.area  # Calc polygon area

    # Overlay intersection
    overlayed = gpd.overlay(polygons_, hexs, how="intersection")

    # Downsample indicators using proporional overlayed area w.r.t polygon area
    area_prop = overlayed.geometry.area / overlayed["poly_area"]
    overlayed[columns] = overlayed[columns].apply(lambda col: col * area_prop)

    # Aggregate over Hex ID
    per_hexagon_data = overlayed.groupby(hex_col)[columns].sum()

    # Preserve data as GeoDataFrame
    hex_df = pd.merge(
        left=per_hexagon_data, right=hexs[[hex_col, "geometry"]], on=hex_col
    )
    hex_gdf = gpd.GeoDataFrame(
        hex_df[[hex_col] + columns], geometry=hex_df["geometry"], crs=hexs.crs
    )

    return hex_gdf

def resolution_downsampling(
    gdf: gpd.GeoDataFrame, hex_col: str, coarse_resolution: int, agg: dict
) -> gpd.GeoDataFrame:
    """
    Downsample hexagon resolution aggregating indicated metrics (e.g. Transform hexagon resolution from 9 to 6).

    Parameters
    ----------
    gdf: GeoDataFrame
        GeoDataFrame with hexagon geometries (output from gen_hexagons).

    hex_col: str
        Determines the column with the hex id.

    coarse_resolution: int, 0:15
        Hexagon resolution lower than gdf actual resolution (higher values create smaller hexagons).

    Returns
    -------
    gdfc: GeoDataFrame
        GeoDataFrame with lower resolution hexagons geometry and metrics aggregated as indicated.
    """
    gdf_coarse = gdf.copy()
    coarse_hex_col = "hex_{}".format(coarse_resolution)
    gdf_coarse[coarse_hex_col] = gdf_coarse[hex_col].apply(
        lambda x: h3.h3_to_parent(x, coarse_resolution)
    )
    dfc = gdf_coarse.groupby([coarse_hex_col]).agg(agg).reset_index()
    gdfc_geometry = dfc[coarse_hex_col].apply(geo_boundary_to_polygon)

    return gpd.GeoDataFrame(dfc, geometry=gdfc_geometry, crs=gdf.crs)

def osmnx_coefficient_computation(
    gdf,
    net_type,
    basic_stats,
    extended_stats,
    connectivity=False,
    anc=False,
    ecc=False,
    bc=False,
    cc=False,
):
    """
    Apply osmnx's graph from polygon to query a city's street network within a geometry.
    This may be a long procedure given the hexagon layer resolution.

    Parameters
    ----------

    gdf: GeoDataFrame
        GeoDataFrame with geometries to download graphs contained within them.

    net_type: str
        Network type to download. One of {'drive', 'drive_service', 'walk', 'bike', 'all', 'all_private'}

    basic_stats: list
        List of basic stats to compute from downloaded graph

    extended_stats: list
        List of extended stats to compute from graph

    connectivity: bool. Default False.
        Compute node and edge connectivity

    anc: bool. Default False.
        Compute avg node connectivity

    ecc: bool. Default False.
        Compute shortest paths, eccentricity and topological metric

    bc: bool. Default False.
        Compute node betweeness centrality

    cc: bool. Default False.
        Compute node closeness centrality

    For more detail about these parameters, see https://osmnx.readthedocs.io/en/stable/osmnx.html#module-osmnx.stats

    Returns
    -------
    gdf: GeoDataFrame
        Input GeoDataFrame with updated columns containing the selected metrics

    Examples
    --------
    >>> hexagons = urbanpy.geom.gen_hexagons(8, lima)
    >>> urbanpy.geom.osmnx_coefficient_computation(hexagons.head(), 'walk', ['circuity_avg'], [])
    On record 1:  There are no nodes within the requested geometry
    On record 3:  There are no nodes within the requested geometry
                hex	| geometry	                                        | circuity_avg
        888e62c64bfffff	| POLYGON ((-76.89763 -12.03869, -76.90194 -12.0... | 1.021441
        888e6212e1fffff	| POLYGON ((-76.75291 -12.19727, -76.75722 -12.2... | NaN
        888e62d333fffff	| POLYGON ((-77.09253 -11.83762, -77.09685 -11.8... | 1.025313
        888e666c2dfffff	| POLYGON ((-76.93109 -11.79031, -76.93540 -11.7... | NaN
        888e62d4b3fffff	| POLYGON ((-76.87935 -12.03688, -76.88366 -12.0... | 1.044654
    """
    # May be a lengthy download depending on the amount of features
    for index, row in track(
        gdf.iterrows(),
        total=gdf.shape[0],
        description="Computing road network coefficients...",
    ):
        try:
            graph = ox.graph_from_polygon(row["geometry"], net_type)
            b_stats = ox.basic_stats(graph)
            ext_stats = ox.extended_stats(graph, connectivity, anc, ecc, bc, cc)

            for stat in basic_stats:
                gdf.loc[index, stat] = b_stats.get(stat)
            for stat in extended_stats:
                gdf.loc[index, stat] = ext_stats.get(stat)
        except Exception as err:
            print(f"On record {index}: ", err)

    return gdf


"""The emails tests module."""
import pytest
from tests.fixtures.auth import USER_EMAIL

from communication.notifications.email import mail_managers, mail_user
from users.models import User

pytestmark = pytest.mark.django_db

def test_mail_managers(mailoutbox):
    """Should send an email to the system managers."""
    mail_managers(subject="Text message", data={"text": "
Test text
"})
    assert len(mailoutbox) == 1
    mail = mailoutbox[0]
    assert mail.recipients() == ["admin@example.com", "manager@example.com"]
    assert "Text message" in mail.subject
    assert "
Test text" in mail.alternatives[0][0]

def test_mail_user(
    user: User,
    mailoutbox,
):
    """Should send an email to the user."""
    mail_user(
        user=user,
        subject="Text message",
        template="message_notification",
        data={},
    )
    assert len(mailoutbox) == 1
    mail = mailoutbox[0]
    assert mail.recipients() == [USER_EMAIL]
    assert "Text message" in mail.subject


# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import logging
import random
from datetime import datetime
from pathlib2 import Path

from common import _utils

def main(argv=None):
  parser = argparse.ArgumentParser(description='SageMaker Batch Transformation Job')
  parser.add_argument('--region', type=str, help='The region where the cluster launches.')
  parser.add_argument('--model_name', type=str, help='The name of the model that you want to use for the transform job.')
  parser.add_argument('--input_location', type=str, help='The S3 location of the data source that is associated with a channel.')
  parser.add_argument('--output_location', type=str, help='The Amazon S3 path where you want Amazon SageMaker to store the results of the transform job.')
  parser.add_argument('--output_location_file', type=str, help='File path where the program will write the Amazon S3 URI of the transform job results.')

  args = parser.parse_args()

  logging.getLogger().setLevel(logging.INFO)
  client = _utils.get_client(args.region)
  logging.info('Submitting Batch Transformation request to SageMaker...')
  batch_job_name = _utils.create_transform_job(
      client, args.model_name, args.input_location, args.output_location)
  logging.info('Batch Job request submitted. Waiting for completion...')
  _utils.wait_for_transform_job(client, batch_job_name)
  _utils.print_tranformation_job_result(args.output_location)

  Path(args.output_location_file).parent.mkdir(parents=True, exist_ok=True)
  Path(args.output_location_file).write_text(unicode(args.output_location))

  logging.info('Batch Transformation creation completed.')

if __name__== "__main__":
  main()


import math

import torch
import torch.nn as nn

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=5000):
        super().__init__()
        self.dropout = nn.Dropout(p=dropout)

        position = torch.arange(max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
        pe = torch.zeros(max_len, 1, d_model)
        pe[:, 0, 0::2] = torch.sin(position * div_term)
        pe[:, 0, 1::2] = torch.cos(position * div_term)
        self.register_buffer("pe", pe)

    def forward(self, x):
        x = x + self.pe[: x.size(0)]
        return self.dropout(x)

class TransformerNet(nn.Module):
    def __init__(
        self,
        d_model=512,
        nhead=8,
        num_encoder_layers=6,
        num_decoder_layers=6,
        dim_feedforward=2048,
        dropout=0.1,
        batch_size=512,
    ):
        super().__init__()
        self.l1 = nn.Linear(1, d_model)
        self.l2 = nn.Linear(1, d_model)
        self.positional_encoder = PositionalEncoding(d_model, max_len=batch_size)
        self.transformer = nn.Transformer(
            d_model,
            nhead,
            num_encoder_layers,
            num_decoder_layers,
            dim_feedforward,
            dropout,
            batch_first=True,
        )
        self.l3 = nn.Linear(d_model, 1)

    def forward(self, enc_x, dec_x):
        mask = nn.Transformer.generate_square_subsequent_mask(dec_x.shape[-1]).cuda()
        enc_x = enc_x.unsqueeze(-1)  # [N, x_seq, 1]
        enc_x = self.l1(enc_x)  # [N, x_seq, d_model]
        enc_x = self.positional_encoder(enc_x)
        dec_x = dec_x.unsqueeze(-1)  # [N, t_seq, 1]
        dec_x = self.l2(dec_x)  # [N, t_seq, d_model]
        dec_x = self.positional_encoder(dec_x)
        y = self.transformer(enc_x, dec_x, tgt_mask=mask)  # [N, t_seq, d_model]
        y = self.l3(y).squeeze(-1)  # [N, t_seq]
        return y

    def test(self, enc_x, dec_x, t_seq):
        dec_x = dec_x[:, [0]]  # [N, 1]
        with torch.no_grad():
            encoder = self.transformer.encoder
            decoder = self.transformer.decoder
            enc_x = enc_x.unsqueeze(-1)
            enc_x = self.l1(enc_x)
            enc_x = self.positional_encoder(enc_x)
            enc_y = encoder(enc_x)
            for i in range(1, t_seq + 1):
                dec_x2 = dec_x.unsqueeze(-1)  # [N, i, 1]
                dec_x2 = self.l2(dec_x2)  # [N, i, d_model]
                dec_x2 = self.positional_encoder(dec_x2)  # [N, i, d_model]
                y = decoder(dec_x2, enc_y)  # [N, i, d_model]
                y = self.l3(y).squeeze(-1)  # [N, 1]
                dec_x = torch.cat([dec_x, y[:, [-1]]], dim=-1)  # [N, i + 1]
        return dec_x[:, 1:]

class LSTMNet(nn.Module):
    def __init__(self, d_model=512, num_layers=1, dropout=0.1, bidirectional=False):
        super().__init__()
        self.l1 = nn.Linear(1, d_model)
        self.l2 = nn.Linear(1, d_model)
        self.enc_lstm = nn.LSTM(
            d_model, d_model, num_layers, dropout=dropout, bidirectional=bidirectional, batch_first=True
        )
        self.dec_lstm = nn.LSTM(
            d_model, d_model, num_layers, dropout=dropout, bidirectional=bidirectional, batch_first=True
        )
        self.l3 = nn.Linear(2 * d_model, 1) if bidirectional else nn.Linear(d_model, 1)

    def forward(self, enc_x, dec_x):
        enc_x = enc_x.unsqueeze(-1)  # [N, x_seq, 1]
        enc_x = self.l1(enc_x)  # [N, x_seq, d_model]
        _, hc = self.enc_lstm(enc_x)
        dec_x = dec_x.unsqueeze(-1)  # [N, t_seq, 1]
        dec_x = self.l2(dec_x)  # [N, t_seq, d_model]
        y, _ = self.dec_lstm(dec_x, hc)  # [N, t_seq, d_model]
        y = self.l3(y).squeeze(-1)  # [N, t_seq]
        return y

    def test(self, enc_x, dec_x, t_seq):
        dec_x = dec_x[:, [0]]  # [N, 1]
        with torch.no_grad():
            enc_x = enc_x.unsqueeze(-1)  # [N, x_seq, 1]
            enc_x = self.l1(enc_x)  # [N, x_seq, d_model]
            _, hc = self.enc_lstm(enc_x)
            for i in range(1, t_seq + 1):
                dec_x2 = dec_x[:, [-1]].unsqueeze(-1)  # [N, 1, 1]
                dec_x2 = self.l2(dec_x2)  # [N, 1, d_model]
                y, hc = self.dec_lstm(dec_x2, hc)  # [N, i, d_model], ...
                y = self.l3(y).squeeze(-1)  # [N, 1]
                dec_x = torch.cat([dec_x, y[:, [-1]]], dim=-1)  # [N, i + 1]
        return dec_x[:, 1:]


# from pandas import *
# from pylab import *
# import numpy as np
# from matplotlib import pyplot as plt
# mpl.rcParams['font.sans-serif'] = ['SimHei']  # 加载中文字体的神奇呀
# idx = Index(np.arange(1,7))
# df = DataFrame(np.random.randn(6, 2), index=idx, columns=['', 'count'])
# valss = np.array([['总数', 100], ['嘿嘿', 10], ['流皮', '5']])
# vals = np.around(df.values,2)
# fig = plt.figure(figsize=(9,4))
# ax = fig.add_subplot(111, frameon=False, xticks=[], yticks=[])  # 去掉背景的意思嘛
# the_table=plt.table(cellText=valss, rowLabels=None, colLabels=['', 'count'],colWidths = [0.1]*vals.shape[1], loc='center',cellLoc='center')
# the_table.set_fontsize(20)
# the_table.scale(2.5,2.58)
# plt.show()  # todo 画表格的

# import numpy as np
# import matplotlib.pyplot as plt
# men_means, men_std = (20, 35, 30, 35, 27), (0, 3, 4, 1, 2)
# women_means, women_std = (25, 32, 34, 20, 25), (3, 5, 2, 3, 3)
# ind = np.arange(len(men_means))  # the x locations for the groups
# width = 0.35  # the width of the bars
# fig, ax = plt.subplots()
# rects1 = ax.bar(ind - width/2, men_means, width,
#                 color='SkyBlue', label='Men')
# rects2 = ax.bar(ind + width/2, women_means, width,
#                 color='IndianRed', label='Women')
# ax.set_ylabel('Scores')
# ax.set_title('Scores by group and gender')
# ax.set_xticks(ind)
# ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5'))
# ax.legend()
# def autolabel(rects, xpos='center'):
#     xpos = xpos.lower()  # normalize the case of the parameter
#     ha = {'center': 'center', 'right': 'left', 'left': 'right'}
#     offset = {'center': 0.5, 'right': 0.57, 'left': 0.43}  # x_txt = x + w*off
#
#     for rect in rects:
#         height = rect.get_height()
#         ax.text(rect.get_x() + rect.get_width()*offset[xpos], 1.01*height,
#                 '{}'.format(height), ha=ha[xpos], va='bottom')
# autolabel(rects1, "left")
# autolabel(rects2, "right")
# plt.show()  # todo 画柱形图的

# import numpy as np
# import matplotlib
# import matplotlib.pyplot as plt
# sphinx_gallery_thumbnail_number = 2
# vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
#               "potato", "wheat", "barley"]
# vegetables1 = [" ", " ", " ", " ",
#               " ", " ", " "]
# vegetables2 = ["a", "asd", "asd", "asd",
#               "zxc", "asd", "qwe"]
# farmers = ["Farmer Joe", "Upland Bros.", "Smith Gardening",
#            "Agrifun", "Organiculture", "BioGoods Ltd.", "Cornylee Corp."]
# harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
#                     [2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
#                     [1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
#                     [0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
#                     [0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
#                     [1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
#                     [0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
# fig, ax = plt.subplots()
# im = ax.imshow(harvest)
# ax.set_xticks(np.arange(len(farmers)))
# ax.set_xticks(np.arange(len(farmers)+1)-.5, minor=True)
# ax.set_xticklabels(farmers)
# ax.set_yticks(np.arange(len(vegetables)))
# ax.set_yticks(np.arange(len(vegetables)+1)-.5, minor=True)
# # ax.set_yticklabels(['' for _ in range(len(vegetables))])
# ax.set_yticklabels(vegetables)
# # ... and label them with the respective list entries
# # ax.set_yticklabels([1,2,3,4,5,6,7])
# # ax.set_yticklabels([str(i) for i in range(len(vegetables))])
# ax.grid(which="minor", color="w", linestyle='-', linewidth=0)
# # Rotate the tick labels and set their alignment.
# plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
#          rotation_mode="anchor")
# # Loop over data dimensions and create text annotations.
# for i in range(len(vegetables)):
#     for j in range(len(farmers)):
#         text = ax.text(j, i, harvest[i, j],
#                        ha="center", va="center", color="w")
# ax.set_title("Harvest of local farmers (in tons/year)")
# fig.tight_layout()
# plt.show()

# import matplotlib.pyplot as plt
# def make_patch_spines_invisible(ax):
#     ax.set_frame_on(True)
#     ax.patch.set_visible(False)
#     for sp in ax.spines.values():
#         sp.set_visible(False)
# fig, host = plt.subplots()
# # fig.subplots_adjust(right=0.75)
# par1 = host.twinx()
# par2 = host.twinx()
# # Offset the right spine of par2.  The ticks and label have already been
# # placed on the right by twinx above.
# par2.spines["right"].set_position(("axes", 1.2))
# # Having been created by twinx, par2 has its frame off, so the line of its
# # detached spine is invisible.  First, activate the frame but make the patch
# # and spines invisible.
# make_patch_spines_invisible(par2)
# # Second, show the right spine.
# par2.spines["right"].set_visible(True)
# p1, = host.plot([0, 1, 2], [0, 1, 2], "b-", label="Density")
# p2, = par1.plot([0, 1, 2], [0, 3, 2], "r-", label="Temperature")
# p3, = par2.plot([0, 1, 2], [50, 30, 15], "g-", label="Velocity")
# host.set_xlim(0, 2)
# host.set_ylim(0, 2)
# par1.set_ylim(0, 4)
# par2.set_ylim(1, 65)
# host.set_xlabel("Distance")
# host.set_ylabel("Density")
# par1.set_ylabel("Temperature")
# par2.set_ylabel("Velocity")
# host.yaxis.label.set_color(p1.get_color())
# par1.yaxis.label.set_color(p2.get_color())
# par2.yaxis.label.set_color(p3.get_color())
# tkw = dict(size=4, width=1.5)
# host.tick_params(axis='y', colors=p1.get_color(), **tkw)
# par1.tick_params(axis='y', colors=p2.get_color(), **tkw)
# par2.tick_params(axis='y', colors=p3.get_color(), **tkw)
# host.tick_params(axis='x', **tkw)
# lines = [p1, p2, p3]
# host.legend(lines, [l.get_label() for l in lines])
# plt.show()

import numpy as np
import matplotlib.pyplot as plt
men_means, men_std = (20, 35, 30, 35, 27), (2, 3, 4, 1, 2)
women_means, women_std = (25, 32, 34, 20, 25), (3, 5, 2, 3, 3)
midde_means, midde_std = (25, 32, 34, 20, 25), (3, 5, 2, 3, 3)
ind = np.arange(len(men_means))  # the x locations for the groups
width = 0.2  # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.barh(ind - width/2, men_means, width,
                color='SkyBlue', label='Men')
rects2 = ax.barh(ind + width/2, women_means, width,
                color='r', label='Women')
rects3 = ax.barh(ind + width/2 + width, midde_means, width,
                color='IndianRed', label='midde')
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_yticks(ind)
ax.set_yticklabels(('G1', 'G2', 'G3', 'G4', 'G5'))
ax.legend()
def autolabel(rects, xpos='center'):
    xpos = xpos.lower()  # normalize the case of the parameter
    ha = {'center': 'center', 'right': 'left', 'left': 'right'}
    offset = {'center': 0.5, 'right': 0.57, 'left': 0.43}  # x_txt = x + w*off
    for rect in rects:
        width = rect.get_width()
        text = '{}'.format(111)
        ax.text(1.01 * rect.get_width(), rect.get_y(), text, va='bottom')
rects1_values = []
for rects in [rects1, rects2, rects2]:
    _rects1_values = []
    for rect in rects:
        _rects1_values.append(rect.get_height())
    rects1_values.append(np.array(_rects1_values))
autolabel(rects1, "center")
autolabel(rects2, "center")
autolabel(rects3, "center")
plt.show()

import matplotlib.pyplot as plt
import numpy as np
np.random.seed(19680801)
width = 0.2
plt.rcdefaults()
fig, ax = plt.subplots()

people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')
y_pos = np.arange(len(people))
performance = 3 + 10 * np.random.rand(len(people))
error = np.random.rand(len(people))

ax.barh(y_pos - width/2, performance, width, xerr=error, align='center',
        color='green', ecolor='black')
ax.barh(y_pos + width/2, performance, width, xerr=error, align='center',
        color='red', ecolor='black')
print(y_pos, people)
ax.set_yticks(y_pos)
ax.set_yticklabels(people)
ax.invert_yaxis()  # labels read top-to-bottom
ax.set_xlabel('Performance')
ax.set_title('How fast do you want to go today?')

plt.show()


##풀었지만 채은이 답이랑 유사해서 그 대신 시간 초과됐던것 올림!!
import sys
input = sys.stdin.readline

n = int(input())
n_num = list(map(int, input().split()))
m = int(input())
m_num = list(map(int, input().split()))

num = list(set(m_num) - set(n_num))

result = []
for i in m_num:
    if i not in num:
        result.append(1)
    else:
        result.append(0)

print(*result)


import sys
import re
import random

"""This method is used to split the data into training and testing dataset.
It takes 2 parameters i.e a 2D array containing sentences along with their labels and factor which is the split factor.
The split factor tells in what ratio we need to divide the testing and training data. It returns 2 arrays, i.e a Training and a Testing
dataset."""

def Split_Data(sentiments,factor):
    split_factor = factor
    Sample_Data = [i for i in range(len(sentiments))]
    #Using random function, we select random data from sentiments array to add to training data.
    Training = random.sample(range(0, len(sentiments)), int(split_factor * len(sentiments)))

    Training = list(set(Training))
    Testing = list(set(Sample_Data).difference(Training)) #This makes sure that Training and testing data are mutually exclusive.

    Testing_Data = []
    Training_Data = []
    for i in range(len(Training)):
        Training_Data.append(sentiments[Training[i]])
    for i in range(len(Testing)):
        Testing_Data.append(sentiments[Testing[i]])
    return Training_Data, Testing_Data

"""This method is used to calculate Accuracy, Precision, Recall and F1 Score.
It takes  4 inputs i.e frequency of True Positives,True Negatives, False Positives and False Negatives."""

def Calculate_Score(True_Positive,True_Negative,False_Positive,False_Negative):
    Accuracy=(float(True_Negative+True_Positive))/(True_Negative+True_Positive+False_Negative+False_Positive)
    Precision=(float(True_Positive))/(True_Positive+False_Positive)
    Recall=(float(True_Positive))/(True_Positive+False_Negative)
    F1_Score=2*(Precision*Recall)/(Precision+Recall)
    print("Accuracy is "+str(Accuracy))
    print("Precision is "+str(Precision))
    print("Recall is "+str(Recall))
    print("F1 Score is "+str(F1_Score))

"""This method is used to get the count of number of words in Positive and Negative sentences respectively."""

def Get_Count(TRAINING_DATA):
    Number_of_Positive = 0
    Number_of_Negatives = 0
    for i in range(len(TRAINING_DATA)):
        if TRAINING_DATA[i][0] == 'pos':# If the label is positive,get the count of number of words in that sentence.
            Number_of_Positive += (len(TRAINING_DATA[i]) - 1)
        else:
            Number_of_Negatives += (len(TRAINING_DATA[i]) - 1) #If label is negative, store count of number of words in that sentence.
    return Number_of_Positive,Number_of_Negatives

"""This method is used to generate the confusion matrix for the Testing Data.
It takes Testing Data which is a 2D array as input and returns the values of the cells in
a confusion matrix"""

def Confusion_Matrix(TESTING_DATA):
    True_Positive = 0
    True_Negative = 0
    False_Positive = 0
    False_Negative = 0

    for x in range(len(TESTING_DATA)):

        words = TESTING_DATA[x]
        POS = 1
        NEG = 1
        POSITIVE_OCCURENCES = 0
        NEGATIVE_OCCURENCES = 0
        for i in range(1, len(words)):
            if words[i] in positive_words:
                POSITIVE_OCCURENCES = positive_words[(words[i]).lower()]
                if POSITIVE_OCCURENCES<=50: #Don't consider a word if its occuring less than or equal to 50 times.
                    continue
            if words[i] in negative_words:
                NEGATIVE_OCCURENCES = negative_words[(words[i]).lower()]
                if NEGATIVE_OCCURENCES<=50: #Don't consider a word if its occuring less than or equal to 50 times.
                    continue

            POS *= (float(POSITIVE_OCCURENCES + 1)) / (len(bag_of_words) + Number_of_Positive)
            NEG *= (float(NEGATIVE_OCCURENCES + 1)) / (len(bag_of_words) + Number_of_Negatives)

        POS=pos*POS #POS multiplied by the probability of positive i.e pos
        NEG=neg*NEG #NEG multiplied by the probability of negative i.e neg

        if (POS>NEG):
            Predicted = 'pos'
        else:
            Predicted = 'neg'

        Actual = words[0] #The first word gives the label i.e positive or negative
        if Predicted == 'pos' and Actual == 'pos':
            True_Positive += 1
        elif Predicted == 'pos' and Actual == 'neg':
            False_Positive += 1
        elif Predicted == 'neg' and Actual == 'pos':
            False_Negative += 1
        elif Predicted == 'neg' and Actual == 'neg':
            True_Negative += 1
    print("True Positives = "+str(True_Positive))
    print("True Negatives = "+str(True_Negative))
    print("False Positives = "+str(False_Positive))
    print("False Negatives = "+str(False_Negative))
    Calculate_Score(True_Positive,True_Negative,False_Positive,False_Negative)

"""This method id used to get the data from a text file and store it into a 2D array. It also counts
the number of positive and negative examples. Also, we have a dictionary named bag_of_words for storing
all the unique words present in the document."""

def Get_Input(path):

    delimeter = ",|-| |;|\.|\(|\)|\n|\"|:|'|/|&|`|[|]|\{|\}"
    bag_of_words = {}
    negative_words = {}
    positive_words = {}
    sentiments = []
    pos = 0
    neg = 0
    with open(path, encoding="utf8") as f:

        for line in f:

            words = re.split(delimeter, line) #Split a sentence into array of words

            if words[1] == 'neg':
                neg += 1 #Counting number of negative examples
                for i in range(4, len(words)):
                    if words[i] not in negative_words:
                        negative_words[words[i]] = 1
                    else:
                        negative_words[words[i]] += 1
            elif words[1] == 'pos':
                pos += 1#Counting number f positive examples
                for i in range(4, len(words)):
                    if words[i] not in positive_words:
                        positive_words[words[i]] = 1
                    else:
                        positive_words[words[i]] += 1

            temp = [words[1]]  # Storing sentences along with their labels
            for i in range(4, len(words)):
                if len(words[i]) != 0:
                    temp += [(words[i]).lower()]

            sentiments.append(temp)

            for i in range(4): #Removing file name, format, and label.
                words.pop(0)

            for i in range(len(words)):

                if len(words[i]) != 0:
                    bag_of_words[(words[i]).lower()] = 1 #Putting words in bag_of_words. It contains the number of unique words in the doc.
    POSITIVE_WORDS={}
    for i in positive_words:
        if positive_words[i]<=10: #Remove less frequent words
            continue
        else:
            POSITIVE_WORDS[i]=positive_words[i]
    NEGATIVE_WORDS={}
    for i in negative_words:
        if negative_words[i]<=10: #Remove less frequent words
            continue
        else:
            NEGATIVE_WORDS[i]=negative_words[i]

    return sentiments,POSITIVE_WORDS,NEGATIVE_WORDS,bag_of_words,pos,neg

#Read and store necessary information from the text file given as input data.
sentiments,positive_words,negative_words,bag_of_words,pos,neg = Get_Input('naive_bayes_data.txt')

#Split the 2D array data i.e sentiments array into 2 sets i.e training and testing data and specify the split_factor
TRAINING_DATA, TESTING_DATA = Split_Data(sentiments,0.8)
#Get the count of number of positive and negative words
Number_of_Positive,Number_of_Negatives=Get_Count(TRAINING_DATA)
#Generate the confusion matrix
Confusion_Matrix(TESTING_DATA)


def main():
    plate = input("Plate: ")
    if is_valid(plate):
        print("Valid")
    else:
        print("Invalid")

def is_valid(s):

    s_len = len(s)

    # must contain a max of 6 and min 2 characters(letters or numbers)
    if 6 <= len(s) >= 2:
        aa = is_nospecial(s)
        bb = start_letters(s)
        cc = first_num(s)
        dd = last_in_digit(s)

        # final decision to feed the main function
        if aa == True and bb == True and cc == True and dd == True:
            return True

    else:
        return False

    # no special characters Eg. Hello, -> cant have ","
def is_nospecial(x):
    special_char = "'[@_!.,#$%^&*()<>?/\|}{~:]'"

    if any(i in special_char for i in x):
	    return False
    else:
        return True

        # Must start at least with 2 letter Eg. AA4554, ASD457, BB1111
def start_letters(x):
    a = x[0]
    b = x[1]

    if a.isalpha() == True and b.isalpha() == True:
        return True
    else:
        return False

        # The first number used cant be a 0 Eg. CS050 -> cant be "0"

def first_num(x):

    safe = ''

    x = string_list(x)

    for i in x:
        if i.isdigit():
            safe += i
            break

    if safe.isdigit():
        if safe == '0':
            return False
        else:
            return True
    else:
        return True

def string_list(x):
    list = []
    list[:0] = x
    return list

        # numbers cant be in the middle of letters must be at the end Eg. AAA124
def last_in_digit(x):
    y = len(x)
    y -= 1
    check = x[y]

    for i in x:
        if check.isdigit():
            return True
        elif x.isalpha():
            return True
        else:
            return False

main()

Back End/2. Python/CS50 With Python/Week2/pset/plates/plates.py