import base64
import json
from io import BytesIO
from os import environ

import boto3
import numpy as np
import requests
from opensearchpy import AWSV4SignerAuth, OpenSearch, RequestsHttpConnection
from PIL import Image

# Global variables that are reused
sm_runtime_client = boto3.client("sagemaker-runtime")
s3_client = boto3.client("s3")

def get_features(img_bytes, sagemaker_endpoint=environ["SM_ENDPOINT"]):
    img_bytes = image_preprocessing(img_bytes, return_body=True)
    response = sm_runtime_client.invoke_endpoint(
        EndpointName=sagemaker_endpoint,
        ContentType="application/json",
        Body=img_bytes,
    )
    response_body = json.loads((response["Body"].read()))
    features = response_body["predictions"][0]
    return features

def get_neighbors(features, oss, k_neighbors=3):
    idx_name = "idx_zalando"
    query = {
        "size": k_neighbors,
        "query": {
            "knn": {"zalando_img_vector": {"vector": features, "k": k_neighbors}}
        },
    }
    res = oss.search(request_timeout=30, index=idx_name, body=query)
    s3_uris = [res["hits"]["hits"][x]["_source"]["image"] for x in range(k_neighbors)]

    return s3_uris

def generate_presigned_urls(s3_uris):
    def _s3_client_presigned_url(bucket, key):
        return s3_client.generate_presigned_url(
            ClientMethod="get_object",
            Params={"Bucket": bucket, "Key": key},
            ExpiresIn=60 * 5,
        )

    bucket = s3_uris[0].replace("s3://", "").split("/")[0]
    presigned_urls = [
        _s3_client_presigned_url(bucket, x.replace(f"s3://{bucket}/", ""))
        for x in s3_uris
    ]

    return presigned_urls

def download_file(url):
    r = requests.get(url)
    if r.status_code == 200:
        file = BytesIO(r.content)
        return file
    else:
        print("file failed to download")
        return None

def create_oss_client():
    region = environ["AWS_REGION"]
    elasticsearch_endpoint = environ["ES_ENDPOINT"]

    credentials = boto3.Session().get_credentials()
    awsauth = AWSV4SignerAuth(credentials, region)

    oss = OpenSearch(
        hosts=[{"host": elasticsearch_endpoint, "port": 443}],
        http_auth=awsauth,
        use_ssl=True,
        verify_certs=True,
        connection_class=RequestsHttpConnection,
    )

    return oss

def image_preprocessing(img_bytes, return_body=True):
    img = Image.open(img_bytes).convert("RGB")
    img = img.resize((224, 224))
    img = np.asarray(img)
    img = np.expand_dims(img, axis=0)
    if return_body:
        body = json.dumps({"instances": img.tolist()})
        return body
    else:
        return img

def lambda_handler(event, _):
    oss_client = create_oss_client()

    api_payload = json.loads(event["body"])
    k = api_payload["k"]

    if event["path"] == "/postURL":
        image = download_file(api_payload["url"])
    else:
        img_string = api_payload["base64img"]
        image = BytesIO(base64.b64decode(img_string))

    features = get_features(image)
    s3_uris_neighbors = get_neighbors(features, oss_client, k_neighbors=k)
    s3_presigned_urls = generate_presigned_urls(s3_uris_neighbors)

    return {
        "statusCode": 200,
        "headers": {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "*",
            "Access-Control-Allow-Methods": "*",
        },
        "body": json.dumps(
            {
                "images": s3_presigned_urls,
            }
        ),
    }


#A function that returns the largest and smallest elements in a list
#Author: Pedro Mota
#Date: 21/09/2017

def ProblemSheetQ6(numberList):
    biggest=0
    smallest=numberList[0]

    for n in numberList:
        if(smallest<n):
            smallest=n
        elif(n>biggest):
            biggest=n

    return smallest,biggest


# -*- encoding:utf-8 -*-
# __author__=='Gan'

# Given a positive integer n, find the number of non-negative integers less than or equal to n,
# whose binary representations do NOT contain consecutive ones.
# Example 1:
# Input: 5
# Output: 5
# Explanation:
# Here are the non-negative integers <= 5 with their corresponding binary representations:
# 0 : 0
# 1 : 1
# 2 : 10
# 3 : 11
# 4 : 100
# 5 : 101
# Among them, only integer 3 disobeys the rule (two consecutive ones) and the other 5 satisfy the rule.
# Note: 1 <= n <= 109

# Reference: http://www.geeksforgeeks.org/count-number-binary-strings-without-consecutive-1s/
# a[i]:the number of binatry string of length i which do not conatain any two consecutive 1s and end in 0
# b[i]:the number of binatry string of length i which do not conatain any two consecutive 1s and end in 1
# a[i]=a[i-1]+b[i-1]
# b[i]=a[i-1]
# 527 / 527 test cases passed.
# Status: Accepted
# Runtime: 62 ms
class Solution(object):
    def findIntegers(self, num):
        """
        :type num: int
        :rtype: int
        """
        binary = bin(num)[2:][::-1]
        l = len(binary)
        a = [0] * l
        b = [0] * l
        a[0] = b[0] = 1
        for i in range(1, l):
            a[i] = a[i - 1] + b[i - 1]
            b[i] = a[i - 1]

        result = a[l - 1] + b[l - 1]
        for i in range(l - 2, -1, -1):
            if binary[i + 1] == binary[i]:
                if binary[i] == '1':
                    break
                elif binary[i] == '0':
                    print(result, binary, b, i)
                    result -= b[i]
        return result

# Dp formula like above, but operation like Fibonacci.
# 527 / 527 test cases passed.
# Status: Accepted
# Runtime: 46 ms
# Your runtime beats 52.38 % of python submissions.
class Solution(object):
    def findIntegers(self, num):
        """
        :type num: int
        :rtype: int
        """
        DP = [1, 2]
        binary = bin(num)[2:]
        l = len(binary)
        [DP.append(DP[-1] + DP[-2]) for _ in range(2, l + 1)]
        for i in range(1, l):
            if binary[i] == binary[i - 1]:
                if binary[i] == '1':
                    break
                if binary[i] == '0':
                    DP[-1] -= (DP[l - i] - DP[l - i - 1])
        return DP[-1]

if __name__ == '__main__':
    print(Solution().findIntegers(20))
    print(Solution().findIntegers(5))
    print(Solution().findIntegers(8))
    print(Solution().findIntegers(1))
    print(Solution().findIntegers(0))
    print(Solution().findIntegers(3))
    print(Solution().findIntegers(2))


# -*- coding: utf-8 -*-
import xlrd
import os
import xlwt
#import pandas as pd
#import numpy as np
from xlrd import open_workbook
from xlutils.copy import copy

execl_file = "test.xlsx"

if __name__ == "__main__":
    print(">>>>>>>Python操作execl<<<<<<<<<")
    try:
        data = xlrd.open_workbook(execl_file)
        print("[OK]Open %s success（%s）" % (execl_file,type(data)))
        #属性遍历
        #for i in dir('data'):
        #   print(i)

        #获取一个工作表,默认有三张表
        table = data.sheets()[0]
        #for i in dir(table):   #遍历对象
        #   print(i)

        #遍历整个表
        nrows = table.nrows
        ncols = table.ncols
        for i in range(nrows):
            for j in range(ncols):
                print("|",table.row_values(i)[j],end="\t"), #输出不换行
            print("|")

        #增加一行保存
        save_xsl = copy(data)
        save_xsl.get_sheet(0).write(4,0,4)
        save_xsl.get_sheet(0).write(4,1,"user4")
        save_xsl.get_sheet(0).write(4,2,"pwd4")
        save_xsl.get_sheet(0).write(4,3,"100")
        save_xsl.get_sheet(0).write(4, 4, "四")
        save_xsl.save("test_copy.xlsx")

    except Exception as e:
        print("%s" % (str(e)))
    finally:
        print(">>>End") #一定会执行到


import sys
sys.stdin=open('sample_input(3).txt')

T= int(input())

def delrep(a):                          # 연속되는 문자를 제거해주는 함수를 만든다.
    lista=list(a)                       # 리스트로 변환
    for i in range(len(a)-1):
        if lista[i]==lista[i+1]:        # 해당 인덱스와 다음 인덱스에 있는 문자가 같으면
            lista[i]=lista[i+1]=''      # 공백으로 변환해준다.

    modi_str="".join(lista)             # 그 뒤 문자열로 변환하고

    if modi_str==a:                     # 만약 변화가 없었다면 더 이상 제거할 만한 부분이 없다는 것으로 그대로 반환하고
        return modi_str
    else:                               # 변화가 있었다면 아직 안 끝났을 수도 있으므로 다시 한 번 함수를 돌려준다.
        return delrep(modi_str)

for t in range(1,T+1):
    inputstring=input().rstrip()

    print(f'#{t} {len(delrep(inputstring))}')


import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR
from tqdm import tqdm
from trainer import *
from tester import *
import torchvision.models as models
import os
import numpy as np
import matplotlib.pyplot as plt

os.environ["TORCH_HOME"] = "~/Desktop/Datasets/"

# Allowing arguments for direct execution from terminal
parser = argparse.ArgumentParser()
parser.add_argument(
    "--arch",
    default="resnet18",
    help="""Choose any model from pytorch. Or input "my" for taking a model from model.py """,
)
parser.add_argument("--weight-decay", default=1e-4, help="weight decay coefficient")
parser.add_argument("--resume", default=False, help="Resume training from a checkpoint")
parser.add_argument(
    "--pretrained",
    default=True,
    help="If part of the standard datasets, downloaded pretrained weights",
)
parser.add_argument("--batch-size", type=int, default=128, help="input batch size")
parser.add_argument("--test-batch-size", type=int, default=1000)

parser.add_argument("--epochs", type=int, default=20, help="no of epochs to train for")

parser.add_argument("--lr", type=float, default=0.01, help="Base learning rate")

parser.add_argument(
    "--max_lr", type=float, default=0.1, help="Max learning rate for OneCycleLR"
)

parser.add_argument(
    "--dry-run", action="store_true", default=False, help="quickly check a single pass"
)

parser.add_argument("--seed", type=int, default=100, help="torch random seed")

parser.add_argument(
    "--log-interval", type=int, default=20, help="interval to show results"
)

parser.add_argument(
    "--save-model",
    action="store_true",
    default=True,
    help="Choose if model to be saved or not",
)

parser.add_argument(
    "--save_path", default="models/model.pt", help="Choose model saved filepath"
)

args = parser.parse_args()

# Setting params

torch.manual_seed(args.seed)
device = torch.device("cuda")
kwargs = {"batch_size": args.batch_size}
kwargs.update({"num_workers": 8, "pin_memory": True, "shuffle": True})

# Defining batch transforms

transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
)

# Loading dataset

train_data = datasets.MNIST("~/Desktop/Datasets/", train=True, transform=transform)

test_data = datasets.MNIST("~/Desktop/Datasets/", train=False, transform=transform)

train_loader = torch.utils.data.DataLoader(train_data, **kwargs)

test_loader = torch.utils.data.DataLoader(test_data, **kwargs)

# Loading model

if args.arch == "my":
    from Nets import *

    model = Net(10).to(device)
    print("Using custom architecture")
else:
    if args.pretrained:
        print(f"Using pretrained {args.arch}")
        model = models.__dict__[args.arch](pretrained=True)
    else:
        print(f"Not using pretrained {args.arch}")
        model = models.__dict__[args.arch]()

model = model.to(device)
print(model)
start_epoch = 1
if args.resume:
    loc = "cuda:0"
    checkpoint = torch.load(args.save_path, map_location=loc)
    model.load_state_dict(checkpoint["state_dict"])
    mode.load_state_dict(checkpoint["optimizer"])
    start_epoch = checkpoint["epoch"]

    print(f"Done loading pretrained, Start epoch: {checkpoint['epoch']}")

optimizer = optim.Adam(model.parameters(), lr=args.lr, weight_decay=args.weight_decay)
scheduler = optim.lr_scheduler.OneCycleLR(
    optimizer, max_lr=args.max_lr, steps_per_epoch=len(train_loader), epochs=10
)

for epoch in tqdm(range(start_epoch, args.epochs + 1)):
    train(args, model, device, train_loader, optimizer, epoch)
    test(model, device, test_loader)
    scheduler.step()

if args.save_model:
    torch.save(model.state_dict(), args.save_path)

# Visualizing model
def convert_image_np(inp):
    """Convert a Tensor to numpy image."""
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    return inp

def visualize_stn():
    with torch.no_grad():
        # Get a batch of training data
        data = next(iter(test_loader))[0].to(device)

        input_tensor = data.cpu()
        transformed_input_tensor = model.stn(data).cpu()

        in_grid = convert_image_np(torchvision.utils.make_grid(input_tensor))

        out_grid = convert_image_np(
            torchvision.utils.make_grid(transformed_input_tensor)
        )

        # Plot the results side-by-side
        f, axarr = plt.subplots(1, 2)
        axarr[0].imshow(in_grid)
        axarr[0].set_title("Dataset Images")

        axarr[1].imshow(out_grid)
        axarr[1].set_title("Transformed Images")
        plt.ioff()
        plt.show()
        plt.savefig("./output.png")

visualize_stn()


import lib.record_rate
import lib.commonclient
import lib.getconfig
import lib.puylogger
import lib.pushdata
import lib.basecheck
import json

baseurl = lib.getconfig.getparam('Docker', 'stats')
detailed_mon = lib.getconfig.getparam('Docker', 'detailed')
prettynames = lib.getconfig.getparam('Docker', 'prettynames')
memstats = lib.getconfig.getparam('Docker', 'memstats')
check_type = 'docker'

class Check(lib.basecheck.CheckBase):

    def precheck(self):

        containers = []
        try:
            jsondata = lib.pushdata.JonSon()
            jsondata.prepare_data()
            base_json = json.loads(lib.commonclient.httpget(__name__, baseurl + '/json'))
            containers.append(base_json[0]['Id'])
            containers.append(base_json[1]['Id'])

            for container in containers:
                data = json.loads(lib.commonclient.httpget(__name__, baseurl + '/' + container + '/stats?logs=0&stream=0'))
                if prettynames is True:
                    container_name = data['name'].replace('/','')
                else:
                    container_name = container
                cpu_delta = data['cpu_stats']['cpu_usage']['total_usage'] - data['precpu_stats']['cpu_usage']['total_usage']
                system_delta = data['cpu_stats']['system_cpu_usage'] - data['precpu_stats']['system_cpu_usage']

                cpu_sage = float("{0:.3f}".format(cpu_delta / system_delta * 100))
                throttled_periods = data['cpu_stats']['throttling_data']['throttled_periods']
                throttled_time = data['cpu_stats']['throttling_data']['throttled_time']
                self.local_vars.append({'name': check_type + '_master_cpu_usage', 'timestamp': self.timestamp, 'value': cpu_sage,'chart_type': 'Percent', 'check_type': check_type,  'extra_tag':{'container': container_name}})
                self.local_vars.append({'name': check_type + '_cpu_throttled_periods', 'timestamp': self.timestamp, 'value': throttled_periods, 'check_type': check_type, 'extra_tag':{'container': container_name}})
                self.local_vars.append({'name': check_type + '_cpu_throttled_time', 'timestamp': self.timestamp, 'value': throttled_time, 'check_type': check_type, 'extra_tag':{'container': container_name}})

                getval = lib.record_rate.GetPrevValue()
                prev_cpu_usage = getval.return_last_value('cpu_total_usage_' + container_name, data['cpu_stats']['cpu_usage']['total_usage'])
                prev_system_usage = getval.return_last_value('cpu_system_usage_'+ container_name, data['cpu_stats']['system_cpu_usage'])

                cpudelta = data['cpu_stats']['cpu_usage']['total_usage'] - prev_cpu_usage
                systemdelta = data['cpu_stats']['system_cpu_usage'] - prev_system_usage
                num_cpu = len(data['precpu_stats']['cpu_usage']['percpu_usage'])

                cpu_percent = (cpudelta / systemdelta) * num_cpu * 100.0
                self.local_vars.append({'name': check_type + '_container_cpu_usage', 'timestamp': self.timestamp, 'value': float("{:.2f}".format(cpu_percent)), 'chart_type': 'Percent', 'check_type': check_type, 'extra_tag':{'container': container_name}})

                network = data['networks']
                for k, v in network.items():
                    bytes_rx = data['networks'][k]['rx_bytes']
                    bytes_tx = data['networks'][k]['tx_bytes']
                    self.local_vars.append({'name': check_type + '_bytes_rx_' + k, 'extra_tag':{'container': container_name}, 'timestamp': self.timestamp, 'value': bytes_rx, 'check_type': check_type, 'chart_type': 'Rate'})
                    self.local_vars.append({'name': check_type + '_bytes_tx_' + k, 'extra_tag':{'container': container_name}, 'timestamp': self.timestamp, 'value': bytes_tx, 'check_type': check_type, 'chart_type': 'Rate'})
                    if detailed_mon is True:
                        netchecks_stack = ('rx_errors', 'tx_errors', 'rx_dropped', 'tx_dropped')
                        netchecks_rated = ('rx_packets', 'tx_packets')
                        for netcheck_rated in netchecks_rated:
                            value = self.rate.record_value_rate(container + netcheck_rated , data['networks'][k][netcheck_rated], self.timestamp)
                            self.local_vars.append({'name': check_type + '_' + netcheck_rated + '_' + k, 'timestamp': self.timestamp, 'value': value, 'check_type': check_type, 'chart_type': 'Rate','extra_tag':{'container': container_name}})
                        for netcheck_stack in netchecks_stack:
                            value = data['networks'][k][netcheck_stack]
                            self.local_vars.append({'name': check_type + '_' + netcheck_stack + '_' + k, 'timestamp': self.timestamp, 'value': value, 'check_type': check_type, 'extra_tag':{'container': container_name}})

                if memstats is True:
                    mem_cur_usage = data['memory_stats']['usage']
                    mem_max_usage = data['memory_stats']['max_usage']
                    self.local_vars.append({'name': check_type + '_mem_cur_usage', 'timestamp': self.timestamp, 'value': mem_cur_usage, 'check_type': check_type, 'extra_tag':{'container': container_name}})
                    self.local_vars.append({'name': check_type + '_mem_max_usage', 'timestamp': self.timestamp, 'value': mem_max_usage, 'check_type': check_type, 'extra_tag':{'container': container_name}})
                    if detailed_mon is True:
                        memchecks = ('total_active_anon', 'total_active_file', 'total_cache',
                                     'total_inactive_anon', 'total_inactive_file', 'total_mapped_file',
                                     'total_rss', 'total_swap', 'total_unevictable')
                        for memcheck in memchecks:
                            value = data['memory_stats']['stats'][memcheck]
                            self.local_vars.append({'name': check_type + '_mem_' + memcheck, 'timestamp': self.timestamp, 'value': value, 'check_type': check_type, 'extra_tag':{'container': container_name}})

        except Exception as e:
            lib.puylogger.print_message(__name__ + ' Error : ' + str(e))
            pass


from ..util import ByteBuffer
from .header import MsgHeader
from .base import MsgBase
from .car_position import MsgCarPosition
from .car_stats import MsgCarStats
from .cup_info import MsgCupInfo
from .heartbeat import MsgHeartbeat
from .lap_info import MsgLapInfo
from .pit_lane_event import MsgPitLaneEvent
from .pit_lane_extended import MsgPitLaneExtended
from .pit_window import MsgPitWindow
from .race_status import MsgRaceStatus
from .track_config import MsgTrackConfig
from .vitc_to_lap import MsgVitcToLap

_parsers = {
    "a": MsgCarStats,
    "b": MsgPitLaneExtended,
    "d": MsgPitLaneExtended,
    "C": MsgCupInfo,
    "F": MsgPitWindow,
    "H": MsgHeartbeat,
    "l": MsgLapInfo,
    "O": MsgTrackConfig,
    "P": MsgPitLaneEvent,
    "s": MsgRaceStatus,
    "V": MsgVitcToLap,
    "W": MsgCarPosition,
}

class MsgFactory(object):

    HEADER_SIZE = 7

    def __init__(self, source=b""):
        self._buffer = ByteBuffer(source)

    def push_data(self, b):
        self._buffer.write(b)

    def has_message(self):
        available = self._buffer.size()
        if available >= self.HEADER_SIZE:
            header = MsgHeader(self._buffer.peek(self.HEADER_SIZE))
            if header.size + self.HEADER_SIZE <= available:
                return True

        return False

    def get_message(self):
        if self.has_message():
            header = MsgHeader(self._buffer.peek(self.HEADER_SIZE))
            message_parser = _parsers[header.byte_type]
            msg_bytes = self._buffer.read(header.size + self.HEADER_SIZE)
            return message_parser(msg_bytes)

        return None


'''
Coded by: Jacky Shew

Line 46 and 52 need reviewing
However I won't be continuing as this program has already served its purpose
'''

import binascii #Program needs this

#################################################
#	       The XOR/OTP Cipher should only       #
#      consist of 13 Hexadecimals each line     #
#################################################

#Insert your One Time Pad inbetween Brackets ([])

pad = [
"F852126DB3BB782B7CC6D652259616DA5EA19ED85A61E97FC383",\
"E4480D28BFBA686563DAC0053FC542C55EED819D587BE96AD483",\
"EF525F71B3A03D6079C7CE0521DE0D9248E982C91967E46EDD92",\
"ED511A6DFCB4692B78C6DA4076D9109259E4CDDE5866EB63C483",\
"E6520C7CFCBA7B2B63C0DC0535DF16CB1BE89E9D5C7EFC7FC983",\
"FF551A28AABC787C37C1CA0533CE16C05AEE9FD9507DED79C983",\
"E3580D6DFCA1756E65CD994424D342DC54A19ECD5077E979C383"
]

#For each line, put speech marks or apostrophes to convert into string
#Separate each line with a comma (,)
#OPTIONAL: Separate each line with a backslash (/) and press Enter

#Last column is not a letter
#Last column probably is Full Stops, Question Marks, etc...

#XOR Cipher cannot be cracked if length()

####################################################
#        PUT THE PROGRAM IN AN EMPTY FOLDER        #
#           MAY CREATE A LOT OF TEXTFILES          #
####################################################

#You may now Start the Program

column_move = 0 #Initialising Variable for Column
letter_count = 0 #Initialising Variable for Alphabetical Letter
first_column = True
while column_move!= 26: #
						# Needs to be 27 so it'll also go over the last column
						#
	first,second = 0+column_move*2,1+column_move*2 #Chained for easier change
	column_hex = pad[0][first]+pad[0][second] #Column Hex looping
	text_file = open(str(column_move+1)+". "+column_hex+"'s Output File.txt", "w") #Saves File with Corresponding Hex
	while letter_count !=26: #
							 # Needs to be a higher count due to additional punctuation marks and full stop
							 #
		cap_letter_hex = [41,42,43,44,45,46,47,48,49,'4A','4B','4C','4D','4E','4F',\
						  50,51,52,53,54,55,56,57,58,59,'5A'] #Hex Code for Capital Letters in Alphabetical Order
		lower_letter_hex = [20,61,62,63,64,65,66,67,68,69,'6A','6B','6C','6D',\
							'6E','6F',70,71,72,73,74,75,76,77,78,79,'7A','2E',21,'3F'] #Hex Code for Space + Lowercase Letters in Alphabetical Order + Ending Punctuation
		if first_column == True:
			key = ((int('0x'+column_hex,16) ^ (int('0x'+str(cap_letter_hex[0+letter_count]),16)))) #Generated Key from XOR
		else:
			key = ((int('0x'+column_hex,16) ^ (int('0x'+str(lower_letter_hex[0+letter_count]),16)))) #Generated Key from XOR
		for i in pad:
			character = chr(int('0x'+i[first]+i[second],16) ^ key) #Takes Key, Cipher XOR with Key
			if character not in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz .?!":
				character = character + " DELETE "*2 #Yes I'm lazy
			#text_file.write(i[first]+i[second]+' could be: '+character+'\n') #Cipher Hex could be: Potential Character
			#
			text_file.write(":"+character+'\n') #Cipher Hex could be: Potential Character
			#								 #Will only print Potential Character (Either use this or Above not Both)
		#text_file.write("\n-------------------SEPARATOR-------------------\n\n") #Separator for New Potential Letter
		#
		text_file.write("\n-----\n\n") #Separator for New Potential Letter
		#								#Here's a smaller Separator better for 'only print Potential Character version'
		letter_count += 1 #Continue While Loop for Alphabetical Letter Descending
	first_column = False
	letter_count = 0 #Reset Letter Count for Next Column
	column_move += 1 #Continue While Loop for Column
	text_file.close()
text_file.close()
text_file = open("28. END.txt", "w")
text_file.write('Most likely...\n\nFullstops\nQuestion Marks\nExclamation Marks\n\netc...')
text_file.close()
text_file = open("0. Readme.txt", "w")
text_file.write("Now that the program has finished\nIt is time you use the Process of Elimination\
				 \n\nSpaces may have DELETE notice but may potentially be correct thus don't delete\n\
				 \nGood Luck\n-Jacky")
text_file.close()


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

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

@app.route('/ninja')
def ninja():
	session.pop("img", None)
	session["img"]=url_for('static', filename='tmnt.png', width=100)
	return render_template('index2.html')

@app.route('/ninja/blue')
def ninja_blue():
	session.pop("img", None)
	session['img']=url_for('static', filename='leonardo.jpg', width=100)
	return render_template('index2.html')

@app.route('/ninja/orange')
def ninja_orange():
	session.pop("img", None)
	session['img']=url_for('static', filename='michelangelo.jpg', width=100)
	return render_template('index2.html')

@app.route('/ninja/red')
def ninja_red():
	session.pop("img", None)
	session['img']=url_for('static', filename='raphael.jpg', width=100)
	return render_template('index2.html')

@app.route('/ninja/purple')
def ninja_purple():
	session.pop("img", None)
	session['img']=url_for('static', filename='donatello.jpg', width=100)
	return render_template('index2.html')

@app.route('/ninja/<x>')
def notapril(x):
	session.pop("img", None)
	session['img']=url_for('static', filename='notapril.jpg', width=100)
	return render_template('index2.html')

app.run(debug=True)


import logbook
from model import db, AccountDb, Customer, autocommit
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship
from arrow import utcnow

class Deferred(db.Model, AccountDb):
    id_field = "deferred_id"

    deferred_id = Column(db.Integer, primary_key=True)
    user_id = Column(db.Integer, ForeignKey("user.user_id"))
    customer_id = Column(db.Integer, ForeignKey("customer.customer_id"), unique=True)
    tariff_id = Column(db.Integer, ForeignKey("tariff.tariff_id"))
    date = Column(db.DateTime(), index=True)
    comment = Column(db.Text())

    tariff = relationship("Tariff")
    user = relationship("User")
    customer = relationship("Customer")

    def __str__(self):
        return "<Deferred %s %s>" % (self.tariff.name, self.date)

    def __repr__(self):
        return str(self)

    @classmethod
    def create(cls, customer_id, tariff_id, user_id, date, comment):
        deferred = cls.query.filter_by(customer_id=customer_id).first()
        if not deferred:
            deferred = cls()
            deferred.customer_id = customer_id
            db.session.add(deferred)
        deferred.tariff_id = tariff_id
        deferred.user_id = user_id
        deferred.date = date
        deferred.comment = comment

        return deferred

    @classmethod
    def find_deferred(cls, now=None):
        now = now or utcnow()
        return cls.query.filter(cls.date <= now)

    def display(self, short=True):
        return {"tariff": self.tariff.display(short=True),
                "date": self.date,
                "user": self.user.display(),
                "comment": self.comment
                }

    @classmethod
    def delete_by_customer(cls, customer_id):
        return cls.query.filter_by(customer_id=customer_id).delete(False)

    @classmethod
    def get_by_customer(cls, customer_id):
        return cls.query.filter_by(customer_id=customer_id).first()

    @classmethod
    def process_pending_deferred_changes(cls, time_now=None, name_prefix=""):
        db.session.rollback()
        logbook.debug("Process pending deferred changes task for customer prefix {} and time {}", name_prefix, time_now)
        time_now = time_now or utcnow().datetime
        query = cls.find_deferred(time_now)
        if name_prefix:
            customer_ids = [c.customer_id for c in Customer.get_customers_by_prefix_info_field(name_prefix, "name")]
            query = query.filter(cls.customer_id.in_(customer_ids)) if customer_ids else []

        count = 0
        for deferred in query:
            cls.do_deferred_changes(deferred)
            count += 1
        logbook.debug("Processed {} pending deferred changes", count)

        db.session.commit()
        return count

    @classmethod
    @autocommit
    def do_deferred_changes(cls, deferred_changes):
        logbook.info("Process pending deferred change {}", deferred_changes)

        deferred_changes.customer.update_tariff(deferred_changes.tariff_id,
                                                deferred_changes.user_id,
                                                deferred_changes.comment)
        Deferred.delete_by_customer(deferred_changes.customer_id)


#! /usr/bin/env python3

import time
import sys

routes = [
    '10.0.0.1/32',
    '10.0.0.2/32',
    '10.0.0.3/32',
    '10.0.0.4/32',
    '10.0.0.5/32',
    '10.0.0.6/32',
    '10.0.0.7/32',
    '10.0.0.8/32',
    '10.0.0.9/32',
    '10.0.0.10/32',
    '10.0.1.0/24',
    '10.0.2.0/24',
    '10.0.3.0/24',
    '10.0.4.0/24',
    '10.0.5.0/24',
    '10.0.6.0/24',
    '10.0.7.0/24',
    '10.0.8.0/24',
    '10.0.9.0/24',
    '10.0.10.0/24',
]

# Helper function to bring route (NLRI + attributes) in format needed by ExaBGP
def get_exa(nlri, withdraw=False):
    path = "[64496 64497 64498]"

    if withdraw:
        exastring = f'withdraw route {nlri}\n'
    else:
        exastring = f'announce route {nlri} next-hop self origin incomplete med 42 as-path {path}\n'

    return exastring

# t=0s: we will wait 5 sec to
time.sleep(5)

# t=5s: announce all routes
for r in routes:
    sys.stdout.write(get_exa(r))
sys.stdout.write("\n")
sys.stdout.flush()

time.sleep(65)
# t=70s: withdraw first 10 routes
for i in range(0,10):
    sys.stdout.write(get_exa(routes[i], True))
sys.stdout.write("\n")
sys.stdout.flush()

time.sleep(65)
# t=135s: announce first 5 routes + withdraw another 2 routes
for i in range(0,5):
    sys.stdout.write(get_exa(routes[i]))
for i in range(10,12):
    sys.stdout.write(get_exa(routes[i], True))
sys.stdout.write("\n")
sys.stdout.flush()

time.sleep(65)
# t=200s: withdraw all routes
for r in routes:
    sys.stdout.write(get_exa(r, True))
sys.stdout.write("\n")
sys.stdout.flush()

time.sleep(10)
# t=210s: shutdown session
sys.stdout.write('shutdown neighbor 192.168.122.44\n')
sys.stdout.flush()


class Solution(object):
    def countSubstrings(self, s):
        """
        :type s: str
        :rtype: int
        """
        n =  len(s)
        count = 0
        for i in range(n):
            step = 1
            while i - step >= 0 and i + step < n and s[i - step] == s[i + step]:
                count += 1
                step += 1
            step = 1
            while i - step + 1 >= 0 and i + step < n and s[i - step + 1] == s[i + step]:
                count += 1
                step += 1
        return count + n

def test_run():
    a = Solution()
    b = a.countSubstrings('aaa')
    print(b)

if __name__ == "__main__":
    test_run()


class Nodo:
    def __init__(self, pregunta=None, animal=None):
        self.pregunta = pregunta
        self.animal = animal
        self.izquierda = None
        self.derecha = None
        self.padre = None

class JuegoAdivinar:
    def __init__(self):
        self.raiz = None
        self.actual = None

    def jugar(self):
        while True:
            print("Menú de Categorías:")
            print("1. Animales")
            print("2. Personajes")
            print("3. Flores")
            print("4. Salir")

            opcion = input("Digite opción: ")
            if opcion == "1":
                self.inicializar_animales()
                self.jugar_juego()
            elif opcion == "2":
                self.inicializar_personajes()
                self.jugar_juego()
            elif opcion == "3":
                self.inicializar_flores()
                self.jugar_juego()
            elif opcion == "4":
                break
            else:
                print("Opción inválida. Intente de nuevo.")

    def jugar_juego(self):
        self.actual = self.raiz

        while self.actual and self.actual.pregunta:
            respuesta = input(self.actual.pregunta + " (si/no): ")
            self.actual = self.actual.izquierda if respuesta.lower() == "si" else self.actual.derecha

        if self.actual is None:
            print("No tengo suficiente información para adivinar.")
            return

        if self.actual.animal:
            animal = input("Ya sé, es un(a) " + self.actual.animal + ". ¿He adivinado? (si/no): ")
            if animal.lower() == "no":
                diferencia = input("¿Cuál es tu Animal? ")
                pregunta = input("¿Qué diferencia a un(a) " + self.actual.animal + " de un(a) " + diferencia + "? ")

                nuevo_animal = Nodo(animal=diferencia)
                nueva_pregunta = Nodo(pregunta=pregunta)

                if respuesta.lower() == "si":
                    nueva_pregunta.izquierda = nuevo_animal
                    nueva_pregunta.derecha = self.actual
                else:
                    nueva_pregunta.derecha = nuevo_animal
                    nueva_pregunta.izquierda = self.actual

                if self.actual == self.raiz:
                    self.raiz = nueva_pregunta
                else:
                    padre = self.actual.padre
                    nueva_pregunta.padre = padre
                    if padre.izquierda == self.actual:
                        padre.izquierda = nueva_pregunta
                    else:
                        padre.derecha = nueva_pregunta

                nuevo_animal.padre = nueva_pregunta
                self.actual.padre = nueva_pregunta

            else:
                print("¡¡He adivinado!!")
        else:
            print("No tengo suficiente información para adivinar.")

    def inicializar_animales(self):
        self.raiz = Nodo(pregunta="¿Es mamífero?")
        self.actual = self.raiz

        self.raiz.izquierda = Nodo(pregunta="¿Tiene plumas?", animal="Pájaro")
        self.raiz.derecha = Nodo(pregunta="¿Tiene pelo?", animal="Perro")

        self.raiz.izquierda.izquierda = Nodo(animal="Avestruz")
        self.raiz.izquierda.derecha = Nodo(pregunta="¿Vuela?", animal="Murciélago")

        self.raiz.derecha.izquierda = Nodo(pregunta="¿Ronronea?", animal="Gato")
        self.raiz.derecha.derecha = Nodo(pregunta="¿Es acuático?", animal="Ballena")

    def inicializar_personajes(self):
        self.raiz = Nodo(pregunta="¿Es real?")
        self.actual = self.raiz

        self.raiz.izquierda = Nodo(pregunta="¿Tiene poderes?", animal="Superhéroe")
        self.raiz.derecha = Nodo(pregunta="¿Es de ficción?", animal="Personaje de libro")

        self.raiz.izquierda.izquierda = Nodo(animal="Superman")
        self.raiz.izquierda.derecha = Nodo(animal="Spiderman")

        self.raiz.derecha.izquierda = Nodo(animal="Harry Potter")
        self.raiz.derecha.derecha = Nodo(animal="Sherlock Holmes")

    def inicializar_flores(self):
        self.raiz = Nodo(pregunta="¿Tiene pétalos?")
        self.actual = self.raiz

        self.raiz.izquierda = Nodo(pregunta="¿Es una flor silvestre?", animal="Margarita")
        self.raiz.derecha = Nodo(pregunta="¿Se usa como decoración?", animal="Rosa")

        self.raiz.izquierda.izquierda = Nodo(animal="Orquídea")
        self.raiz.izquierda.derecha = Nodo(animal="Tulipán")

        self.raiz.derecha.izquierda = Nodo(animal="Lirio")
        self.raiz.derecha.derecha = Nodo(animal="Girasol")

# Crear instancia del juego y jugar
juego = JuegoAdivinar()
juego.jugar()


'''
Created on 13.02.2015

@author: Iris
'''
from Component.Component import Component
from Component.Container import container
from Util.Vector import Vector2
from Physics.Body import Body
from Physics.Damping import Damping
from Collision.AabbCollider import AabbCollider
from Component.LifeCycle import LifeCycle
from Component.PoseTransmitter import PoseTransmitter
from Component.OutOfBoundsPosInverter import OutOfBoundsPosInverter
from Entities.Bullet import Bullet

class SpaceshipLogic(Component):
    '''
    Spaceship Logic
    '''

    def __init__(self, pos, numLives = 3):
        Component.__init__(self)
        self._pos = pos
        self._numLives = numLives

    def activate(self):
        Component.activate(self)

        self._setParameters()

        self.parent.lives = self._numLives

        self.parent.onCollision = self._onCollision
        self.parent.loseLife = self._loseLife
        self.parent.shoot = self._shoot
        self.parent.reset = self._reset
        self.parent.destroy = self._destroy

        self._initSpaceship()
        container.get('updater').register(self._update)

    def deactivate(self):
        Component.deactivate(self)
        container.get('updater').deregister(self._update)
        del self.parent.lives

    def _setParameters(self):
        self._parameters = container.get('parameters')

        self._spaceshipHeight = self._parameters.getFloat('spaceship', 'height')
        self._spaceshipDamping = self._parameters.getFloat('spaceship', 'damping')
        self._spaceshipAngularDamping = self._parameters.getFloat('spaceship', 'angular_damping')
        self._spaceshipTorque = self._parameters.getFloat('spaceship', 'torque')
        self._spaceshipSpeed = self._parameters.getFloat('spaceship', 'speed')

        self._bulletWidth = self._parameters.getFloat('bullet', 'width')
        self._bulletHeight = self._parameters.getFloat('bullet', 'height')

    def _initSpaceship(self):
        self._prevButtonState = False
        self._shipComponents = {}

        self._shipComponents['body'] = Body(Vector2(self._pos))
        self._shipComponents['damping'] = Damping(
            self._spaceshipDamping,
            self._spaceshipAngularDamping
        )
        self._shipComponents['outOfBoundsPosInverter'] = OutOfBoundsPosInverter()
        self._shipComponents['collider'] = AabbCollider()
        self._shipComponents['lifeCycle'] = LifeCycle()
        self._shipComponents['poseTransmitter'] = PoseTransmitter()

        for _, component in self._shipComponents.iteritems():
            self.parent.add(component)

    def _destroy(self):
        for _, component in self._shipComponents.iteritems():
            self.parent.remove(component)

        self._shipComponents = {}

    def _reset(self):
        self._destroy()
        self._initSpaceship()

    def _onCollision(self, collider):
        if not hasattr(collider, 'tags'):
            return

        if 'spaceship' in collider.tags or 'asteroid' in collider.tags:
            self.parent.pos.setTo(self.parent.lastValidPos)
            self.parent.loseLife()

        if 'bullet' in collider.tags:
            if collider.spaceship == self.parent:
                return

            self.parent.loseLife()

    def _shoot(self):
        position = Vector2(self.parent.pos.x, self.parent.pos.y)
        tip = Vector2(0, self._spaceshipHeight * 0.5).rotated(self.parent.angle)

        bullet = Bullet(
            pos = Vector2(position + tip),
            angle = self.parent.angle,
            width = self._bulletWidth,
            height = self._bulletHeight,
            spaceship = self.parent
        )
        container.add(bullet)

    def _loseLife(self):
        self.parent.lives -= 1
        container.get('messenger').send('lost_life', self.parent)

    def _update(self):
        if not hasattr(self.parent, 'body'):
            return

        self._handleTorque()
        self._handleShooting()
        self._handleVelocity()

    def _handleTorque(self):
        self.parent.addTorque(-self.parent.axis[0] * self._spaceshipTorque)

    def _handleShooting(self):
        if self.parent.buttons[0] is True and self._prevButtonState is False:
            self.parent.shoot()

        # only shoot once for every key press
        self._prevButtonState = self.parent.buttons[0]

    def _handleVelocity(self):
        # only allow flying forward
        if self.parent.axis[1] < 0:
            self.parent.addForce(
                Vector2(0, -self.parent.axis[1] * self._spaceshipSpeed).rotated(self.parent.angle)
            )


import logging
from functools import wraps
from typing import Any, Callable, Optional, Union

import numpy as np
import xarray
from rioxarray.exceptions import MissingCRS

from sertit.logs import SU_NAME
from sertit.rasters_rio import (
    MAX_CORES,
    PATH_ARR_DS,
    bigtiff_value,
    get_window,
    path_arr_dst,
)
from sertit.types import AnyPathStrType, AnyPathType, AnyXrDataStructure

try:
    import geopandas as gpd
    import rasterio
    import rioxarray
    import xarray as xr
    from rasterio import features
    from rasterio.enums import Resampling
    from shapely.geometry import Polygon
except ModuleNotFoundError as ex:
    raise ModuleNotFoundError(
        "Please install 'rioxarray' and 'geopandas' to use the 'rasters' package."
    ) from ex

from sertit import geometry, path, rasters_rio, vectors

MAX_CORES = MAX_CORES
PATH_XARR_DS = Union[str, AnyXrDataStructure, rasterio.DatasetReader]
LOGGER = logging.getLogger(SU_NAME)

# NODATAs
UINT8_NODATA = rasters_rio.UINT8_NODATA
""" UINT8 nodata: 255 """

INT8_NODATA = rasters_rio.INT8_NODATA
""" INT8 nodata: -128 """

UINT16_NODATA = rasters_rio.UINT16_NODATA
""" UINT16 nodata: 65535 """

FLOAT_NODATA = rasters_rio.FLOAT_NODATA
""" FLOAT nodata: -9999 """

def get_nodata_value(dtype) -> int:
    """
    Get default nodata value:

    .. code-block:: python
            if dtype == np.uint8:
                nodata = UINT8_NODATA
            elif dtype == np.int8:
                nodata = INT8_NODATA
            elif dtype in [np.uint16, np.uint32, np.int32, np.int64, np.uint64, int]:
                nodata = UINT16_NODATA
            elif dtype in [np.int16, np.float32, np.float64, float]:
                nodata = FLOAT_NODATA

    Args:
        dtype: Dtype for the wanted nodata. Best if numpy's dtype.

    Returns:
        int: Nodata value
    """
    return rasters_rio.get_nodata_value(dtype)

def path_xarr_dst(function: Callable) -> Callable:
    """
    Path, :code:`xarray` or dataset decorator. Allows a function to ingest:

    - a path
    - a :code:`xarray`
    - a :code:`rasterio` dataset

    .. code-block:: python

        >>> # Create mock function
        >>> @path_or_dst
        >>> def fct(ds):
        >>>     read(ds)
        >>>
        >>> # Test the two ways
        >>> read1 = fct("path/to/raster.tif")
        >>> with rasterio.open("path/to/raster.tif") as ds:
        >>>     read2 = fct(ds)
        >>>
        >>> # Test
        >>> read1 == read2
        True
    Args:
        function (Callable): Function to decorate

    Returns:
        Callable: decorated function
    """

    @wraps(function)
    def path_or_xarr_or_dst_wrapper(path_or_ds: PATH_XARR_DS, *args, **kwargs) -> Any:
        """
        Path or dataset wrapper
        Args:
            path_or_ds (PATH_XARR_DS): Raster path or its dataset
            *args: args
            **kwargs: kwargs

        Returns:
            Any: regular output
        """
        if isinstance(path_or_ds, xr.DataArray):
            out = function(path_or_ds, *args, **kwargs)
        elif isinstance(path_or_ds, xr.Dataset):
            # Try on the whole dataset
            try:
                out = function(path_or_ds, *args, **kwargs)
            except Exception:
                # Try on every dataarray
                try:
                    xds_dict = {}
                    convert_to_xdataset = False
                    for var in path_or_ds.data_vars:
                        xds_dict[var] = function(path_or_ds[var], *args, **kwargs)
                        if isinstance(xds_dict[var], xr.DataArray):
                            convert_to_xdataset = True

                    # Convert in dataset if we have dataarrays, else keep the dict
                    if convert_to_xdataset:
                        xds = xr.Dataset(xds_dict)
                    else:
                        xds = xds_dict
                    return xds
                except Exception as ex:
                    raise TypeError("Function not available for xarray.Dataset") from ex
        else:
            # Get name
            if path.is_path(path_or_ds):
                name = str(path_or_ds)
                path_or_ds = str(path_or_ds)
            else:
                name = path_or_ds.name

            with rioxarray.open_rasterio(
                path_or_ds,
                masked=True,
                default_name=name,
                chunks=kwargs.pop("chunks", True),
            ) as xds:
                out = function(xds, *args, **kwargs)
        return out

    return path_or_xarr_or_dst_wrapper

def get_nodata_mask(xds: AnyXrDataStructure) -> np.ndarray:
    """
    Get nodata mask from a xarray.

    .. code-block:: python

        >>> diag_arr = xr.DataArray(data=np.diag([1, 2, 3]))
        >>> diag_arr.rio.write_nodata(0, inplace=True)
        <xarray.DataArray (dim_0: 3, dim_1: 3)>
        array([[1, 0, 0],
               [0, 2, 0],
               [0, 0, 3]])
        Dimensions without coordinates: dim_0, dim_1

        >>> get_nodata_mask(diag_arr)
        array([[1, 0, 0],
               [0, 1, 0],
               [0, 0, 1]], dtype=uint8)

    Args:
        xds (AnyXrDataStructure): Array to evaluate

    Returns:
        np.ndarray: Pixelwise nodata array

    """

    nodata = xds.rio.nodata

    try:
        is_nan = np.isnan(nodata)
    except TypeError:
        is_nan = False

    if is_nan:
        nodata_pos = np.isnan(xds.data)
    else:
        nodata_pos = xds.data == nodata

    return np.where(nodata_pos, 0, 1).astype(np.uint8)

@path_xarr_dst
def rasterize(
    xds: PATH_XARR_DS,
    vector: Union[gpd.GeoDataFrame, AnyPathStrType],
    value_field: str = None,
    default_nodata: int = 0,
    **kwargs,
) -> AnyXrDataStructure:
    """
    Rasterize a vector into raster format.

    Note that passing `merge_alg = MergeAlg.add` will add the vector values to the given a raster

    See: https://pygis.io/docs/e_raster_rasterize.html

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        vector (Union[gpd.GeoDataFrame, AnyPathStrType]): Vector to be rasterized
        value_field (str): Field of the vector with the values to be burnt on the raster (should be scalars). If let to None, the raster will be binary.
        default_nodata (int): Default nodata of the raster (outside the vector in the raster extent)

    Returns:
        AnyXrDataStructure: Rasterized vector
    """
    # Use classic option
    arr, meta = rasters_rio.rasterize(
        xds, vector, value_field, default_nodata, **kwargs
    )
    if len(arr.shape) != 3:
        arr = np.expand_dims(arr, axis=0)

    # Change nodata
    rasterized_xds = xds.copy(data=arr)
    rasterized_xds = set_nodata(rasterized_xds, nodata_val=meta["nodata"])
    return rasterized_xds

@path_xarr_dst
def _vectorize(
    xds: PATH_XARR_DS,
    values: Union[None, int, list] = None,
    keep_values: bool = True,
    dissolve: bool = False,
    get_nodata: bool = False,
    default_nodata: int = 0,
) -> gpd.GeoDataFrame:
    """
    Vectorize a xarray, both to get classes or nodata.

    If dissolved is False, it returns a GeoDataFrame with a GeoSeries per cluster of pixel value,
    with the value as an attribute. Else it returns a GeoDataFrame with a unique polygon.

    .. WARNING::
        - If :code:`get_nodata` is set to False:
            - Your data is casted by force into np.uint8, so be sure that your data is classified.
            - This could take a while as the computing time directly depends on the number of polygons to vectorize.
                Please be careful.
    Else:
        - You will get a classified polygon with data (value=0)/nodata pixels. To

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        values (Union[None, int, list]): Get only the polygons concerning this/these particular values
        keep_values (bool): Keep the passed values. If False, discard them and keep the others.
        dissolve (bool): Dissolve all the polygons into one unique. Only works if values are given.
        get_nodata (bool): Get nodata vector (raster values are set to 0, nodata values are the other ones)
        default_nodata (int): Default values for nodata in case of non existing in file
    Returns:
        gpd.GeoDataFrame: Vector with the raster values (if dissolve is not set)
    """
    # Manage nodata value
    uint8_nodata = 255
    if xds.rio.encoded_nodata is not None:
        nodata = uint8_nodata
    else:
        nodata = default_nodata

    if get_nodata:
        data = get_nodata_mask(xds)
        nodata_arr = None
    else:
        xds_uint8 = xds.fillna(uint8_nodata)
        data = xds_uint8.data.astype(np.uint8)

        # Manage values
        if values is not None:
            if not isinstance(values, list):
                values = [values]

            # If we want a dissolved vector, just set 1instead of real values
            arr_vals = 1 if dissolve else data
            if keep_values:
                true = arr_vals
                false = nodata
            else:
                true = nodata
                false = arr_vals

            # Update data array
            data = np.where(np.isin(data, values), true, false).astype(np.uint8)

        # Get nodata array
        nodata_arr = rasters_rio.get_nodata_mask(
            data, has_nodata=False, default_nodata=nodata
        )

        if data.dtype != np.uint8:
            raise TypeError("Your data should be classified (np.uint8).")

    # WARNING: features.shapes do NOT accept dask arrays !
    if not isinstance(data, (np.ndarray, np.ma.masked_array)):
        data = data.compute()
    if nodata_arr is not None and not isinstance(
        nodata_arr, (np.ndarray, np.ma.masked_array)
    ):
        nodata_arr = nodata_arr.compute()

    # Get shapes (on array or on mask to get nodata vector)
    shapes = features.shapes(data, mask=nodata_arr, transform=xds.rio.transform())

    # Convert to geodataframe
    gdf = vectors.shapes_to_gdf(shapes, xds.rio.crs)

    # Return valid geometries
    gdf = geometry.make_valid(gdf)

    # Dissolve if needed
    if dissolve:
        gdf = gpd.GeoDataFrame(geometry=gdf.geometry, crs=gdf.crs).dissolve()

    return gdf

@path_xarr_dst
def vectorize(
    xds: PATH_XARR_DS,
    values: Union[None, int, list] = None,
    keep_values: bool = True,
    dissolve: bool = False,
    default_nodata: int = 0,
) -> gpd.GeoDataFrame:
    """
    Vectorize a :code:`xarray` to get the class vectors.

    If dissolved is False, it returns a GeoDataFrame with a GeoSeries per cluster of pixel value,
    with the value as an attribute. Else it returns a GeoDataFrame with a unique polygon.

    .. WARNING::
        - Your data is casted by force into np.uint8, so be sure that your data is classified.
        - This could take a while as the computing time directly depends on the number of polygons to vectorize.
        Please be careful.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"
        >>> vec1 = vectorize(raster_path)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     vec2 = vectorize(ds)
        >>> vec1 == vec2
        True

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        values (Union[None, int, list]): Get only the polygons concerning this/these particular values
        keep_values (bool): Keep the passed values. If False, discard them and keep the others.
        dissolve (bool): Dissolve all the polygons into one unique. Only works if values are given.
        default_nodata (int): Default values for nodata in case of non existing in file
    Returns:
        gpd.GeoDataFrame: Classes Vector
    """
    return _vectorize(
        xds,
        values=values,
        keep_values=keep_values,
        dissolve=dissolve,
        get_nodata=False,
        default_nodata=default_nodata,
    )

@path_xarr_dst
def get_valid_vector(xds: PATH_XARR_DS, default_nodata: int = 0) -> gpd.GeoDataFrame:
    """
    Get the valid data of a raster as a vector.

    Pay attention that every nodata pixel will appear too.
    If you want only the footprint of the raster, please use :code:`get_footprint`.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"
        >>> nodata1 = get_nodata_vec(raster_path)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     nodata2 = get_nodata_vec(ds)
        >>> nodata1 == nodata2
        True

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        default_nodata (int): Default values for nodata in case of non existing in file
    Returns:
        gpd.GeoDataFrame: Nodata Vector

    """
    nodata = _vectorize(
        xds, values=None, get_nodata=True, default_nodata=default_nodata
    )
    return nodata[
        nodata.raster_val != 0
    ]  # 0 is the values of not nodata put there by rasterio

@path_xarr_dst
def get_nodata_vector(ds: PATH_ARR_DS, default_nodata: int = 0) -> gpd.GeoDataFrame:
    """
    Get the nodata vector of a raster as a vector.

    Pay attention that every nodata pixel will appear too.
    If you want only the footprint of the raster, please use :code:`get_footprint`.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"  # Classified raster, with no data set to 255
        >>> nodata1 = get_nodata_vec(raster_path)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     nodata2 = get_nodata_vec(ds)
        >>> nodata1 == nodata2
        True

    Args:
        ds (PATH_ARR_DS): Path to the raster, its dataset, its :code:`xarray` or a tuple containing its array and metadata
        default_nodata (int): Default values for nodata in case of non existing in file
    Returns:
        gpd.GeoDataFrame: Nodata Vector

    """
    nodata = _vectorize(ds, values=None, get_nodata=True, default_nodata=default_nodata)
    return nodata[nodata.raster_val == 0]

@path_xarr_dst
def mask(
    xds: PATH_XARR_DS,
    shapes: Union[gpd.GeoDataFrame, Polygon, list],
    nodata: Optional[int] = None,
    **kwargs,
) -> AnyXrDataStructure:
    """
    Masking a dataset:
    setting nodata outside of the given shapes, but without cropping the raster to the shapes extent.

    The original nodata is kept and completed with the nodata provided by the shapes.

    Overload of rasterio mask function in order to create a :code:`xarray`.

    The :code:`mask` function docs can be seen `here <https://rasterio.readthedocs.io/en/latest/api/rasterio.mask.html>`_.
    It basically masks a raster with a vector mask, with the possibility to crop the raster to the vector's extent.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"
        >>> shape_path = "path/to/shapes.geojson"  # Any vector that geopandas can read
        >>> shapes = gpd.read_file(shape_path)
        >>> mask1 = mask(raster_path, shapes)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     mask2 = mask(ds, shapes)
        >>> mask1 == mask2
        True

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        shapes (Union[gpd.GeoDataFrame, Polygon, list]): Shapes with the same CRS as the dataset
            (except if a :code:`GeoDataFrame` is passed, in which case it will automatically be converted)
        nodata (int): Nodata value. If not set, uses the ds.nodata. If doesnt exist, set to 0.
        **kwargs: Other rasterio.mask options

    Returns:
         AnyXrDataStructure: Masked array as a xarray
    """
    # Use classic option
    arr, meta = rasters_rio.mask(xds, shapes=shapes, nodata=nodata, **kwargs)

    masked_xds = xds.copy(data=arr)

    if nodata:
        masked_xds = set_nodata(masked_xds, nodata)

    # Convert back to xarray
    return masked_xds

@path_xarr_dst
def paint(
    xds: PATH_XARR_DS,
    shapes: Union[gpd.GeoDataFrame, Polygon, list],
    value: int,
    invert: bool = False,
    **kwargs,
) -> AnyXrDataStructure:
    """
    Painting a dataset: setting values inside the given shapes. To set outside the shape, set invert=True.
    Pay attention that this behavior is the opposite of the :code:`rasterio.mask` function.

    The original nodata is kept.
    This means if your shapes intersects the original nodata,
    the value of the pixel will be set to nodata rather than to the wanted value.

    Overload of rasterio mask function in order to create a :code:`xarray`.
    The :code:`mask` function docs can be seen `here <https://rasterio.readthedocs.io/en/latest/api/rasterio.mask.html>`_.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"
        >>> shape_path = "path/to/shapes.geojson"  # Any vector that geopandas can read
        >>> shapes = gpd.read_file(shape_path)
        >>> paint1 = paint(raster_path, shapes, value=100)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     paint2 = paint(ds, shapes, value=100)
        >>> paint1 == paint2
        True

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        shapes (Union[gpd.GeoDataFrame, Polygon, list]): Shapes with the same CRS as the dataset
            (except if a :code:`GeoDataFrame` is passed, in which case it will automatically be converted)
        value (int): Value to set on the shapes.
        invert (bool): If invert is True, set value outside the shapes.
        **kwargs: Other rasterio.mask options

    Returns:
         AnyXrDataStructure: Painted array as a xarray
    """
    # Fill na values in order to not interfere with the mask function
    if xds.rio.encoded_nodata is not None:
        xds_fill = xds.fillna(xds.rio.encoded_nodata)
    elif xds.rio.nodata is not None:
        xds_fill = xds.fillna(xds.rio.nodata)
    else:
        xds_fill = xds

    # Use classic option
    arr, meta = rasters_rio.mask(
        xds_fill, shapes=shapes, nodata=value, invert=not invert, **kwargs
    )

    # Create and fill na values created by the mask to the wanted value
    painted_xds = xds.copy(data=arr)
    painted_xds = painted_xds.fillna(value)

    # Keep all attrs after fillna
    painted_xds.rio.update_attrs(xds.attrs, inplace=True)
    painted_xds.rio.update_encoding(xds.encoding, inplace=True)

    # Set back nodata to keep the original nodata
    if xds.rio.encoded_nodata is not None:
        painted_xds = set_nodata(painted_xds, xds.rio.encoded_nodata)

    # Convert back to xarray
    return painted_xds

@path_xarr_dst
def crop(
    xds: PATH_XARR_DS,
    shapes: Union[gpd.GeoDataFrame, Polygon, list],
    nodata: Optional[int] = None,
    **kwargs,
) -> (np.ma.masked_array, dict):
    """
    Cropping a dataset:
    setting nodata outside the given shapes AND cropping the raster to the shapes extent.

    Overload of
    `rioxarray.clip <https://corteva.github.io/rioxarray/stable/rioxarray.html#rioxarray.raster_array.RasterArray.clip>`_
    function in order to create a masked_array.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"
        >>> shape_path = "path/to/shapes.geojson"  # Any vector that geopandas can read
        >>> shapes = gpd.read_file(shape_path)
        >>> xds2 = crop(raster_path, shapes)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     xds2 = crop(ds, shapes)
        >>> xds1 == xds2
        True

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        shapes (Union[gpd.GeoDataFrame, Polygon, list]): Shapes with the same CRS as the dataset
            (except if a :code:`GeoDataFrame` is passed, in which case it will automatically be converted)
        nodata (int): Nodata value. If not set, uses the ds.nodata. If doesnt exist, set to 0.
        **kwargs: Other rioxarray.clip options

    Returns:
         AnyXrDataStructure: Cropped array as a xarray
    """
    if nodata:
        xds = set_nodata(xds, nodata)

    if isinstance(shapes, (gpd.GeoDataFrame, gpd.GeoSeries)):
        shapes = shapes.to_crs(xds.rio.crs).geometry

    if "from_disk" not in kwargs:
        kwargs["from_disk"] = True  # WAY FASTER

    # Clip keeps encoding and attrs
    return xds.rio.clip(shapes, **kwargs)

@path_arr_dst
def read(
    ds: PATH_ARR_DS,
    resolution: Union[tuple, list, float] = None,
    size: Union[tuple, list] = None,
    window: Any = None,
    resampling: Resampling = Resampling.nearest,
    masked: bool = True,
    indexes: Union[int, list] = None,
    chunks: Union[int, tuple, dict] = None,
    as_type: Any = None,
    **kwargs,
) -> AnyXrDataStructure:
    """
    Read a raster dataset from a :

    - :code:`xarray` (compatibility issues)
    - :code:`rasterio.Dataset`
    - :code:`rasterio` opened data (array, metadata)
    - a path.

    The resolution can be provided (in dataset unit) as:

    - a tuple or a list of (X, Y) resolutions
    - a float, in which case X resolution = Y resolution
    - None, in which case the dataset resolution will be used

    Uses `rioxarray.open_rasterio <https://corteva.github.io/rioxarray/stable/rioxarray.html#rioxarray-open-rasterio>`_.
    For Dask usage, you can look at the
    `rioxarray tutorial <https://corteva.github.io/rioxarray/stable/examples/dask_read_write.html>`_.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"
        >>> xds1 = read(raster_path)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>    xds2 = read(ds)
        >>> xds1 == xds2
        True

    Args:
        ds (PATH_ARR_DS): Path to the raster or a rasterio dataset or a xarray
        resolution (Union[tuple, list, float]): Resolution of the wanted band, in dataset resolution unit (X, Y)
        size (Union[tuple, list]): Size of the array (width, height). Not used if resolution is provided.
        window (Any): Anything that can be returned as a window (i.e. path, gpd.GeoPandas, Iterable, rasterio.Window...).
            In case of an iterable, assumption is made it corresponds to geographic bounds.
            For pixel, please provide a rasterio.Window directly.
        resampling (Resampling): Resampling method
        masked (bool): Get a masked array
        indexes (Union[int, list]): Indexes of the band to load. Load the whole array if None. Starts at 1 like GDAL.
        chunks (int, tuple or dict): Chunk sizes along each dimension, e.g., 5, (5, 5) or {'x': 5, 'y': 5}.
            If chunks is provided, it used to load the new DataArray into a dask array.
            Chunks can also be set to True or "auto" to choose sensible chunk sizes
            according to dask.config.get("array.chunk-size").
        as_type (Any): Type in which to load the array
        **kwargs: Optional keyword arguments to pass into rioxarray.open_rasterio().
    Returns:
        Union[AnyXrDataStructure]: Masked xarray corresponding to the raster data and its meta data

    """
    if window is not None:
        window = get_window(ds, window)

    # Get new height and width
    new_height, new_width, do_resampling = rasters_rio.get_new_shape(
        ds, resolution, size, window
    )

    # Read data (and load it to discard lock)
    with xarray.set_options(keep_attrs=True):
        with rioxarray.set_options(export_grid_mapping=False):
            with rioxarray.open_rasterio(
                ds, default_name=path.get_filename(ds.name), chunks=chunks, **kwargs
            ) as xda:
                orig_dtype = xda.dtype

                # Windows
                if window is not None:
                    xda = xda.rio.isel_window(window).load()

                # Indexes
                if indexes is not None:
                    if not isinstance(indexes, list):
                        indexes = [indexes]

                    # Open only wanted bands
                    if 0 in indexes:
                        raise ValueError("Indexes should start at 1.")

                    ok_indexes = np.isin(indexes, xda.band)
                    if any(~ok_indexes):
                        LOGGER.warning(
                            f"Non available index: {[idx for i, idx in enumerate(indexes) if not ok_indexes[i]]} for {ds.name}"
                        )

                    xda = xda.isel(
                        band=[idx - 1 for ok, idx in zip(ok_indexes, indexes) if ok]
                    )

                    try:
                        # Set new long name: Bands nb are idx + 1
                        xda.long_name = tuple(
                            name
                            for i, name in enumerate(xda.long_name)
                            if i + 1 in indexes
                        )
                    except AttributeError:
                        pass

                # Manage resampling
                if do_resampling:
                    factor_h = xda.rio.height / new_height
                    factor_w = xda.rio.width / new_width

                    # Manage 2 ways of resampling, coarsen being faster than reprojection
                    # TODO: find a way to match rasterio's speed
                    if factor_h.is_integer() and factor_w.is_integer():
                        xda = xda.coarsen(x=int(factor_w), y=int(factor_h)).mean()
                    else:
                        xda = xda.rio.reproject(
                            xda.rio.crs,
                            shape=(new_height, new_width),
                            resampling=resampling,
                        )

                # Convert to wanted type
                if as_type:
                    # Modify the type as wanted by the user
                    # TODO: manage nodata and uint/int numbers
                    xda = xda.astype(as_type)

                # Mask if necessary
                if masked:
                    # Set nodata not in opening due to some performance issues
                    xda = set_nodata(xda, ds.meta["nodata"])

                # Set original dtype
                xda.encoding["dtype"] = orig_dtype

    return xda

@path_xarr_dst
def write(
    xds: AnyXrDataStructure, path: AnyPathStrType, tags: dict = None, **kwargs
) -> None:
    """
    Write raster to disk.
    (encapsulation of :code:`rasterio`'s function, because for now :code:`rioxarray` to_raster doesn't work as expected)

    Metadata will be created with the :code:`xarray` metadata (ie. width, height, count, type...)
    The driver is :code:`GTiff` by default, and no nodata value is provided.
    The file will be compressed if the raster is a mask (saved as uint8).

    If not overwritten, sets the nodata according to :code:`dtype`:

    - uint8: 255
    - int8: -128
    - uint16, uint32, int32, int64, uint64: 65535
    - int16, float32, float64, float128, float: -9999

    Compress with :code:`LZW` option by default. To disable it, add the :code:`compress=None` parameter.

    .. code-block:: python
        >>> raster_path = "path/to/raster.tif"
        >>> raster_out = "path/to/out.tif"
        >>> # Read raster
        >>> xds = read(raster_path)
        >>> # Rewrite it
        >>> write(xds, raster_out)

    Args:
        xds (AnyXrDataStructure): Path to the raster or a rasterio dataset or a xarray
        path (AnyPathStrType): Path where to save it (directories should be existing)
        **kwargs: Overloading metadata, ie :code:`nodata=255` or :code:`dtype=np.uint8`
    """
    # Manage dtype
    if "dtype" in kwargs:
        dtype = kwargs["dtype"]
    else:
        dtype = xds.dtype

    if isinstance(dtype, str):
        # Convert to numpy dtype
        dtype = getattr(np, dtype)
    xds.encoding["dtype"] = dtype

    # Write nodata
    xds.rio.write_nodata(
        kwargs.pop("nodata", get_nodata_value(dtype)), encoded=True, inplace=True
    )

    # WORKAROUND: Pop _FillValue attribute (if existing)
    if "_FillValue" in xds.attrs:
        xds.attrs.pop("_FillValue")

    # Default compression to LZW
    if "compress" not in kwargs:
        kwargs["compress"] = "lzw"

    if (
        kwargs["compress"].lower() in ["lzw", "deflate", "zstd"]
        and "predictor" not in kwargs  # noqa: W503
    ):
        if xds.encoding["dtype"] in [np.float32, np.float64, float]:
            kwargs["predictor"] = "3"
        else:
            kwargs["predictor"] = "2"

    # Bigtiff if needed
    bigtiff = bigtiff_value(xds)

    # Manage tiles
    if "tiled" not in kwargs:
        kwargs["tiled"] = True

    # Force GTiff
    kwargs["driver"] = kwargs.get("driver", "GTiff")

    # Write on disk
    xds.rio.to_raster(
        str(path), BIGTIFF=bigtiff, NUM_THREADS=MAX_CORES, tags=tags, **kwargs
    )

def collocate(
    reference: AnyXrDataStructure,
    other: AnyXrDataStructure,
    resampling: Resampling = Resampling.nearest,
) -> AnyXrDataStructure:
    """
    Collocate two georeferenced arrays:
    forces the *other* raster to be exactly georeferenced onto the *reference* raster by reprojection.

    .. code-block:: python

        >>> reference_path = "path/to/reference.tif"
        >>> other_path = "path/to/other.tif"
        >>> col_path = "path/to/collocated.tif"

        >>> # Collocate the other to the reference
        >>> col_xds = collocate(read(reference_path), read(other_path), Resampling.bilinear)

        >>> # Write it
        >>> write(col_xds, col_path)

    Args:
        reference (AnyXrDataStructure): Reference xarray
        other (AnyXrDataStructure): Other xarray
        resampling (Resampling): Resampling method

    Returns:
        AnyXrDataStructure: Collocated xarray

    """
    if isinstance(other, xr.DataArray):
        old_dtype = other.dtype
        collocated_xds = (
            other.astype(reference.dtype)
            .rio.reproject_match(reference, resampling=resampling)
            .astype(old_dtype)
        )
    else:
        collocated_xds = other.rio.reproject_match(reference, resampling=resampling)

    # Bug for now, tiny difference in coords
    collocated_xds = collocated_xds.assign_coords(
        {
            "x": reference.x,
            "y": reference.y,
        }
    )

    # Set back attributes and encoding
    collocated_xds.rio.update_attrs(other.attrs, inplace=True)
    collocated_xds.rio.update_encoding(other.encoding, inplace=True)

    return collocated_xds

@path_xarr_dst
def sieve(
    xds: PATH_XARR_DS, sieve_thresh: int, connectivity: int = 4
) -> AnyXrDataStructure:
    """
    Sieving, overloads rasterio function with raster shaped like (1, h, w).

    .. WARNING::
        Your data is casted by force into :code:`np.uint8`, so be sure that your data is classified.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"  # classified raster

        >>> # Rewrite it
        >>> sieved_xds = sieve(raster_path, sieve_thresh=20)

        >>> # Write it
        >>> raster_out = "path/to/raster_sieved.tif"
        >>> write(sieved_xds, raster_out)

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
        sieve_thresh (int): Sieving threshold in pixels
        connectivity (int): Connectivity, either 4 or 8

    Returns:
        (AnyXrDataStructure): Sieved xarray
    """
    assert connectivity in [4, 8]

    # Use this trick to make the sieve work
    mask = np.squeeze(np.where(np.isnan(xds.data), 0, 1).astype(np.uint8))
    data = np.squeeze(xds.data.astype(np.uint8))

    # Sieve
    try:
        sieved_arr = features.sieve(
            data, size=sieve_thresh, connectivity=connectivity, mask=mask
        )
    except TypeError:
        # Manage dask arrays that fails with rasterio sieve
        sieved_arr = features.sieve(
            data.compute(),
            size=sieve_thresh,
            connectivity=connectivity,
            mask=mask.compute(),
        )

    # Set back nodata and expand back dim
    sieved_arr = sieved_arr.astype(xds.dtype)
    sieved_arr[np.isnan(np.squeeze(xds.data))] = np.nan
    sieved_arr = np.expand_dims(sieved_arr, axis=0)
    sieved_xds = xds.copy(data=sieved_arr)

    return sieved_xds

def get_dim_img_path(dim_path: AnyPathStrType, img_name: str = "*") -> AnyPathType:
    """
    Get the image path from a :code:`BEAM-DIMAP` data.

    A :code:`BEAM-DIMAP` file cannot be opened by rasterio, although its :code:`.img` file can.

    .. code-block:: python

        >>> dim_path = "path/to/dimap.dim"  # BEAM-DIMAP image
        >>> img_path = get_dim_img_path(dim_path)

        >>> # Read raster
        >>> raster, meta = read(img_path)

    Args:
        dim_path (AnyPathStrType): DIM path (.dim or .data)
        img_name (str): .img file name (or regex), in case there are multiple .img files (ie. for S3 data)

    Returns:
        AnyPathType: .img file
    """
    return rasters_rio.get_dim_img_path(dim_path, img_name)

@path_xarr_dst
def get_extent(xds: PATH_XARR_DS) -> gpd.GeoDataFrame:
    """
    Get the extent of a raster as a :code:`geopandas.Geodataframe`.

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"

        >>> extent1 = get_extent(raster_path)
        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     extent2 = get_extent(ds)
        >>> extent1 == extent2
        True

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray

    Returns:
        gpd.GeoDataFrame: Extent as a :code:`geopandas.Geodataframe`
    """
    return vectors.get_geodf(geom=[*xds.rio.bounds()], crs=xds.rio.crs)

@path_xarr_dst
def get_footprint(xds: PATH_XARR_DS) -> gpd.GeoDataFrame:
    """
    Get real footprint of the product (without nodata, in french == emprise utile)

    .. code-block:: python

        >>> raster_path = "path/to/raster.tif"

        >>> footprint1 = get_footprint(raster_path)

        >>> # or
        >>> with rasterio.open(raster_path) as ds:
        >>>     footprint2 = get_footprint(ds)
        >>> footprint1 == footprint2

    Args:
        xds (PATH_XARR_DS): Path to the raster or a rasterio dataset or a xarray
    Returns:
        gpd.GeoDataFrame: Footprint as a GeoDataFrame
    """
    footprint = get_valid_vector(xds)
    return geometry.get_wider_exterior(footprint)

def merge_vrt(
    crs_paths: list,
    crs_merged_path: AnyPathStrType,
    abs_path: bool = False,
    **kwargs,
) -> None:
    """
    Merge rasters as a VRT. Uses :code:`gdalbuildvrt`.

    See here: https://gdal.org/programs/gdalbuildvrt.html

    Creates VRT with relative paths !

    .. WARNING::
        They should have the same CRS otherwise the mosaic will be false !

    .. code-block:: python

        >>> paths_utm32630 = ["path/to/raster1.tif", "path/to/raster2.tif", "path/to/raster3.tif"]
        >>> paths_utm32631 = ["path/to/raster4.tif", "path/to/raster5.tif"]

        >>> mosaic_32630 = "path/to/mosaic_32630.vrt"
        >>> mosaic_32631 = "path/to/mosaic_32631.vrt"

        >>> # Create mosaic, one by CRS !
        >>> merge_vrt(paths_utm32630, mosaic_32630)
        >>> merge_vrt(paths_utm32631, mosaic_32631, {"-srcnodata":255, "-vrtnodata":0})

    Args:
        crs_paths (list): Path of the rasters to be merged with the same CRS
        crs_merged_path (AnyPathStrType): Path to the merged raster
        abs_path (bool): VRT with absolute paths. If not, VRT with relative paths (default)
        kwargs: Other gdlabuildvrt arguments
    """
    return rasters_rio.merge_vrt(crs_paths, crs_merged_path, abs_path, **kwargs)

def merge_gtiff(crs_paths: list, crs_merged_path: AnyPathStrType, **kwargs) -> None:
    """
    Merge rasters as a GeoTiff.

    .. WARNING::
        They should have the same CRS otherwise the mosaic will be false !

    .. code-block:: python

        >>> paths_utm32630 = ["path/to/raster1.tif", "path/to/raster2.tif", "path/to/raster3.tif"]
        >>> paths_utm32631 = ["path/to/raster4.tif", "path/to/raster5.tif"]

        >>> mosaic_32630 = "path/to/mosaic_32630.tif"
        >>> mosaic_32631 = "path/to/mosaic_32631.tif"

        # Create mosaic, one by CRS !
        >>> merge_gtiff(paths_utm32630, mosaic_32630)
        >>> merge_gtiff(paths_utm32631, mosaic_32631)

    Args:
        crs_paths (list): Path of the rasters to be merged with the same CRS
        crs_merged_path (AnyPathStrType): Path to the merged raster
        kwargs: Other rasterio.merge arguments
            More info `here <https://rasterio.readthedocs.io/en/latest/api/rasterio.merge.html#rasterio.merge.merge>_
    """
    return rasters_rio.merge_gtiff(crs_paths, crs_merged_path, **kwargs)

def unpackbits(array: np.ndarray, nof_bits: int) -> np.ndarray:
    """
    Function found
    `here <https://stackoverflow.com/questions/18296035/how-to-extract-the-bits-of-larger-numeric-numpy-data-types>`_

    .. code-block:: python

        >>> bit_array = np.random.randint(5, size=[3,3])
        array([[1, 1, 3],
               [4, 2, 0],
               [4, 3, 2]], dtype=uint8)

        # Unpack 8 bits (8*1, as itemsize of uint8 is 1)
        >>> unpackbits(bit_array, 8)
        array([[[1, 0, 0, 0, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [1, 1, 0, 0, 0, 0, 0, 0]],
               [[0, 0, 1, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0]],
               [[0, 0, 1, 0, 0, 0, 0, 0],
                [1, 1, 0, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0]]], dtype=uint8)
    Args:
        array (np.ndarray): Array to unpack
        nof_bits (int): Number of bits to unpack

    Returns:
        np.ndarray: Unpacked array
    """
    return rasters_rio.unpackbits(array, nof_bits)

def read_bit_array(
    bit_mask: Union[xr.DataArray, np.ndarray], bit_id: Union[list, int]
) -> Union[np.ndarray, list]:
    """
    Read bit arrays as a succession of binary masks (sort of read a slice of the bit mask, slice number bit_id)

    .. code-block:: python

        >>> bit_array = np.random.randint(5, size=[3,3])
        array([[1, 1, 3],
               [4, 2, 0],
               [4, 3, 2]], dtype=uint8)

        # Get the 2nd bit array
        >>> read_bit_array(bit_array, 2)
        array([[0, 0, 0],
               [1, 0, 0],
               [1, 0, 0]], dtype=uint8)

    Args:
        bit_mask (np.ndarray): Bit array to read
        bit_id (int): Bit ID of the slice to be read
          Example: read the bit 0 of the mask as a cloud mask (Theia)

    Returns:
        Union[np.ndarray, list]: Binary mask or list of binary masks if a list of bit_id is given
    """
    if isinstance(bit_mask, xr.DataArray):
        bit_mask = bit_mask.data

    return rasters_rio.read_bit_array(bit_mask, bit_id)

def read_uint8_array(
    bit_mask: Union[xr.DataArray, np.ndarray], bit_id: Union[list, int]
) -> Union[np.ndarray, list]:
    """
    Read 8 bit arrays as a succession of binary masks.

    Forces array to :code:`np.uint8`.

    See :code:`read_bit_array`.

    Args:
        bit_mask (np.ndarray): Bit array to read
        bit_id (int): Bit ID of the slice to be read
          Example: read the bit 0 of the mask as a cloud mask (Theia)

    Returns:
        Union[np.ndarray, list]: Binary mask or list of binary masks if a list of bit_id is given
    """
    return read_bit_array(bit_mask.astype(np.uint8), bit_id)

def set_metadata(
    naked_xda: xr.DataArray, mtd_xda: xr.DataArray, new_name=None
) -> xr.DataArray:
    """
    Set metadata from a :code:`xr.DataArray` to another (including :code:`rioxarray` metadata such as encoded_nodata and crs).

    Useful when performing operations on xarray that result in metadata loss such as sums.

    .. code-block:: python

        >>> # xda: some xr.DataArray
        >>> sum = xda + xda  # Sum loses its metadata here
        <xarray.DataArray 'xda' (band: 1, y: 322, x: 464)>
        array([[[nan, nan, nan, ..., nan, nan, nan],
                [nan, nan, nan, ..., nan, nan, nan],
                [nan, nan, nan, ..., nan, nan, nan],
                ...,
                [nan, nan, nan, ...,  2., nan, nan],
                [nan, nan, nan, ...,  2., nan, nan],
                [nan, nan, nan, ...,  2., nan, nan]]])
        Coordinates:
          * band         (band) int32 1
          * y            (y) float64 4.798e+06 4.798e+06 ... 4.788e+06 4.788e+06
          * x            (x) float64 5.411e+05 5.411e+05 ... 5.549e+05 5.55e+05

        >>> # We need to set the metadata back (and we can set a new name)
        >>> sum = set_metadata(sum, xda, new_name="sum")
        <xarray.DataArray 'sum' (band: 1, y: 322, x: 464)>
        array([[[nan, nan, nan, ..., nan, nan, nan],
                [nan, nan, nan, ..., nan, nan, nan],
                [nan, nan, nan, ..., nan, nan, nan],
                ...,
                [nan, nan, nan, ...,  2., nan, nan],
                [nan, nan, nan, ...,  2., nan, nan],
                [nan, nan, nan, ...,  2., nan, nan]]])
        Coordinates:
          * band         (band) int32 1
          * y            (y) float64 4.798e+06 4.798e+06 ... 4.788e+06 4.788e+06
          * x            (x) float64 5.411e+05 5.411e+05 ... 5.549e+05 5.55e+05
            spatial_ref  int32 0
        Attributes: (12/13)
            grid_mapping:              spatial_ref
            BandName:                  Band_1
            RepresentationType:        ATHEMATIC
            STATISTICS_COVARIANCES:    0.2358157950609785
            STATISTICS_MAXIMUM:        2
            STATISTICS_MEAN:           1.3808942647686
            ...                        ...
            STATISTICS_SKIPFACTORX:    1
            STATISTICS_SKIPFACTORY:    1
            STATISTICS_STDDEV:         0.48560665546817
            STATISTICS_VALID_PERCENT:  80.07
            original_dtype:            uint8

    Args:
        naked_xda (xr.DataArray): DataArray to complete
        mtd_xda (xr.DataArray): DataArray with the correct metadata
        new_name (str): New name for naked DataArray

    Returns:
        xr.DataArray: Complete DataArray
    """
    try:
        naked_xda.rio.write_crs(mtd_xda.rio.crs, inplace=True)
    except MissingCRS:
        pass

    if new_name:
        naked_xda = naked_xda.rename(new_name)

    naked_xda.rio.update_attrs(mtd_xda.attrs, inplace=True)
    naked_xda.rio.update_encoding(mtd_xda.encoding, inplace=True)
    naked_xda.rio.set_nodata(mtd_xda.rio.nodata, inplace=True)

    return naked_xda

def set_nodata(xda: xr.DataArray, nodata_val: Union[float, int]) -> xr.DataArray:
    """
    Set nodata to a xarray that have no default nodata value.

    In the data array, the no data will be set to :code:`np.nan`.
    The encoded value can be retrieved with :code:`xda.rio.encoded_nodata`.

    .. code-block:: python

        >>> A = xr.DataArray(dims=("x", "y"), data=np.zeros((3,3), dtype=np.uint8))
        >>> A[0, 0] = 1
        <xarray.DataArray (x: 3, y: 3)>
        array([[1, 0, 0],
               [0, 0, 0],
               [0, 0, 0]], dtype=uint8)
        Dimensions without coordinates: x, y

        >>> A_nodata = set_nodata(A, 0)
        <xarray.DataArray (x: 3, y: 3)>
        array([[ 1., nan, nan],
               [nan, nan, nan],
               [nan, nan, nan]])
        Dimensions without coordinates: x, y

    Args:
        xda (xr.DataArray): DataArray
        nodata_val (Union[float, int]): Nodata value

    Returns:
        xr.DataArray: DataArray with nodata set
    """
    encoding = xda.encoding
    attrs = xda.attrs

    xda = xda.where(xda.data != nodata_val)
    xda.rio.write_nodata(nodata_val, encoded=True, inplace=True)

    # Set back attributes and encoding
    xda.rio.update_attrs(attrs, inplace=True)
    xda.rio.update_encoding(encoding, inplace=True)

    return xda

def where(
    cond, if_true, if_false, master_xda: xr.DataArray = None, new_name: str = ""
) -> xr.DataArray:
    """
    Overloads :code:`xr.where` with:

    - setting metadata of :code:`master_xda`
    - preserving the nodata pixels of the :code:`master_xda`

    If :code:`master_xda` is None, use it like :code:`xr.where`.
    Else, it outputs a :code:`xarray.DataArray` with the same dtype than :code:`master_xda`.

    .. WARNING::
        If you don't give a :code:`master_xda`,
        it is better to pass numpy arrays to :code:`if_false` and :code:`if_true` keywords
        as passing xarrays interfers with the output metadata (you may lose the CRS and so on).
        Just pass :code:`if_true=true_xda.data` inplace of :code:`if_true=true_xda` and the same for :code:`if_false`

    .. code-block:: python

        >>> A = xr.DataArray(dims=("x", "y"), data=[[1, 0, 5], [np.nan, 0, 0]])
        >>> mask_A = rasters.where(A > 3, 0, 1, A, new_name="mask_A")
        <xarray.DataArray 'mask_A' (x: 2, y: 3)>
        array([[ 1.,  1.,  0.],
               [nan,  1.,  1.]])
        Dimensions without coordinates: x, y
    Args:
        cond (scalar, array, Variable, DataArray or Dataset): Conditional array
        if_true (scalar, array, Variable, DataArray or Dataset): What to do if :code:`cond` is True
        if_false (scalar, array, Variable, DataArray or Dataset):  What to do if :code:`cond` is False
        master_xda: Master :code:`xr.DataArray` used to set the metadata and the nodata
        new_name (str): New name of the array

    Returns:
        xr.DataArray: Where array with correct mtd and nodata pixels
    """
    # Enforce condition
    where_xda = xr.where(cond, if_true, if_false)

    if master_xda is not None:
        # Convert to master dtype
        if where_xda.dtype != master_xda.dtype:
            where_xda = where_xda.astype(master_xda.dtype)

        # Convert to datarray if needed
        if not isinstance(where_xda, xr.DataArray):
            where_xda = master_xda.copy(data=where_xda)

        # Set nodata to nan
        where_xda = where_xda.where(~np.isnan(master_xda))

        # Set mtd
        where_xda = set_metadata(where_xda, master_xda, new_name=new_name)

    return where_xda

@path_xarr_dst
def hillshade(
    xds: PATH_XARR_DS, azimuth: float = 315, zenith: float = 45
) -> AnyXrDataStructure:
    """
    Compute the hillshade of a DEM from an azimuth and elevation angle (in degrees).

    Goal: replace `gdaldem CLI <https://gdal.org/programs/gdaldem.html>`_

    NB: altitude = zenith

    References:

    - `1 <https://www.neonscience.org/resources/learning-hub/tutorials/create-hillshade-py>`_
    - `2 <http://webhelp.esri.com/arcgisdesktop/9.2/index.cfm?TopicName=How%20Hillshade%20works>`_

    Args:
        xds (PATH_XARR_DS): Path to the raster, its dataset, its :code:`xarray` or a tuple containing its array and metadata
        azimuth (float): Azimuth angle in degrees
        zenith (float): Zenith angle in degrees

    Returns:
        AnyXrDataStructure: Hillshade
    """
    # Use classic option
    arr, meta = rasters_rio.hillshade(xds, azimuth=azimuth, zenith=zenith)

    return xds.copy(data=arr)

@path_xarr_dst
def slope(
    xds: PATH_XARR_DS, in_pct: bool = False, in_rad: bool = False
) -> AnyXrDataStructure:
    """
    Compute the slope of a DEM (in degrees).

    Goal: replace `gdaldem CLI <https://gdal.org/programs/gdaldem.html>`_

    Args:
        xds (PATH_XARR_DS): Path to the raster, its dataset, its :code:`xarray` or a tuple containing its array and metadata
        in_pct (bool): Outputs slope in percents
        in_rad (bool): Outputs slope in radians. Not taken into account if :code:`in_pct == True`

    Returns:
        AnyXrDataStructure: Slope
    """
    # Use classic option
    arr, meta = rasters_rio.slope(xds, in_pct=in_pct, in_rad=in_rad)

    return xds.copy(data=arr)


#!/usr/bin/python3

import gym
import tensorflow as tf
import numpy as np
import random

def weight_var(shape):
        return tf.Variable(tf.truncated_normal(shape, stddev=.1))

def bias_var(shape):
        return tf.Variable(tf.constant(.1, shape=shape))

class NeuralNetwork():
	def __init__(self, layer_sizes, learn_rate, session):
		#layer_sizes includes input and output layers
		self.sess = session

		self.num_weights = len(layer_sizes)-1
		self.weights = []
		self.biases = []
		for i in range(len(layer_sizes)-1):
			self.weights.append(weight_var([layer_sizes[i],layer_sizes[i+1]]))
			self.biases.append(bias_var([layer_sizes[i+1]]))

		self.x = tf.placeholder('float',[None,layer_sizes[0]])
		self.y = tf.placeholder('float',[None,layer_sizes[-1]])
		self.learn_rate = learn_rate
		self.pred = self.setup_network(self.x)
		self.train_step = self.setup_train()

		self.sess.run(tf.global_variables_initializer())

	def setup_network(self, x):
		for i in range(self.num_weights):
			if i == 0:
				h = tf.nn.relu(tf.matmul(x, self.weights[0]) + self.biases[0])
			elif i == self.num_weights-1:
				h = tf.matmul(h, self.weights[i]) + self.biases[i]
			else:
				h = tf.nn.relu(tf.matmul(h, self.weights[i]) + self.biases[i])
		return h

	def forward_pass(self, x):
		return self.sess.run(self.pred, feed_dict={self.x:x})

	def setup_train(self):
		cost = tf.reduce_mean(tf.squared_difference(self.y, self.pred))
		self.cost = cost
		train = tf.train.AdamOptimizer(self.learn_rate).minimize(cost)
		return train

	def train(self, x, y, p=False):
		if p:
			#print the cost function
			print(self.sess.run(self.cost, feed_dict={self.x:x,self.y:y}))
		self.sess.run(self.train_step, feed_dict={self.x:x,self.y:y})

	def setup_copy(self, nn):
		weight_copy = [self.weights[i].assign(nn.weights[i]) for i in range(len(self.weights))]
		bias_copy = [self.biases[i].assign(nn.biases[i]) for i in range(len(self.biases))]
		self.copy = weight_copy + bias_copy

	def run_copy(self):
		for c in self.copy:
			self.sess.run(c)

class EnvWrapper():
	def __init__(self, env, nn1, nn2, gamma=.9, max_replay_size=10000):
		self.env = env
		self.a_size = env.action_space.n
		self.o_size = env.observation_space.shape[0]
		self.nn1 = nn1
		self.nn2 = nn2
		self.gamma = gamma
		self.replays = []
		self.max_replay_size = max_replay_size
		self.last_run = []

		self.observation = self.action = self.reward = self.new_obs = None
		self.done = True

	def reset(self):
		self.observation = self.env.reset()
		self.action = self.reward = self.new_obs = None
		self.done = False

	def choose_action(self, epsilon=0):
		if random.random() < epsilon:
			self.action = self.env.action_space.sample()
		else:
			qvals = self.nn1.forward_pass(np.reshape(self.observation, [1,self.o_size]))
			self.action = np.argmax(qvals)

	def step(self):
		self.new_obs, self.reward, self.done, _ = self.env.step(self.action)

	def add_replay(self):
		if len(self.replays) >= self.max_replay_size:
			self.replays.pop(0)
		self.replays.append((self.observation, self.action, self.reward, self.new_obs, self.done))

	def update_obs(self):
		self.observation = self.new_obs
		self.new_obs = None

	def run(self, epsilon=0, render=False):
		self.reset()
		reward = 0
		while not self.done:
			if render:
				env.render()
			self.choose_action(epsilon)
			self.step()
			self.add_replay()
			self.update_obs()
			reward += self.reward
		return reward

	def get_train_target(self, replay):
		observation, action, reward, new_obs, done = replay
		observation = np.reshape(observation, [1,self.o_size])
		qvals = self.nn1.forward_pass(observation)
		new_obs = np.reshape(new_obs, [1,self.o_size])
		maxQ = np.max(self.nn2.forward_pass(new_obs))
		if not done:
			reward += maxQ * self.gamma
		y = np.reshape(qvals, [self.a_size])
		y[action] = reward
		return y

	def get_train_batch(self, batch_size):
		#add the last run
		#last_run = np.array(self.last_run)
		#last_x = np.vstack(last_run[:,0])
		#last_y = np.array([self.get_train_target(step) for step in last_run])

		#add a random sample from the replay
		l = len(self.replays)
		if l > batch_size:
			b = random.sample(range(l), batch_size)
		else:
			b = list(range(l))
		replays = np.array(self.replays)[b]
		replays_x = np.vstack(replays[:,0])
		replays_y = np.array([self.get_train_target(replay) for replay in replays])

		#batch_x = np.concatenate((last_x, replays_x),axis=0)
		#batch_y = np.concatenate((last_y, replays_y),axis=0)
		#return batch_x, batch_y
		return replays_x, replays_y

def accuracy(pred, y):
	correct = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
	return tf.reduce_mean(tf.cast(correct, 'float'))

env = gym.make('CartPole-v1')
a_size = env.action_space.n
o_size = env.observation_space.shape[0]
#Tune the number and size of hidden layers
layers = [o_size,80,a_size]
learn_rate = .001
sess = tf.InteractiveSession()
nn1 = NeuralNetwork(layers, learn_rate, sess)
nn2 = NeuralNetwork(layers, learn_rate, sess)
nn2.setup_copy(nn1)
ew = EnvWrapper(env, nn1, nn2, gamma=.95)
sess.graph.finalize()

#Tune these parameters as necessary
epochs = 10000
batch_size = 50
epsilon = .8
epsilon_final = .1
epsilon_dec = .005
train_steps = 20
copy_period = 1
for epoch in range(epochs):
	reward = ew.run(epsilon)
	x, y = ew.get_train_batch(batch_size)
	for i in range(train_steps):
		ew.nn1.train(x,y,p=(i==0 or i==train_steps-1))
	print('finished epoch {}, total reward {}'.format(epoch, reward))
	if epsilon > epsilon_final:
		epsilon -= epsilon_dec
	if epoch % copy_period == 0:
		nn2.run_copy()

print(ew.run())


import sys
import re
import glob
from collections import Counter

# files = glob.glob(sys.argv[1])

skipList = []

words = []
phrases = {1:[],2:[],3:[],4:[],5:[]}

print('Files Checked:')
for filename in sys.argv[1:]:
	if filename in skipList:
		continue

	print('\t'+filename)
	with open(filename, 'r') as fin:
	    content = fin.read()

	## Chew up new lines replacing them with spaces and also remove comments
	lines = content.split('\n')
	content = ''
	sep = ''
	for line in lines:
		lineWithComment = line.split('%')[0]
		if len(lineWithComment):
			content += sep + lineWithComment
			sep = ' '

	## Replace latex newlines with spaces
	content = content.replace('\\\\', ' ')

	## Remove punctuation for our needs, this can lead to potentially misleading
	## results since it will potentially put ends of sentences together with
	## beginnings of sentences for phrases, but I care less about false positives,
	## and would prefer to err on the side of reporting too much, remember the job
	## is to make the writing more distinct.
	punctuation = '\.|!|`|,|\?|;|:|\"|\'|\(|\)'
	content = re.sub(punctuation, ' ', content)

	## Make all of the opening and closing brackets their own token for easing the
	## handling later
	content = re.sub('\{', ' { ', content)
	content = re.sub('\}', ' } ', content)
	content = re.sub('\[', ' [ ', content)
	content = re.sub('\]', ' ] ', content)

	## Let us begin the counting
	tokens = re.split('~| ', content)

	## Are we currently inside a command? If so, then ignore everything until we are
	## back at the base level
	insideCommand = 0

	## Are we currently inside a command? If so, then ignore everything until we are
	## back at the base level
	insideBracket = 0

	for i in range(len(tokens)):
		token = tokens[i]
		if not len(token):
			continue

		if token in '{}[]':
			if token == '{':
				insideCommand += 1
			elif token == '}':
				insideCommand -= 1
				if insideCommand < 0:
					print('Error where text resembles: '+' '.join(tokens[(i-5):(i+5)]))
			elif token == '[':
				insideBracket += 1
			else:
				insideBracket -= 1
				if insideBracket < 0:
					print('Error where text resembles: '+' '.join(tokens[(i-5):(i+5)]))
		elif token.startswith('\\'):
			pass
		else:
			if not (insideCommand+insideBracket):
				words.append(token.lower())
			else:
				pass

## Again can cause potentially misleading results since file order is not
## specified, and we will group words together that are from different files,
## but the goal is to identify repeated phrases, if these so happen to generate
## real phrases, then it will not hurt to show them. More likely, they will
## create non-sense phrases that will be filtered out when we look at the
## highest frequency occurences. Basically, not worth the effort to do
## it correctly.
for count in phrases.keys():
	for i in range(len(words)-count):
		phrases[count].append(' '.join(words[i:(i+count)]))

rankLimit = 20

print('\nTotal word count: %d' % len(words))
for count,phraseC in phrases.items():
	commonCounts = Counter(phraseC).most_common(rankLimit)
	print('\n%d most common %d-word phrases:' % (rankLimit,count))
	for key,val in commonCounts:
		print('\t%s: %d' % (key,val))