import usb.core
import usb.util
import sys
from PIL import Image, ImageTk
import numpy
from numpy import array
import colorscale
from scipy.misc import toimage
from scipy import ndimage

class PhotoTaker():

    def __init__(self):
        self.palettes = dict()
        self.palettes['tillscale'] = colorscale.TillPalette()
        self.palettes['gray1scale'] = colorscale.Gray1Palette()
        self.palettes['redgreenscale'] = colorscale.RedGreenPalette()
        self.palettes['greenredscale'] = colorscale.GreenRedPalette()
        self.palettes['rain1scale'] = colorscale.Rain1Palette()
        self.palettes['ironscale'] = colorscale.IronPalette()
        self.palettes['blackhotscale'] = colorscale.BlackHotPalette()
        self.palettes['whitehotscale'] = colorscale.WhiteHotPalette()
        self.status = None
        self.calImage = None
        self.calImagex = None
        self.dev = None
        self.label = None
        self.Label1 = None
        self.Label2 = None
        self.Label3 = None
        self.Label4 = None
        self.Label5 = None
        self.Label6 = None
        self.scl = None
        self.scl1 = None
        self.scl2 = None
        self.calImage = None
        self.calImagex = None
        self.pal = None
        self.snapshot = None
        self.initialize()

    def initialize(self):
        # Default palette is "iron"
        # global dev, label, Label1, Label2, Label3, Label4, Label5, Label6
        # global scl, scl1, scl2
        # global calImage, calImagex, pal, snapshot
        self.pal = 'gray1scale'
        self.snapshot = 0

        # Set up device
        self.dev = self.usbinit()
        self.camerainit(self.dev)

        # get a cal image so the data isn't null if/when we miss the first one
        self.get_cal_image(self.dev)

    def deinit(self,dev):
        msg = '\x00\x00'
        for i in range(3):
            self.send_msg(dev,0x41, 0x3C, 0, 0, msg)           # 0x3c = 60  Set Operation Mode 0x0000 (Sleep)

    def usbinit(self):
        # find our Seek Thermal device  289d:0010
        dev = usb.core.find(idVendor=0x289d, idProduct=0x0010)

        # was it found?
        if dev is None:
            raise ValueError('Device not found')

        # set the active configuration. With no arguments, the first
        # configuration will be the active one
        dev.set_configuration()

        # get an endpoint instance
        cfg = dev.get_active_configuration()
        intf = cfg[(0,0)]

        # match the first OUT endpoint
        ep = usb.util.find_descriptor(
            intf,
            custom_match = \
            lambda e: \
                usb.util.endpoint_direction(e.bEndpointAddress) == \
                usb.util.ENDPOINT_OUT)

        assert ep is not None

        return dev

    # send_msg sends a message that does not need or get an answer
    def send_msg(self,dev,bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength=None, timeout=None):
        assert (dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout) == len(data_or_wLength))

    # receive msg actually sends a message as well.
    def receive_msg(self,dev,bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength=None, timeout=None):
        zz = dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout) # == len(data_or_wLength))
        return zz

    def camerainit(self, dev):
        try:
            msg = '\x01'
            self.send_msg(dev,0x41, 0x54, 0, 0, msg)                 # 0x54 = 84 Target Platform 0x01 = Android
        except Exception as e:
            self.deinit(dev)
            msg = '\x01'
            self.send_msg(dev,0x41, 0x54, 0, 0, msg)                 # 0x54 = 84 Target Platform 0x01 = Android

        self.send_msg(dev,0x41, 0x3C, 0, 0, '\x00\x00')              # 0x3c = 60 Set operation mode    0x0000  (Sleep)
        ret1 = self.receive_msg(dev,0xC1, 0x4E, 0, 0, 4)             # 0x4E = 78 Get Firmware Info

        ret2 = self.receive_msg(dev,0xC1, 0x36, 0, 0, 12)            # 0x36 = 54 Read Chip ID

        self.send_msg(dev,0x41, 0x56, 0, 0, '\x20\x00\x30\x00\x00\x00')                  # 0x56 = 86 Set Factory Settings Features
        ret3 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x40)                              # 0x58 = 88 Get Factory Settings

        self.send_msg(dev,0x41, 0x56, 0, 0, '\x20\x00\x50\x00\x00\x00')                  # 0x56 = 86 Set Factory Settings Features
        ret4 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x40)                              # 0x58 = 88 Get Factory Settings

        self.send_msg(dev,0x41, 0x56, 0, 0, '\x0C\x00\x70\x00\x00\x00')                  # 0x56 = 86 Set Factory Settings Features
        ret5 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x18)                              # 0x58 = 88 Get Factory Settings

        self.send_msg(dev,0x41, 0x56, 0, 0, '\x06\x00\x08\x00\x00\x00')                  # 0x56 = 86 Set Factory Settings Features
        ret6 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x0C)                              # 0x58 = 88 Get Factory Settings

        self.send_msg(dev,0x41, 0x3E, 0, 0, '\x08\x00')                                  # 0x3E = 62 Set Image Processing Mode 0x0008
        ret7 = self.receive_msg(dev,0xC1, 0x3D, 0, 0, 2)                                 # 0x3D = 61 Get Operation Mode

        self.send_msg(dev,0x41, 0x3E, 0, 0, '\x08\x00')                                  # 0x3E = 62 Set Image Processing Mode  0x0008
        self.send_msg(dev,0x41, 0x3C, 0, 0, '\x01\x00')                                  # 0x3c = 60 Set Operation Mode         0x0001  (Run)
        ret8 = self.receive_msg(dev,0xC1, 0x3D, 0, 0, 2)                                 # 0x3D = 61 Get Operation Mode

    def read_frame(self,dev):
        # Send a read frame request
        # 0x53 = 83 Set Start Get Image Transfer
        self.send_msg(dev,0x41, 0x53, 0, 0, '\xC0\x7E\x00\x00')
        try:
            data  = dev.read(0x81, 0x3F60, 1000)
            data += dev.read(0x81, 0x3F60, 1000)
            data += dev.read(0x81, 0x3F60, 1000)
            data += dev.read(0x81, 0x3F60, 1000)

        except usb.USBError as e:
            sys.exit()

        return data

    # Add (really subtract) the data from the 207 row to each pixil
    def add_207(self,imgF):
        # global scl2
        # or not depending on the testing some of the following may be commented out.
        # there are a different # of black dots in each row so the divisor
        # needs to change for each row according to what is in the dot_numbers.txt file.
        # this may not be the best way to do this. The code below does not do this now.
        # need to try to use numpy or scipy to do this as it is a real hit on cpu useage.
        # But doing it only for the cal image doesn't impact the real time images.
        tuning = self.scl2.get() / 150.0

        z = (.002 * imgF[:,206].mean())
        z1 = z * tuning

        for i in range(0,156,1):
            for j in range(0,205,1):
                    # try scaled pixil and scaled pixil 207
                    imgF[i,j] = imgF[i,j] - (.05 * imgF[i,j]/z) - imgF[i,206]/z1

        return

    def get_cal_image(self, dev):
        # Get the first cal image so calImage isn't null
        # global status, calImage, calImagex
        self.status = 0

        #  Wait for the cal frame
        while self.status != 1:
            #  1 is a Calibration frame
            # Read a raw frame
            ret9 = self.read_frame(dev)
            self.status = ret9[20]
            status1 = ret9[80]
            #  6 is a pre-calibration frame (whatever that means)
            #  4, 9, 8, 7, 5, 10 other... who knows.
            #  See http://www.eevblog.com/forum/testgear/yet-another-cheap-thermal-imager-incoming/msg545910/#msg545910
            #  for examples.

        #  Convert the raw 16 bit calibration data to a PIL Image
        calimgI = Image.frombytes("F", (208,156), ret9, "raw", "F;16")

        # save 16bit cal image for later
        self.calImagex = Image.frombytes("I", (208,156), ret9, "raw", "I;16")

        #  Convert the PIL Image to an unsigned numpy float asarray
        im2arr = numpy.asarray(calimgI)

        # clamp values < 2000 to 2000
        im2arr = numpy.where(im2arr < 2000, 2000, im2arr)
        im2arrF = im2arr.astype('float')
        self.calImage = im2arrF

        return

    def get_image(self):
        dev = self.dev
        # global calImage, calImagex, status, scl, scl1, pal, snapshot
        self.status = 0

        #  Wait for the next image frame, ID = 3 is a Normal frame
        while self.status != 3:
            # Read a raw frame
           ret9 = self.read_frame(dev)
           self.status = ret9[20]

            # check for a new cal frame, if so update the cal image
           if self.status == 1:
                # Convert the raw 16 bit calibration data to a PIL Image
                calimgI = Image.frombytes("F", (208,156), ret9, "raw", "F;16")
                # save cal 16bit image for later
                self.calImagex = Image.frombytes("I", (208,156), ret9, "raw", "I;16")
                # Convert the PIL Image to an unsigned numpy float array
                im2arr = numpy.asarray(calimgI)
                # Pixel 40 is a counter of some sort that starts at 0 and increments to 65535
                # maybe an internal frame counter or clock.
                status1 = im2arr[0,40]
                # clamp values < 2000 to 2000
                im2arr = numpy.where(im2arr < 2000, 2000, im2arr)
                im2arrF = im2arr.astype('float')
                # Clamp pixel 40 to 2000 so it doesn't cause havoc as it rises to 65535
                im2arrF[0,40] = 2000
                # Add the row 207 correction (maybe) >>Looks like it needs to be applied to just the cal frame<<
                # self.add_207(im2arrF)
                # Zero out column 207
                im2arrF[:,206] = numpy.zeros(156)
                #  Print the min max values for the calimage (Some Tinker related stuff)
                #  self.printCAL(im2arrF)
                #  Save the calibration image
                self.calImage = im2arrF

        #  If this is normal image data
        #  Convert the raw 16 bit thermal data to a PIL Image
        imgx = Image.frombytes("F", (208,156), ret9, "raw", "F;16")
        imgy = Image.frombytes("I", (208,156), ret9, "raw", "I;16")

        #  Convert the PIL Image to an unsigned numpy float array
        im1arr = numpy.asarray(imgx)

        # clamp values < 2000 to 2000
        im1arr = numpy.where(im1arr < 2000, 2000, im1arr)
        im1arrF = im1arr.astype('float')

        # Clamp pixel 40 to 2000 so it doesn't cause havoc as it rises to 65535
        im1arrF[0,40]  = 2000

        # Zero out column 207
        im1arrF[:,206] = numpy.zeros(156)

        #  Print the min max values for the image (UI related stuff)
        #  self.printIMG(im1arrF)

        #  Subtract the most recent calibration image from the offset image data
        #  With both the cal and image as floats, the offset doesn't matter and
        #  the following image conversion scales the result to display properly
        additionF = (im1arrF) + 600 - (self.calImage)

        #  Try removing noise from the image, this works suprisingly well, but takes some cpu time
        #  It gets rid of bad pixels as well as the "Patent Pixils"
        noiselessF = ndimage.median_filter(additionF, 3)

        # don't bother to zero out column 207 as it contains no data
        # can't see any difference on the image anyway.
        # noiselessF[:,206] = numpy.zeros(156)

        #  Print the min max values for the calibrated/noise filtered image (UI related stuff)
        #  self.printSUM(noiselessF)

        # This will colorize the image, it works but it is a cpu hog
        #bottom = self.scl.get()
        #top = self.scl1.get()
        #display_min = bottom * 4
        #display_max = top * 16
        image8 = noiselessF
        #image8.clip(display_min, display_max, out=image8)
        #image8 -= display_min
        #image8 //= (display_max - display_min + 1) / 256.
        image8 = image8.astype(numpy.uint8)
        noiselessI8= image8
        conv = colorscale.GrayToRGB(self.palettes[self.pal])
        cred = numpy.frompyfunc(conv.get_red, 1, 1)
        cgreen = numpy.frompyfunc(conv.get_green, 1, 1)
        cblue = numpy.frompyfunc(conv.get_blue, 1, 1)

        # Convert to a PIL image sized to 640 x 480
        color = numpy.dstack((cred(noiselessI8).astype(noiselessI8.dtype), cgreen(noiselessI8).astype(noiselessI8.dtype), cblue(noiselessI8).astype(noiselessI8.dtype)))
        imgCC = Image.fromarray(color).resize((640, 480),Image.ANTIALIAS).transpose(3)

        # If user has clicked Snap! then save the rawCal, rawData, and colorized image
        # File names are hardcoded for now.

        im1arry = numpy.asarray(imgy)
        im1arrz = numpy.asarray(self.calImagex)
        jj = Image.fromarray(im1arry)
        jj.save('data/rawData.png')
        jj = Image.fromarray(im1arrz)
        jj.save('data/rawCal.png')
        imgCC.save('data/CImage.png')

        return imgCC

if __name__ == "__main__":
    pt = PhotoTaker()
    pt.get_image()


import json
from mongoengine.errors import NotUniqueError
from app.database.dog_repository import DogRepository
from werkzeug.exceptions import BadRequest, NotFound
from flask import jsonify
from app.kafka.report.report_producer import ReportProducer
from reportlab.pdfgen import canvas

class DogSaveDto:
    def __init__(self, name=str, breed=str, features=list):
        self.name = name
        self.breed = breed
        self.features = features

class DogUpdateDto(DogSaveDto):
    pass

class DogService:
    def __init__(self):
        self.repository = DogRepository()
        self.report_producer = ReportProducer()

    def save(self, req):
        try:
            data = json.loads(req.data)
            dog = self.repository.save(DogSaveDto(name=data['name'],
                                                  breed=data['breed'],
                                                  features=data['features']))
            return jsonify(dog)
        except NotUniqueError:
            raise BadRequest(f"Already exists dog with name: {data['name']}")

    def get_dog_by_name_or_fail(self, name=str):
        dog = self.repository.get_by_name(name)
        if not dog:
            raise NotFound(f"Not found dog with name: {name}")
        return dog

    def get_by_name(self, req):
        name = req.args.get('name')
        dog = self.get_dog_by_name_or_fail(name)
        return jsonify(dog)

    def update_by_name(self, req):
        try:
            name = req.args.get('name')
            data = json.loads(req.data)
            dog = self.get_dog_by_name_or_fail(name)
            dog = self.repository.update_by_name(dog, DogSaveDto(name=data['name'],
                                                                 breed=data['breed'],
                                                                 features=data['features']))
            return jsonify(dog)
        except NotUniqueError:
            raise BadRequest(f"Already exists dog with name: {data['name']}")

    def get_all(self):
        dog_list = self.repository.get_all()
        return jsonify(dog_list)

    def generate_report(self, req):
        data = json.loads(req.data)
        self.report_producer.send(json.dumps(data))
        return jsonify({"message": "send report to generate"})

    def generate_report_pdf(self):
        dog_list = self.repository.get_all()
        pdf = canvas.Canvas('report.pdf')
        x = 720
        for dog in dog_list:
            x -= 20
            pdf.drawString(100, x, f"{dog.name} - {dog.breed}")
        pdf.setTitle('Relatório de Cachorros Cadastrados')
        pdf.drawString(100, 750, 'Relatório de Cachorros Cadastrados')
        pdf.save()


#papago  번역
from requests import Request
from requests import Session

s = Session()

# id, 비밀번호

headers = {
    'X-Naver-Client-Id' : 'RkLQo7eDF4QVrC93IQzp',
    'X-Naver-Client-Secret' : 'KDvxQ1hq0N'
}

text = "Yesterday all my troubles seemed so far away"

# dict type

payLoad = {
    'source' : 'en',
    'target' : 'ko',
    'text' : text,
}

# encText = urllib.parse.quote("번역할 문장을 입력하세요")
# data = "source=ko&target=en&text=" + encText

# 문장번역 URL 지정
# url = "https://openapi.naver.com/v1/language/translate"
# 위의 url은 구 주소로, api가 동작하지 않으면 api 주소가 그대로인지 확인하기
url = "https://openapi.naver.com/v1/papago/n2mt"

req = Request('POST', url, data=payLoad, headers=headers)
# 미리 내부적으로 compile
prepared = req.prepare()
# session 객체를 통해 전송
res = s.send(prepared)

print("res.json() -> " , res.json())
print("text -> ", res.json()['message']['result']['translatedText']) # 번역텍스트만 출력됨


import autocomplete_light
from cities_light.models import Country, City
from django.contrib.auth.models import User, Group

class AutocompleteTaggableItems(autocomplete_light.AutocompleteGenericBase):
    choices = (
        User.objects.all(),
        Group.objects.all(),
        City.objects.all(),
        Country.objects.all(),
    )

    search_fields = (
        ('username', 'email'),
        ('name',),
        ('search_names',),
        ('name_ascii',),
    )

autocomplete_light.register(AutocompleteTaggableItems)


import torch
import torch.nn as nn
import torch.nn.functional as F
from src.base.model import BaseModel

from src.layers.cell import *
from src.layers.cst_cell import *
from numpy.fft import fft, ifft
from scipy.fftpack import fftfreq
from math import sqrt

class FourierDecomp(nn.Module):
    def __init__(self, hist_len, in_dim, hid_dim, out_dim, n_nodes, freq_type, freq_threshold, device):
        super(FourierDecomp, self).__init__()
        '''
        input time series: [b, n, l, d]
        output c/e time seires: [b, n, l, d]
        '''
        self.fourier_layer = FourierCell(in_dim * n_nodes, out_dim* n_nodes, hist_len, freq_type, freq_threshold, device)
        self.mlp = nn.Sequential(nn.Conv1d(in_dim* n_nodes, hid_dim *2, 1),
                                        nn.GELU(),
                                        nn.Conv1d(hid_dim *2, out_dim* n_nodes, 1)
                                        ).to(device)

    def forward(self, ts_in):
        b, n, l, d = ts_in.shape
        ts_in = ts_in.permute(0,1,3,2).reshape(b, n*d, l)
        x1 = self.fourier_layer(ts_in)
        x2 = self.mlp(ts_in)
        x = x1 + x2
        ts_out = F.relu(x)
        return ts_out.reshape(b, n, d, l).permute(0,1,3,2)

class FourierCell(nn.Module):
    def __init__(self, in_channels, out_channels, t_len, freq_type, freq_threshold, device):
        super(FourierCell, self).__init__()
        """
        FFT -> linear transform -> Inverse FFT
        """
        self.in_channels = in_channels # n * in_d
        self.out_channels = out_channels # n * out_d
        self.freq_type = freq_type
        self.freq_threshold = int(freq_threshold)
        self.scale = (1 / (in_channels*out_channels))
        if freq_type == 'high':
            self.weights = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, t_len//2 + 1 - self.freq_threshold, dtype=torch.cfloat)).to(device)
        elif freq_type == 'low':
            self.weights = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.freq_threshold, dtype=torch.cfloat)).to(device)
    def compl_mul(self, input, weights):
        # (batch, in_channel, x ), (in_channel, out_channel, x) -> (batch, out_channel, x)
        return torch.einsum("bix,iox->box", input, weights)

    def forward(self, ts_in):
        x_ft = torch.fft.rfft(ts_in) # [1024, 34, 13]
        ts_out = torch.zeros(ts_in.shape[0], self.out_channels, ts_in.size(-1)//2 + 1, device=ts_in.device, dtype=torch.cfloat)
        if self.freq_type == 'low':
            ts_out[:, :, :self.freq_threshold] = self.compl_mul(x_ft[:, :, :self.freq_threshold], self.weights)
        elif self.freq_type == 'high':
            ts_out[:, :, self.freq_threshold:] = self.compl_mul(x_ft[:, :, self.freq_threshold:], self.weights)
        ts_out = torch.fft.irfft(ts_out, n=ts_in.shape[-1])
        return ts_out

#################### tcn
class TemporalEnc(nn.Module):
    '''
    input (b, n, t, d)
    output (b, n, l-2, hid_dim)
    '''
    def __init__(self, in_dim, hid_dim, kernel_size=3):
        super(TemporalEnc, self).__init__()
        self.c_in = in_dim
        self.c_out = hid_dim
        self.res_conv = nn.Conv2d(in_dim, hid_dim, 1)
        self.kernel_size = kernel_size
        self.conv1 = nn.Conv2d(in_dim, hid_dim, (1, kernel_size))
        self.conv2 = nn.Conv2d(in_dim, hid_dim, (1, kernel_size))

    def forward(self, x):
        # x [b, n, t, d]
        x = x.permute(0, 3, 1, 2)  # [b, c, num_nodes, t]
        x_input = self.res_conv(x)
        x_input = x_input[:, :, :, self.kernel_size - 1:]
        out = self.conv1(x) + x_input
        out = out * torch.sigmoid(self.conv2(x))
        out = out.permute(0, 2, 3, 1) #[b, n, l-2, d]
        return out

class SpatialAtten(nn.Module):
    '''
    input (b, n, l, d)
    output: a_c, a_e (b, n, n) *2
    '''
    def __init__(self, hid_dim, embed_dim, wo_causal_s, attn_threshold, block_num, seq_len, input_dim):
        super(SpatialAtten, self).__init__()
        self.attn_threshold = attn_threshold
        self.wo_causal_s = wo_causal_s
        self.embed_dim = embed_dim
        # layer
        l_t = seq_len - (block_num+1) * 2
        self.ts_proj = nn.Linear(l_t * hid_dim, embed_dim)

    def get_norm_attn(self, attn):
        '''
        attn: (b, n, n)
        '''
        # np.save('/home/yutong/STCausal/notebook/attn_0.npy', attn.cpu().detach().numpy())
        mask_c_bool = torch.ge(attn, self.attn_threshold)
        mask_c = mask_c_bool.int() * -np.inf
        mask_c = torch.where(torch.isnan(mask_c), torch.full_like(mask_c,1),mask_c).requires_grad_(False)
        maske_attn_c = attn * mask_c
        a_c = F.softmax(maske_attn_c, -1).detach()
        a_c = torch.where(torch.isnan(a_c), torch.full_like(a_c,0),a_c).requires_grad_(False)

        if not self.wo_causal_s:
            mask_e_bool = ~mask_c_bool
            mask_e = mask_e_bool.int() * -np.inf
            mask_e = torch.where(torch.isnan(mask_e), torch.full_like(mask_e,1),mask_e).requires_grad_(False)
            neg_attn = (torch.ones(attn.shape).to(attn.device) / attn)
            maske_attn_e = neg_attn * mask_e
            a_e = F.softmax(maske_attn_e, -1).detach()
            a_e = torch.where(torch.isnan(a_e), torch.full_like(a_e,0),a_e).requires_grad_(False)
        else: a_e = None

        return a_c, a_e

    def attention(self, queries, keys):
        scale = 1. / sqrt(self.embed_dim)
        scores = torch.einsum("ble,bse->bls", queries, keys)
        A = torch.softmax(scale * scores, dim=-1)
        return A

    def forward(self, ts_enc):
        # ts_enc #[b, n, l_, d]
        b, n, l_, d = ts_enc.shape
        # print(ts_enc.shape)
        # input()
        ts_enc = self.ts_proj(ts_enc.reshape(b * n, -1)).reshape(b, n, self.embed_dim) # [b, n, 512]
        attn = self.attention(ts_enc, ts_enc) # [b, n, n]
        a_c, a_e = self.get_norm_attn(attn)
        return a_c, a_e
        # return attn, a_e

# class Prediction(nn.Module):
#     def __init__(self, pred_len, hid_dim, n_nodes, hid_dim_pred, output_dim):
#         super(Prediction, self).__init__()
#         self.output_dim = output_dim
#         self.pred_len = pred_len
#         self.hid_dim_pred = hid_dim_pred
#         self.fc_in = nn.Linear(hid_dim, hid_dim_pred)
#         self.fc_out = nn.Linear(hid_dim_pred, pred_len * output_dim)
#         self.ln = nn.LayerNorm([n_nodes ,hid_dim_pred])

#     def forward(self, hn):
#         # h: (b, n, hid_dim)
#         b, n, d = hn.shape
#         h = F.relu(self.fc_in(hn.reshape(b * n, d)))
#         h = self.ln(h.reshape(b, n, self.hid_dim_pred))
#         pred = self.fc_out(h.reshape(b * n, self.hid_dim_pred))
#         return pred.reshape(b, n, self.pred_len, self.output_dim).permute(0,2,1,3)

class CSTBlock(nn.Module):
    def __init__(self, seq_len, pred_len, hid_dim, n_nodes, input_dim, output_dim, pred_block, device, wo_causal_s, attn_threshold, spatial_embed_dim, block_num):
        super(CSTBlock, self).__init__()
        self.pred_block = pred_block
        self.output_dim = output_dim
        self.pred_len = pred_len
        self.block_num = block_num
        self.wo_causal_s = wo_causal_s

        # layer
        if block_num == 0:
            self.temporal= TemporalEnc(input_dim, hid_dim, kernel_size=3)
        else:
            self.temporal= TemporalEnc(hid_dim, hid_dim, kernel_size=3)
        self.get_attention = SpatialAtten(hid_dim, spatial_embed_dim, wo_causal_s, attn_threshold, block_num, seq_len, input_dim)
        self.ln = nn.LayerNorm([n_nodes ,hid_dim])

    def spatial_agg(self, att, v):
        return torch.einsum("bln,bnsd->blsd", att, v)

    def forward(self, h):
        # h: [b, n, s, d]
        # b, n, s, d = h.shape
        h_ = self.temporal(h) # [512, n, l, d?]
        a_c, a_e = self.get_attention(h_)
        c = self.spatial_agg(a_c , h_)
        c = c + h_
        c = self.ln(c.permute(0, 2, 1, 3))# [b, l_, n, d]
        c = c.permute(0, 2, 1, 3)

        if not self.wo_causal_s:
            e = self.spatial_agg(a_e, h_)
            e = e.reshape(-1, e.shape[-1])
        else: e = None
        return c, e, a_c


#coding: utf-8
"""
GPLv3
"""

from collections import OrderedDict
from datetime import date
import os

from osgeo import osr

from trytond.model import ModelView, ModelSingleton, ModelSQL, fields
from trytond.pyson import Bool, Eval, Not
from trytond.pool import PoolMeta, Pool

from trytond.modules.geotools.tools import get_as_epsg4326, bbox_aspect
from trytond.modules.map.map_render import MapRender
from trytond.modules.qgis.qgis import QGis
from trytond.modules.qgis.mapable import Mapable

__all__ = ['INSEE', 'INSEEQGis']

class INSEE(Mapable, ModelSQL, ModelView):
    u'INSEE'
    __name__ = 'portrait.insee'
    _rec_name = 'name'

    code = fields.Char(
            string=u'id',
            help=u'id du carreau INSEE',
            required=True,
        )
    name = fields.Char(
            string = u'idINSPIRE',
            help = u'idINSPIRE du carreau INSEE',
        )
    idk = fields.Char(
            string = u'idk',
            help = u'idk du carreau INSEE',
        )
    individu = fields.Float(
            string = u'Individu',
            help = u'Nombre d\'individus résidant dans le carreau INSEE',
            digits=(16,2)
        )
    geom = fields.MultiPolygon(
            string=u'Geometry',
            srid=2154
        )
    version = fields.Date(
            string=u'Date de version',
            help=u'Date de version',
        )

    @staticmethod
    def default_version():
        return Pool().get('ir.date').today()

    insee_image = fields.Function(
                    fields.Binary(
                        string=u'Image'
                    ),
            'get_image'
        )
    insee_map = fields.Binary(
            string=u'Image'
        )

    def get_image(self, ids):
        return self._get_image( 'insee_image.qgs', 'carte' )

    def get_map(self, ids):
        return self._get_image( 'insee_map.qgs', 'carte' )

    COLOR = (1, 0.1, 0.1, 1)
    BGCOLOR = (1, 0.1, 0.1, 0.1)

    @classmethod
    def __setup__(cls):
        super(INSEE, cls).__setup__()
        cls._buttons.update({
            'insee_edit': {},
            'generate': {},
        })

    @classmethod
    @ModelView.button_action('portrait.report_insee_edit')
    def insee_edit(cls, ids):
        pass

    @classmethod
    @ModelView.button
    def generate(cls, records):
        for record in records:
            if record.code is None:
                continue
            cls.write([record], {'insee_map': cls.get_map(record, 'map')})

class INSEEQGis(QGis):
    'INSEEQGis'
    __name__ = 'portrait.insee.qgis'
    TITLES = {
        'portrait.insee': u'INSEE',
        }


# -*- coding: UTF-8 -*-
"""
title: 每个小孩最多能分到多少糖果
You are given a 0-indexed integer array candies. Each element in the array denotes a pile of candies of size candies[i]. You can divide each pile into any number of sub piles, but you cannot merge two piles together.
You are also given an integer k. You should allocate piles of candies to k children such that each child gets the same number of candies. Each child can take at most one pile of candies and some piles of candies may go unused.
Return the maximum number of candies each child can get.

Example 1:
Input: candies = [5,8,6], k = 3
Output: 5
Explanation: We can divide candies[1] into 2 piles of size 5 and 3, and candies[2] into 2 piles of size 5 and 1. We now have five piles of candies of sizes 5, 5, 3, 5, and 1. We can allocate the 3 piles of size 5 to 3 children. It can be proven that each child cannot receive more than 5 candies.

Example 2:
Input: candies = [2,5], k = 11
Output: 0
Explanation: There are 11 children but only 7 candies in total, so it is impossible to ensure each child receives at least one candy. Thus, each child gets no candy and the answer is 0.

Constraints:
1 <= candies.length <= 10^5
1 <= candies[i] <= 10^7
1 <= k <= 10^12
"""
from typing import List

class Solution:
    def maximumCandies(self, candies: List[int], k: int) -> int:
        """二分查找"""
        total = sum(candies)
        if total < k:
            return 0
        candies.sort()
        n = len(candies)
        left, right = 1, total // k
        while left <= right:
            mid = (left + right) // 2
            cur = 0
            for i in range(n - 1, -1, -1):
                if candies[i] < mid or cur >= k:
                    break
                cur += candies[i] // mid
            if cur >= k:
                left = mid + 1
            else:
                right = mid - 1
        return right

if __name__ == '__main__':
    print(Solution().maximumCandies(candies=[2, 5], k=11))


import cv2
import argparse
import sys
# local
from measure import measureVcut
from utils.transforms import resFactor2std
from resolutionFactor import resolutionFactor

# use in a command line
# >> python fromFile.py ""

if __name__ == '__main__':

    if(len(sys.argv) == 1):
        # add args created in generate.py' to test this module
        sys.argv.append("masked.png")
        sys.argv.append(open("line.txt", 'r').read())
        sys.argv.append('-u unmasked.png')

    parser = argparse.ArgumentParser(description='Image sharpness (resolution factor) from masked images via v-cut method')
    parser.add_argument('masked', type=str, help='Path to masked image')
    parser.add_argument('line', type=str, help='Line within v-cut e.g. 10,20,100,25 for x0=10,y0=20,x1=100,y1=25')

    parser.add_argument('-u', '--unmasked', type=str, default=None, help='Path to unmasked image')
    parser.add_argument('-b', '--background', type=str, default=None, help='Path to background image')
    parser.add_argument('-w', '--max_width', type=int, default=101, help='Maximum width [px] of v=cut')

    parser.add_argument('--mask_not_dark', dest='mask_is_dark', action='store_false', help='use flag, if mask if not completely opaque')
    parser.set_defaults(mask_is_dark=True)

    args = parser.parse_args()

    try:
        line = args.line.split(',')
        if(len(line) != 4):
            raise Exception()
        line = [float(l) for l in line]
    except:
        raise Exception("need to write line as x0,y0,x1,y1  e.g.: 10,20,100,25")

    img_masked = cv2.imread(args.masked, cv2.IMREAD_GRAYSCALE)

    img_unmasked = args.unmasked
    if(img_unmasked != None):
        img_unmasked = cv2.imread(img_unmasked, cv2.IMREAD_GRAYSCALE)

    img_bg = args.background
    if(img_bg != None):
        img_bg = cv2.imread(img_bg, cv2.IMREAD_GRAYSCALE)

    out = measureVcut(img_masked, line, img_unmasked, img_bg,
             args.max_width, mask_is_dark=args.mask_is_dark)[0]

    fres = resolutionFactor(*out)
    print("Resolution factor=", fres)
    print("Corresp. std of Gaussian blur kernel=", resFactor2std(fres))


import pygame
from services.service import Service
from presentation.board_gui import BoardGUI
from pygame.locals import *
import time

class GUI:
    BACKGROUND_COLOR = (255, 255, 255)
    WINDOW_HEIGHT = 600
    WINDOW_WIDTH = 600
    WINDOW_CAPTION = "Obstruction"

    def __init__(self, board: BoardGUI, services: Service):
        """window initialization"""
        pygame.init()
        pygame.display.set_caption(self.WINDOW_CAPTION)
        self.__window = pygame.display.set_mode((self.WINDOW_WIDTH, self.WINDOW_HEIGHT))
        self.__window.fill(self.BACKGROUND_COLOR)
        self.__board = board
        self.__score = 0
        self.__services = services
        self.__font = pygame.font.SysFont("newyork", 20)
        self.__score_text_color = (0, 0, 0)
        self.__score_background_color = (255, 255, 255)

    def __call__(self):

        running = True
        turn = "player"

        while running:

            for event in pygame.event.get():

                if event.type == pygame.QUIT:
                    running = False
                self.__text = self.__font.render(f'Score: {self.__score}', True, self.__score_text_color,
                                                 self.__score_background_color)

                if self.__services.no_free_cell():
                    if turn == "player":
                        if self.__score > 0:
                            self.__score -= 1
                        win_message = pygame.image.load("assets/you_lost_message.png")
                        self.__window.blit(win_message, (180, 60))
                        self.__board.draw(self.__window)

                    else:
                        self.__score += 1
                        win_message = pygame.image.load("assets/win_message.png")
                        self.__window.blit(win_message, (180, 60))
                        self.__board.draw(self.__window)

                    pygame.display.update()
                    time.sleep(1)
                    color = (255, 255, 255)

                    pygame.draw.rect(self.__window, color, (180, 60, 250, 80))
                    self.__services.reset_game_board()

                elif turn == "computer":
                    self.__services.computer_move()
                    turn = "player"

                else:
                    if event.type == MOUSEBUTTONDOWN:
                        y, x = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]

                        if self.__services.click_on_game_board(x, y, self.__board.board_top, self.__board.height, self.__board.board_left, self.__board.width):
                            row = self.__services.get_row(x, self.__board.board_top)
                            column = self.__services.get_column(y, self.__board.board_left)

                            if self.__services.get_cell_state(row, column) == "free":
                                self.__services.update_cell_state(row, column, "x")
                                self.__services.set_blocks(row, column)
                                turn = "computer"

                self.__window.blit(self.__text, (500, 20))
                self.__board.draw(self.__window)
                pygame.display.update()

        pygame.quit()


from .exceptions import *
from .abstract import ParserInterface
from .raw import RawParser
from .json import JsonParser
from .xml import XmlParser
from .xlsx import XlsxParser
from .ini import IniParser
from .csv import CsvParser

from os import access, R_OK
from os.path import exists
from typing import Any, Mapping, Union
from .commons import *

class FileParser():
    input_path:str
    output_path:str

    raw_content:str
    parsed_content:Mapping

    format:str
    __engine:ParserInterface

    def read(self, input_path:str):
        if not exists(input_path):
            raise InputFileNotFoundException

        if not access(input_path, R_OK):
            raise InputFileNotPermissionToReadException

        self.input_path = input_path
        self.raw_content = ""

        # print warning for xml
        if input_path.endswith('.xml'):
            warn_xml:str = """ Warning: .xml files may exploit vulnerabilities in the xml parsers to perform some malicious attack.
    Please be aware that your input '.xml' file comes from a trusted source."""

            print(warn_xml)
            f = open(input_path, 'rb')
        else:
            f = open(input_path, 'r')

        try:
            f.seek(0)
            self.raw_content = f.read()
        except UnicodeDecodeError:
            fb = open(input_path, 'rb')
            fb.seek(0)
            self.raw_content = fb.read()
            fb.close()
            print("Warning: input file not utf-8. Using bytes")

        f.close()

    def parse(self, format:str, **kwargs) -> Mapping:
        '''put here the implementation'''
        if format == 'raw':
            self.__engine = RawParser()
        elif format == 'csv':
            self.__engine = CsvParser(**kwargs)
        elif format == 'ini':
            self.__engine = IniParser()
        elif format == 'json':
            self.__engine = JsonParser()
        elif format == 'xml':
            self.__engine = XmlParser(**kwargs)
        elif format == 'xlsx':
            self.__engine = XlsxParser()
        else:
            raise NotSupportedFileException

        setattr(self.__engine, 'raw_content', self.raw_content)
        self.format = format
        self.parsed_content = self.__engine.parse_input() # may throw exception
        return self.parsed_content

    def write(self, format:str, output_path:str, **kwargs) -> str:
        _content = kwargs.get('content') if kwargs.get('content') else self.parsed_content
        check_is_not_null(_content)
        check_extension_file(format, output_path)
        self.output_path = output_path

        if format == 'raw':
            self.__engine = RawParser()
        elif format == 'csv':
            self.__engine = CsvParser(**kwargs)
        elif format == 'ini':
            self.__engine = IniParser()
        elif format == 'json':
            self.__engine = JsonParser()
        elif format == 'xml':
            self.__engine = XmlParser(**kwargs)
        elif format == 'xlsx':
            self.__engine = XlsxParser()
        else:
            raise NotSupportedFileException

        self.__engine.parse_output(self.output_path, _content, **kwargs) # may throw exceptions
        return self.output_path

    def __dict__(self) -> Mapping:
        return dict(
            input_path      = self.input_path       if hasattr(self, 'input_path')  else '' \
            ,output_path    = self.output_path      if hasattr(self, 'output_path') else '' \
            ,raw_content    = self.raw_content      if hasattr(self, 'raw_content') else '' \
            ,format         = self.format           if hasattr(self, 'format')      else '' \
            ,parsed_content = self.parsed_content   if hasattr(self, 'parsed_content') else '' \
            )

    def __repr__(self) -> str:
        return str(self.__dict__())

    def __str__(self) -> str:
        return str(self.__dict__())


try:

    import RPi.GPIO as GPIO
    from lib_nrf24 import NRF24
    import time
    import spidev

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(23, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(22, GPIO.OUT, initial=GPIO.LOW)

    print("Transmitter")
    pipes = [0xe7, 0xe7, 0xe7, 0xe7, 0xe7]

    radio = NRF24(GPIO, spidev.SpiDev())
    radio.begin(0, 22)
    radio.setPayloadSize(1)
    radio.setChannel(30)

    radio.setDataRate(NRF24.BR_250KBPS)
    radio.setPALevel(NRF24.PA_MAX)
    radio.setAutoAck(False)
    radio.enableDynamicPayloads()

    radio.openWritingPipe(pipes)
    radio.printDetails()
    print("///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")

    i = 0

    while True:
        #print("Transmitting Ping")
        message = 0xFF
        radio.write(str(message))
        #time.sleep(1)
        i += 1

except KeyboardInterrupt:

    radio.write("HE XAPAT")
    GPIO.output(22,0)
    GPIO.output(23,0)
    #GPIO.output(24,0)
    GPIO.cleanup()


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import importlib
import json
import pstats
import sys
import traceback

from compas.data import DataDecoder
from compas.data import DataEncoder

try:
    from cStringIO import StringIO
except ImportError:
    try:
        from StringIO import StringIO
    except ImportError:
        from io import StringIO

try:
    from cProfile import Profile
except ImportError:
    from profile import Profile

class Dispatcher(object):
    """Base class for remote services.

    Notes
    -----
    This object is used to dispatch API calls to the corresponding functions or methods.
    Since it is run on the server side, all errors are intercepted and their
    message strings assigned to the `'error'` key of the output dictionary
    such that the errors can be rethrown on the client side.

    """

    def on_module_imported(self, module, newly_loaded_modules):
        """Event triggered when a module is successfully imported.

        Override this method when subclassing in order to handle the what happens
        after a module has been imported.

        Parameters
        ----------
        module : module
            A module object.
        newly_loaded_modules : sequence[str]
            A list of module names that have been loaded.

        Returns
        -------
        None

        """
        pass

    def _dispatch(self, name, args):
        """Dispatcher method for XMLRPC API calls.

        This method is automatically called by the XMLRPC server if an instance
        of the dispatcher is registered with the server and the API call dies not
        correspond to a method of the server itself, or of an explicilty registered
        function.

        Parameters
        ----------
        name : str
            Name of the function.
        args : list
            List of positional arguments.
            The first argument in the list should be the JSON serialized string
            representation of the input dictionary. The structure of the input
            dictionary is defined by the caller.

        Returns
        -------
        str
            A JSON serialized string representation of the output dictionary.
            The output dictionary has the following structure:

            * `'data'`    : The returned result of the function call.
            * `'error'`   : The error message of any error that may have been thrown in the processes of dispatching to or execution of the API function.
            * `'profile'` : A profile of the function execution.

        """
        odict = {"data": None, "error": None, "profile": None}

        if len(args) > 1:
            if args[1] not in sys.path:
                sys.path.insert(0, args[1])

        parts = name.split(".")

        functionname = parts[-1]

        try:
            if len(parts) > 1:
                modulename = ".".join(parts[:-1])

                modules_before_import = set(sys.modules.keys())

                # Trigger import
                module = importlib.import_module(modulename)

                newly_loaded_modules = set(sys.modules.keys()) - modules_before_import
                self.on_module_imported(module, newly_loaded_modules)
            else:
                module = self
        except Exception:
            odict["error"] = traceback.format_exc()

        else:
            try:
                function = getattr(module, functionname)
            except AttributeError:
                odict["error"] = "This function is not part of the API: {0}".format(functionname)

            else:
                try:
                    idict = json.loads(args[0], cls=DataDecoder)
                except (IndexError, TypeError):
                    odict["error"] = (
                        "API methods require a single JSON encoded dictionary as input.\n"
                        "For example: input = json.dumps({'param_1': 1, 'param_2': [2, 3]})"
                    )

                else:
                    self._call(function, idict, odict)

        return json.dumps(odict, cls=DataEncoder)

    def _call(self, function, idict, odict):
        """Method that handles the actual call to the function corresponding to the API call.

        Parameters
        ----------
        function : callable
            The callable object corresponding to the requested API call.
        idict : dict
            The input dictionary.
        odict : dict
            The output dictionary.

        Notes
        -----
        The output dictionary will be modified in place.

        """
        args = idict["args"]
        kwargs = idict["kwargs"]

        try:
            data = function(*args, **kwargs)
        except Exception:
            odict["error"] = traceback.format_exc()
        else:
            odict["data"] = data

    def _call_wrapped(self, function, idict, odict):
        """Does the same as _call, but with profiling enabled."""
        args = idict["args"]
        kwargs = idict["kwargs"]

        try:
            profile = Profile()
            profile.enable()

            data = function(*args, **kwargs)

            profile.disable()
            stream = StringIO()
            stats = pstats.Stats(profile, stream=stream)
            stats.strip_dirs()
            stats.sort_stats(1)
            stats.print_stats(20)

        except Exception:
            odict["error"] = traceback.format_exc()
        else:
            odict["data"] = data
            odict["profile"] = stream.getvalue()


"""
购买促销商品
"""
from common.base import open_app
from page.browser_home_page import BrowserHomePage
from page.firm_order_page import FirmOrderPage
from page.sales_goods_page import SalesGoodsPage
import time,pytest,allure

class TestPromotion:
    def setup(self):
        self.driver = open_app()

    def teardown(self):
        self.driver.quit()

    @pytest.allure.severity(pytest.allure.severity_level.CRITICAL)
    @allure.step(title="使用余额购买商品")
    def test_promotion(self):
        allure.attach("点击购买商品", "促销商品")
        b = BrowserHomePage(self.driver) # 实例化主页页面
        b.click_cx_goods()  # 点击促销
        s = SalesGoodsPage(self.driver)  # 实例化促销商品页面
        time.sleep(3)
        allure.attach("点击商品", "立即购买")
        s.click_goods() # 点击商品
        s.click_bay()   # 点击立即购买
        s.click_true()  # 点击确认购买
        # 跳转到确认订单页面
        time.sleep(3)
        f = FirmOrderPage(self.driver) # 先实例化确认订单页面
        time.sleep(3)
        # 获取当前余额
        proice = float(f.get_proice())
        allure.attach("获取购买前的余额", f"{proice}")
        # # 点击使用积分购买
        # f.click_integer()
        # 点击余额购买
        f.click_balance()
        # 获取商品价格
        goodsproice = float(f.get_goods_proice())
        allure.attach("获取需要购买商品的价格", f"{goodsproice}")
        # 点击立即购买
        f.click_order()
        # 输入密码
        f.send_passwd("123456")
        # 点击确定
        allure.attach("点击确认订单", "确认")
        f.click_qtrue()
        time.sleep(3)
        # 返回 获取余额
        f.click_back()
        time.sleep(3)
        f.click_back()
        time.sleep(3)
        f.click_back()
        f.click_my()
        # 获取新的余额
        newproice = float(f.get_new_proice())
        # 获取购物前余额
        allure.attach("获取购买后的余额", f"{newproice}")
        if proice - goodsproice == newproice:
            assert 1
        else:
            assert 0

if __name__ == '__main__':
    pytest.main("-s test_promotion.py")


from django.conf.urls import url, include
from .views import *

urlpatterns = [
    url(r'^profile/$', user_profile),
    url(r'^profile/(?P<pk>[0-9]+)/$', user_profile_by_id),
    url(r'^update_profile/$', user_update_profile),
    url(r'^follow/(?P<pk>[0-9]+)/$', user_follow),
    url(r'^unfollow/(?P<pk>[0-9]+)/$', user_unfollow),
    url(r'following/$', user_following),
    url(r'following/(?P<pk>[0-9]+)/$', user_following_by_id),
    url(r'followers/$', user_followers),
    url(r'followers/(?P<pk>[0-9]+)/$', user_followers_by_id),
    # ...
]


import sys
from typing import Callable

from PyQt6 import QtGui
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QGridLayout
from PyQt6.QtWidgets import QLabel
from PyQt6.QtWidgets import QLineEdit
from PyQt6.QtWidgets import QPushButton
from PyQt6.QtWidgets import QWidget
from savings import PresentRequiredEarningsObserver
from savings import SavingsObserver

class QtRequiredEarningsObserver(PresentRequiredEarningsObserver):
    def __init__(self):
        super().__init__()
        self.required_earnings = None

    def did_present_required_earnings(self, required_earnings: float):
        self.required_earnings = required_earnings

class QtSavingsObserver(SavingsObserver):
    def register_savings_functions(self, present_required_earnings: Callable[[int,
                                                                              float,
                                                                              PresentRequiredEarningsObserver],
                                                                             None]):
        qt_application = QApplication(sys.argv)
        screen_center = QtGui.QGuiApplication.primaryScreen().availableGeometry().center()

        main_window = QWidget()

        desired_savings_title_label = QLabel('Desired Savings')
        desired_savings_value_edit = QLineEdit()

        tax_rate_title_label = QLabel('Tax Rate')
        tax_rate_value_edit = QLineEdit()

        required_earnings_title_label = QLabel('Need to earn')
        required_earnings_value_label = QLabel('-')

        calculate_button = QPushButton('Calculate')

        def handle_calculate_event():
            tax_rate = int(tax_rate_value_edit.text())
            desired_savings = float(desired_savings_value_edit.text())
            observer = QtRequiredEarningsObserver()
            present_required_earnings(tax_rate, desired_savings, observer)
            required_earnings = observer.required_earnings
            required_earnings_value_label.setText('{}'.format(required_earnings))

        calculate_button.clicked.connect(handle_calculate_event)
        desired_savings_value_edit.returnPressed.connect(handle_calculate_event)
        tax_rate_value_edit.returnPressed.connect(handle_calculate_event)

        grid = QGridLayout()
        grid.setSpacing(20)

        grid.addWidget(desired_savings_title_label, 1, 0)
        grid.addWidget(desired_savings_value_edit, 1, 1)

        grid.addWidget(tax_rate_title_label, 2, 0)
        grid.addWidget(tax_rate_value_edit, 2, 1)

        grid.addWidget(required_earnings_title_label, 3, 0)
        grid.addWidget(required_earnings_value_label, 3, 1)

        grid.addWidget(calculate_button, 4, 1)

        main_window.setLayout(grid)

        main_window.setGeometry(300, 300, 300, 150)
        main_window.setWindowTitle('Buttons')

        widget_frame_geometry = main_window.frameGeometry()
        widget_frame_geometry.moveCenter(screen_center)
        main_window.show()
        sys.exit(qt_application.exec())


from django.db import transaction
from rest_framework import serializers

from back.models.invoice import Invoice, InvoiceStatus
from back.models.invoicing_month import InvoicingMonth
from back.models.payment import Payment
from back.serializers.invoice import InvoiceShortSerializer
from back.serializers.payment import PaymentSerializer

class InvoicingMonthSerializer(serializers.ModelSerializer):
    id_mes_facturacion = serializers.ReadOnlyField()
    invoices = InvoiceShortSerializer(many=True, write_only=True, required=False)

    class Meta:
        model = InvoicingMonth
        fields = "__all__"

    @transaction.atomic
    def create(self, validated_data):
        invoicing_month_to_close_query = InvoicingMonth.objects.filter(is_open=True)
        if invoicing_month_to_close_query.count() > 1:
            raise serializers.ValidationError(
                "Existen varios meses de facturación abiertos. Debe revisar este problema."
            )
        invoicing_month_to_close = invoicing_month_to_close_query.first()

        invoicing_month_to_close_payments = Payment.objects.filter(
            mes_facturacion=invoicing_month_to_close
        ).count()
        if invoicing_month_to_close_payments == 0:
            raise serializers.ValidationError(
                "El mes anterior no ha importado ningún pago. Revise si la facturación del mes que va a cerrar está correcta."
            )

        if invoicing_month_to_close is not None:
            invoicing_month_to_close.is_open = False
            invoicing_month_to_close.save()

            last_month_invoices = Invoice.objects.filter(
                mes_facturacion=invoicing_month_to_close
            )
            for last_month_invoice in last_month_invoices:
                if (
                    last_month_invoice.estado == InvoiceStatus.NUEVA
                    or last_month_invoice.estado == InvoiceStatus.PENDIENTE_DE_COBRO
                ):
                    if (
                        last_month_invoice.total is not None
                        and (
                            last_month_invoice.pago_1_al_10
                            + last_month_invoice.pago_11_al_30
                        )
                        >= last_month_invoice.total
                    ):
                        last_month_invoice.estado = InvoiceStatus.COBRADA
                    else:
                        last_month_invoice.estado = InvoiceStatus.NO_COBRADA
                    last_month_invoice.save()

        invoices = validated_data.pop("invoices", [])
        validated_data["id_mes_facturacion"] = validated_data.get(
            "anho"
        ) + validated_data.get("mes")
        validated_data["is_open"] = True
        invoicing_month = InvoicingMonth.objects.create(**validated_data)
        for invoice in invoices:
            invoice["mes_facturacion"] = invoicing_month
            Invoice.objects.create(**invoice)
        return invoicing_month


from flask import Flask, jsonify, request
from flask_cors import CORS, cross_origin
from handler.user import UserHandler
from handler.email import EmailHandler

# Activate
app = Flask(__name__)
# Apply CORS to this app
CORS(app)

@app.route('/')
def greeting():
    return 'Hello, this is the Email DB App 404!'

# USER SERVICES
@app.route('/404/user/create', methods=['POST'])
def insertNewUser():
    print("REQUEST: ", request.json)
    user_data = request.json
    return UserHandler().insertUser(user_data);

@app.route('/404/user/<int:id_user>/friend/add', methods=['POST'])
def addFriend(id_user):
    print("REQUEST: ", request.json)
    user_data = request.json
    return UserHandler().addFriend(id_user,user_data);

@app.route('/404/user/<int:id_user>/friend/remove', methods=['POST'])
def removeFriend(id_user):
    print("REQUEST: ", request.json)
    user_data = request.json
    return UserHandler().removeFriend(id_user,user_data);

@app.route('/404/user/getallusers', methods=['GET'])
def users_handler():
    if request.method == 'GET':
        return UserHandler().getAllUsers()
    else:
        return jsonify("Not Supported"), 405

@app.route('/404/user/<int:id_user>', methods=['GET'])
def user_by_id_handler(id_user):
    if request.method == 'GET':
        return UserHandler().getUserbyId(id_user)
    else:
        return jsonify("Not Supported"), 405

@app.route('/404/user/top10usersinbox', methods=['GET'])
def top10inbox_handler():
    if request.method == 'GET':
        return UserHandler().viewTop10UsersWithMoreEmailsOnInbox()
    else:
        return jsonify("Not Supported"), 405

@app.route('/404/user/top10usersoutbox', methods=['GET'])
def top10outbox_handler():
    if request.method == 'GET':
        return UserHandler().viewTop10UsersWithMoreEmailsOnOutbox()
    else:
        return jsonify("Not Supported"), 405

# END USER SERVICES

# EMAIL SERVICES

@app.route('/404/user/<int:id_user>/email/send', methods=['POST'])
def createNewEmail(id_user):
    print("REQUEST: ", request.json)
    email_data = request.json
    return EmailHandler().createEmail(email_data,id_user);

@app.route('/404/user/<int:id_user>/email/inbox', methods=['GET'])
def retreiveInbox(id_user):
    return EmailHandler().retreiveInbox(id_user);

@app.route('/404/user/<int:id_user>/email/inbox/search', methods=['GET'])
def searchInbox(id_user):
    args = request.args;
    field = args.get("field", default=None, type=str)
    value = args.get("value", default=None, type=str)
    return EmailHandler().searchInbox(id_user,field,value);

@app.route('/404/user/<int:id_user>/email/<int:id_email>/setcategory', methods=['POST'])
def setCategory(id_user,id_email):
    json = request.json
    return EmailHandler().setCategoryEmail(id_user,id_email,json);

@app.route('/404/user/<int:id_user>/email/outbox', methods=['GET'])
def retreiveOutbox(id_user):
    return EmailHandler().retreiveOutbox(id_user);

@app.route('/404/user/<int:id_user>/email/outbox/search', methods=['GET'])
def searchOutbox(id_user):
    args = request.args;
    field = args.get("field", default=None, type=str)
    value = args.get("value", default=None, type=str)
    return EmailHandler().searchOutbox(id_user,field,value);

@app.route('/404/user/<int:id_user>/email/inbox/delete', methods=['POST'])
def deleteInbox(id_user):
    email_delete = request.json
    return EmailHandler().deleteInbox(id_user,email_delete);

@app.route('/404/user/<int:id_user>/email/outbox/delete', methods=['POST'])
def deleteOutbox(id_user):
    email_delete = request.json
    return EmailHandler().deleteOutbox(id_user,email_delete);

@app.route('/404/user/<int:id_user>/email/outbox/update', methods=['POST'])
def updateOutbox(id_user):
    email_update = request.json
    return EmailHandler().updateOutbox(id_user,email_update);

@app.route('/404/user/<int:id_user>/email/inbox/<int:id_email>', methods=['GET'])
def viewInboxEmail(id_user,id_email):
    return EmailHandler().viewInboxEmail(id_user,id_email);

@app.route('/404/user/<int:id_user>/email/outbox/<int:id_email>', methods=['GET'])
def viewInboxOutbox(id_user,id_email):
    return EmailHandler().viewOutboxEmail(id_user,id_email);

@app.route('/404/user/<int:id_user>/email/inbox/<int:id_email>/reply', methods=['POST'])
def replyEmail(id_user,id_email):
    print("REQUEST: ", request.json)
    email_data = request.json
    return EmailHandler().createEmailReply(id_user,id_email,email_data);

@app.route('/404/email/getallemails', methods=['GET'])
def emails_handler():
    if request.method == 'GET':
        return EmailHandler().getAllEmails()
    else:
        return jsonify("Not Supported"), 405

@app.route('/404/email/<int:id_email>', methods=['GET'])
def email_by_id_handler(id_email):
    if request.method == 'GET':
        return EmailHandler().getEmailbyId(id_email)
    else:
        return jsonify("Not Supported"), 405

@app.route('/404/email/mostrecipients', methods=['GET'])
def recipients_handler():
    if request.method == 'GET':
        return EmailHandler().viewEmailMostRecipients()
    else:
        return jsonify("Not Supported"), 405

@app.route('/404/email/mostreplies', methods=['GET'])
def replies_handler():
    if request.method == 'GET':
        return EmailHandler().viewEmailMostReplies()
    else:
        return jsonify("Not Supported"), 405

# END EMAIL SERVICES

if __name__ == '__main__':
    app.run()

# See PyCharm help at https://www.jetbrains.com/help/pycharm/


#!/usr/common/env python3
# -*- coding: utf-8 -*-
#
# describe:
# author: cg
# time: 2018-10-24 21:55

import os
import json
import configparser
from util.configMsg import ConfigMsg

class ConfigureUtil:

    '''
    describe: .conf为后缀的文件操作类, 这里用来操作配置文件
    '''

    def __init__(self, logUtilObj, strDir, strFileName):

        '''
        describe: 构造函数
        :param logUtilObj: 日志文件操作对象
        :param strDir:
        :param strFileName:
        '''

        self.logUtilObj = logUtilObj
        self.strDir = strDir
        self.strFileName = strFileName

        self.configMsgObj = ConfigMsg()

        # self.fileUtilObj = FileUtil()
        # self.strRootPath = os.getcwd()
        # self.configParserObj = configparser.ConfigParser(allow_no_value=True, delimiters=':')

    def initConfig(self, configParserObj):

        '''
        describe: 初始化配置文件, 编码格式为utf-8
        :param configParserObj: configparser的对象
        :return: 1: 初始化成功, -1: 初始化出错
        '''

        # self.fileUtilObj.checkAndCreateDir(self.strDir)
        # if not os.path.exists(self.strDir+self.strFileName):
        #     self.logUtilObj.writerLog('将初始化配置文件')

        intIndex = 0

        try:

            configParserObj.add_section(self.configMsgObj.strExportSessionName)

            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strExportDescribe)
            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strHtmlUrlKey,
                                self.configMsgObj.strHtmlUrlValue)
            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strParCateGoryNameKey,
                                self.configMsgObj.strParCateGoryNameValue)

            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strChiCategoryLabelNameKey,
                                self.configMsgObj.strChiCategoryLabelNameValue)

            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strChiCategoryKeyNameKey,
                                self.configMsgObj.strChiCategoryKeyNameValue)

            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strChiCateGoryNameKey,
                                self.configMsgObj.strChiCateGoryNameValue)
            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strSpiderSleepTimeKey,
                                self.configMsgObj.strSpiderSleepTimeValue)
            configParserObj.set(self.configMsgObj.strExportSessionName, self.configMsgObj.strSaveFilePathKey,
                                self.configMsgObj.strSaveFilePathValue)

            with open(self.strDir + self.strFileName, 'w', encoding='utf-8') as configureFile:
                configParserObj.write(configureFile, space_around_delimiters=True)

        except Exception as error:

            intIndex = -1
            self.logUtilObj.writerLog('配置文件初始化出错' + str(error))

        else:

            intIndex = 1
            self.logUtilObj.writerLog('配置文件已初始化完成(' + self.strDir + self.strFileName + ')')

        return intIndex
        # else:
        #     self.logUtilObj.writerLog('配置文件已经存在(' + self.strDir + self.strFileName + ')')

    def checkAndInitConfigure(self, configParserObj):

        '''
        describe: 检测配置文件是否存在, 若存在则执行检测, 若不存在则初始化创建
        :param configParserObj: configparser的对象
        :return: 1: 存在(也可能是创建后), -1:
        '''

        # 检测配置文件是否存在, 若存在则执行检测, 若不存在则初始化创建
        # configParserObj: configparser的对象

        if not os.path.exists(self.strDir + self.strFileName):
            intIndex = self.initConfig(configParserObj)
        else:

            intIndex = self.checkConfigHasExist(configParserObj, self.configMsgObj.strExportSessionName,
                                     self.configMsgObj.listKey)

            if intIndex == 1:

                self.logUtilObj.writerLog('配置文件已经存在(' + self.strDir + self.strFileName + ')')

        return intIndex

    def getConfig(self, configParserObj):

        '''
        describe: 读取配置文件内容, 注释了不读取，值为空会读取, 读取写入的key名字全部小写
        :param configParserObj: configparser的对象
        :return: 返回一个字典, dict类型
        '''

        dictConfMsg = {}
        if os.path.exists(self.strDir + self.strFileName):
            configParserObj.read(self.strDir + self.strFileName, encoding='utf-8')
            try:
                listSectionName = configParserObj.sections()
            except:

                self.logUtilObj.writerLog("读取配置文件出错")
            else:
                for sectionItem in listSectionName:

                    listKeyName = configParserObj.options(sectionItem)
                    sectionObj = configParserObj[sectionItem]

                    if len(listKeyName) != 0:
                        for keyItem in listKeyName:

                            if '#' not in keyItem:

                                valueItem = sectionObj[keyItem]
                                if valueItem is None:
                                    dictConfMsg[sectionItem] = listKeyName
                                else:
                                    dictConfMsg[keyItem] = valueItem
                            else:
                                continue
                    else:
                        dictConfMsg[sectionItem] = ''

        strConfigMsg = json.dumps(dictConfMsg, indent=3, ensure_ascii=False)
        self.logUtilObj.writerLog('配置文件中读取到的配置有: ' + strConfigMsg)
        return dictConfMsg

    def updateConfigSingleKey(self, configParserObj, strSessionName, strKey, strValue):

        '''
        修改改配置文件, 针对单个key
        :param configParserObj: configparser的对象
        :param strSessionName: 需要更改的strKey对应的上一级名字
        :param strKey: 需要更改的key
        :param strValue: 需要更改的strKey的值
        :return:
        '''

        try:
            configParserObj.set(strSessionName, strKey, strValue)
            with open(self.strDir + self.strFileName, 'w', encoding='utf-8') as configureFile:
                configParserObj.write(configureFile, space_around_delimiters=True)
        except:
            self.logUtilObj.writerLog('更新修改出错: [' + strSessionName + ']' + strKey + ' = ' + strValue)
        else:
            self.logUtilObj.writerLog('已更新修改: [' + strSessionName + ']' + strKey + ' = ' + strValue)

    def updateConfigSingleSession(self, configParserObj, strSessionName, dictMsg):

        '''
        describe: 修改配置文件, 针对单个session块
        :param configParserObj: configparser的对象
        :param strSessionName: 块名字
        :param dictMsg: 块对应的新内容, 字典类型
        :return:
        '''

        configParserObj.read(self.strDir + self.strFileName)
        try:
            for strKey, strValue in dictMsg.items():
                configParserObj.set(strSessionName, strKey, strValue)

            with open(self.strDir + self.strFileName, 'w', encoding='utf-8') as configureFile:
                configParserObj.write(configureFile, space_around_delimiters=True)
        except:
            self.logUtilObj.writerLog('更新修改出错')
        else:
            self.logUtilObj.writerLog('已经更新修改: [' + strSessionName + ']配置块')

    def checkConfigHasExist(self, configParserObj, strSession, listKey):

        '''
        describe: 检测判断配置文件中是否存在session块配置信息,及配置信息的key是否完全
        :param configParserObj: configparser的对象
        :param strSession: 块名字
        :param listKey: list类型, 其元素为strSession块中的配置项的key名字
        :return: 1: 检查的配置项存在, -1: session配置块存在, 但缺少某项key, -2: session配置块不存在
        '''

        intIndex = 0

        configParserObj.read(self.strDir + self.strFileName, encoding='utf-8')
        if configParserObj.has_section(strSession):

            for strKeyItem in listKey:
                if not configParserObj.has_option(strSession, strKeyItem):
                    self.logUtilObj.writerLog('配置文件中的' + strSession + '配置中' + strKeyItem + '的配置项不存在')
                    intIndex = -1
                    break
                else:
                    intIndex = 1
                    pass
        else:
            intIndex = -2
            self.logUtilObj.writerLog('配置文件中不存在' + strSession + '的配置块')

        return intIndex

    @staticmethod
    def getCustomizeConfigParserObj():

        '''
        describe: 静态方法, 获取configparser对象, 返回一个实例化的configparser对象, 实例化时参数可改, 参数的配置需要根据自己的需求
        :return:
        '''

        return configparser.ConfigParser(allow_no_value=True, delimiters=':')


import os
import numpy
from typing import Dict
from flax.traverse_util import flatten_dict
from flax.serialization import msgpack_restore
import torch
torch.set_grad_enabled(False)

def load_vqgan_torch_params(path: str) -> Dict[str, torch.Tensor]:
    with open(os.path.join(path, 'flax_model.msgpack'), "rb") as f:
        params: Dict[str, numpy.ndarray] = msgpack_restore(f.read())

    P: Dict[str, numpy.ndarray] = flatten_dict(params, sep='.')

    for i in list(P.keys()):
        j = i
        if 'up' in i or 'down' in i:
            j = i.replace('_', '.')
            j = j.replace('proj.out', 'proj_out')
            j = j.replace('nin.short', 'nin_short')
        if 'bias' in i:
            P[j] = P.pop(i)
        elif 'scale' in i:
            j = j.replace('scale', 'weight')
            P[j] = P.pop(i)
        elif 'kernel' in i:
            j = j.replace('kernel', 'weight')
            P[j] = P.pop(i).transpose(3, 2, 0, 1)

    for i in P:
        P[i] = torch.tensor(P[i])

    P['embedding.weight'] = P.pop('quantize.embedding.embedding')

    for i in list(P):
        if i.split('.')[0] in ['encoder', 'quant_conv']:
            P.pop(i)

    return P

def load_dalle_bart_flax_params(path: str) -> Dict[str, numpy.ndarray]:
    with open(os.path.join(path, "flax_model.msgpack"), "rb") as f:
        params = msgpack_restore(f.read())

    for codec in ['encoder', 'decoder']:
        k = 'FlaxBart{}Layers'.format(codec.title())
        P: dict = params['model'][codec]['layers'][k]
        P['pre_self_attn_layer_norm'] = P.pop('LayerNorm_0')
        P['self_attn_layer_norm'] = P.pop('LayerNorm_1')
        P['self_attn'] = P.pop('FlaxBartAttention_0')
        if codec == 'decoder':
            P['pre_encoder_attn_layer_norm'] = P.pop('LayerNorm_2')
            P['encoder_attn_layer_norm'] = P.pop('LayerNorm_3')
            P['encoder_attn'] = P.pop('FlaxBartAttention_1')
        P['glu']: dict = P.pop('GLU_0')
        P['glu']['ln0'] = P['glu'].pop('LayerNorm_0')
        P['glu']['ln1'] = P['glu'].pop('LayerNorm_1')
        P['glu']['fc0'] = P['glu'].pop('Dense_0')
        P['glu']['fc1'] = P['glu'].pop('Dense_1')
        P['glu']['fc2'] = P['glu'].pop('Dense_2')

    for codec in ['encoder', 'decoder']:
        layers_params = params['model'][codec].pop('layers')
        params['model'][codec] = {
            **params['model'][codec],
            **layers_params
        }

    model_params = params.pop('model')
    params = {**params, **model_params}

    params['decoder']['lm_head'] = params.pop('lm_head')

    return params

def convert_dalle_bart_torch_from_flax_params(
    params: dict,
    layer_count: int,
    is_encoder: bool
) -> dict:
    P: Dict[str, numpy.ndarray] = flatten_dict(params, sep='.')

    for i in P:
        P[i] = torch.tensor(P[i]).to(torch.float16)

    for i in list(P):
        if 'kernel' in i:
            j = i.replace('kernel', 'weight')
            P[j] = P.pop(i).transpose(-1, -2)
        elif 'scale' in i:
            j = i.replace('scale', 'weight')
            P[j] = P.pop(i)

    for i in list(P):
        j = 'FlaxBart{}Layers'.format('Encoder' if is_encoder else 'Decoder')
        if j in i:
            for l in range(layer_count):
                k = i.replace(j, 'layers.' + str(l))
                P[k] = P[i][l]
            P.pop(i)

    P['embed_tokens.weight'] = P.pop('embed_tokens.embedding')
    P['embed_positions.weight'] = P.pop('embed_positions.embedding')
    return P

def convert_and_save_torch_params(is_mega: bool, model_path: str):
    print("converting params to torch")
    layer_count = 24 if is_mega else 12
    flax_params = load_dalle_bart_flax_params(model_path)
    encoder_params = convert_dalle_bart_torch_from_flax_params(
        flax_params['encoder'],
        layer_count=layer_count,
        is_encoder=True
    )
    decoder_params = convert_dalle_bart_torch_from_flax_params(
        flax_params['decoder'],
        layer_count=layer_count,
        is_encoder=False
    )

    for i in decoder_params:
        decoder_params[i] = decoder_params[i].to(torch.float16)

    for i in encoder_params:
        encoder_params[i] = encoder_params[i].to(torch.float16)

    detoker_params = load_vqgan_torch_params('./pretrained/vqgan')
    detoker_path = os.path.join('pretrained', 'vqgan', 'detoker.pt')

    torch.save(encoder_params, os.path.join(model_path, 'encoder.pt'))
    torch.save(decoder_params, os.path.join(model_path, 'decoder.pt'))
    torch.save(detoker_params, detoker_path)


import logging
from enum import Enum
from typing import Tuple

import numpy as np
from numpy.typing import ArrayLike

from .utils import complex_pchip

MAX_TAYLOR_ITERATIONS = 1000

class InterpolationMode(Enum):
    PCHIP = "pchip"
    LINEAR = "linear"

def _lambda(x: ArrayLike) -> np.ndarray:
    """Calculates the Lambda function defined in equations E.136 in [N.Mounet-PhD]_, which is given by:

    Lambda(x) = -j*exp(j*x)/x + (exp(j*x)-1)/x^2,

    where j = sqrt(-1) is the imaginary unit.

    For \|x\| < 1, a Taylor series approximation is summed until convergence is reached.

    :param x: The input variable x, given as a single float or an array of floats.
    :type x: ArrayLike
    :raises RuntimeError: If the Taylor series summation did not converge after 1000 iterations.
    :return: Lambda, an array with the same shape as x
    :rtype: np.ndarray
    """
    # Make input into array if it is not already, and prepare output arrays
    x = np.asarray(x)
    result = np.zeros_like(1j*x)

    # Set relative tolerance to machine precision
    rel_tolerance = np.finfo(x.dtype).eps

    # Define a mask of where to use a Taylor series approximation
    taylor_mask = np.abs(x) < 1.0

    # Calculate phi and psi for x >= 1
    xx = x[~taylor_mask]
    exp_jx = np.exp(1j*xx)
    result[~taylor_mask] = -1j*exp_jx/xx + (exp_jx-1)/xx**2

    # Set up for convergence loop for x < 1
    xx = x[taylor_mask]
    inv_factorial = 1.
    jx_to_n = np.ones_like(1j*xx) # Starts as x**0
    abs_x_to_n_plus_1 = np.abs(xx) # Starts as x**(0+1)
    exp_abs_x = np.exp(np.abs(xx))

    # Run convergence loop until phi and psi converged
    for n in range(MAX_TAYLOR_ITERATIONS+1):

        # Add next term
        result[taylor_mask] += 1/(n+2) * jx_to_n * inv_factorial

        # Check convergence
        min_component = np.min([np.abs(np.real(result[taylor_mask])), np.abs(np.imag(result[taylor_mask]))], axis=0)
        remaining_term_bound = abs_x_to_n_plus_1*exp_abs_x * inv_factorial * 1/((n+3)*(n+1))
        if np.all(remaining_term_bound < rel_tolerance * min_component):
            break

        # Update variables
        jx_to_n *= 1j*xx
        abs_x_to_n_plus_1 *= np.abs(xx)
        inv_factorial /= n + 1

    # If loop finished without converging, raise error
    if n == MAX_TAYLOR_ITERATIONS:
        raise RuntimeError(f"Lambda calculation failed to converge after {MAX_TAYLOR_ITERATIONS} iterations")

    return result

def _phi_and_psi(x: ArrayLike) -> Tuple[np.ndarray, np.ndarray]:
    """Calculates the Phi and Psi functions defined in equations E.142 and E.143 in [N.Mounet-PhD]_, which are given by:

    Phi(x) = -j*exp(j*x)/x - 6j*(exp(j*x)+1)/x^3 + 12(exp(j*x)-1)/x^4,

    Psi(x) = exp(j*x)/x^2 + 2j*(2*exp(j*x)+1)/x^3 - 6(exp(j*x)-1)/x^4,

    Where j = sqrt(-1) is the imaginary unit.

    For \|x\| < 1, a Taylor series approximation is summed until convergence is reached.

    :param x: The input variable x, given as a single float or an array of floats.
    :type x: ArrayLike
    :raises RuntimeError: If the Taylor series summation did not converge after 1000 iterations.
    :return: Tuple of two arrays: Phi and Psi, with the same shape as x
    :rtype: Tuple[np.ndarray, np.ndarray]
    """

    # Make input into array if it is not already, and prepare output arrays
    x = np.asarray(x)
    phi = np.zeros_like(1j*x)
    psi = np.zeros_like(phi)

    # Set relative tolerance to machine precision
    rel_tolerance = np.finfo(x.dtype).eps

    # Define a mask of where to use a Taylor series approximation
    taylor_mask = np.abs(x) < 1.0

    # Calculate phi and psi for x >= 1
    xx = x[~taylor_mask]
    exp_jx = np.exp(1j*xx)
    phi[~taylor_mask] = (-1j*xx**3 * exp_jx - 6j*xx*(exp_jx+1) + 12*(exp_jx-1))/xx**4
    psi[~taylor_mask] = ( xx**2 * exp_jx + 2j*xx*(2*exp_jx+1) - 6*(exp_jx-1))/xx**4

    # Set up for convergence loop for x < 1
    xx = x[taylor_mask]
    inv_factorial = 1.
    jx_to_n = np.ones_like(1j*xx) # Starts as x**0
    abs_x_to_n_plus_1 = np.abs(xx) # Starts as x**(0+1)
    exp_abs_x = np.exp(np.abs(xx))

    # Run convergence loop until phi and psi converged
    phi_converged = False
    psi_converged = False
    for n in range(MAX_TAYLOR_ITERATIONS+1):

        # Stop loop if converged
        if phi_converged and psi_converged:
            break

        # Only calculate phi if not yet converged (to save computation time)
        if not phi_converged:
            phi[taylor_mask] += (n+6)/((n+3)*(n+4)) * jx_to_n * inv_factorial

            # Check for convergence
            min_phi_component = np.min([np.abs(np.real(phi[taylor_mask])), np.abs(np.imag(phi[taylor_mask]))], axis=0)
            phi_remaining_terms_bound = 2 * abs_x_to_n_plus_1 * exp_abs_x * inv_factorial / ((n+1)*(n+5))
            phi_converged = np.all(phi_remaining_terms_bound < rel_tolerance * min_phi_component)

        # Only calculate psi if not yet converged
        if not psi_converged:
            psi[taylor_mask] += - 1/((n+3)*(n+4)) * jx_to_n * inv_factorial

            # Check for convergence
            min_psi_component = np.min([np.abs(np.real(psi[taylor_mask])), np.abs(np.imag(psi[taylor_mask]))], axis=0)
            psi_remaining_terms_bound = abs_x_to_n_plus_1 * exp_abs_x * inv_factorial / ((n+1)*(n+4)*(n+5))
            psi_converged = np.all(psi_remaining_terms_bound < rel_tolerance*min_psi_component)

        # Update variables
        jx_to_n *= 1j*xx
        abs_x_to_n_plus_1 *= np.abs(xx)
        inv_factorial /= n+1

    # If loop finished without converging, raise error
    if not (phi_converged and psi_converged):
        raise RuntimeError(f"Phi and Psi calculation failed to converge after {MAX_TAYLOR_ITERATIONS} iterations")

    return phi, psi

def _fourier_integral_inf_correction(
    times: ArrayLike,
    omega_end: float,
    func_value_end: ArrayLike,
    func_derivative_end: ArrayLike=0.,
    positive_inf: bool=True
) -> np.ndarray:
    """Calculate the asymptotic correction term as omega -> +-inf of a Fourier integral for the given times.
    Uses a first or second (if func_derivative_end is given) order Taylor expansion around the angular frequency omega_end.

    :param times: Float or 1D array of floats of length M, the time(s) [s]* to compute the fourier integral for
    :type times: ArrayLike
    :param omega_end: An angular frequency [rad/s]* to expand the Taylor series from. If ``positive_inf == True``, this should be the highest angular frequency used. Otherwise, it should be the lowest (closest to -inf).
    :type omega_end: float
    :param func_value_end: An array of shape (X1, X2, ...) containing the values of the function to be transformed evaluated at omega_end
    :type func_value_end: ArrayLike
    :param func_derivative_end: An array of shape (X1, X2, ...) containing the derivative of the function to be transformed, evaluated at omega_end, defaults to 0
    :type func_derivative_end: ArrayLike, optional
    :param positive_inf: Flag set to True if the asymptotic correction towards +inf should be calculated, and False if the correction towards -inf should be calculated
    :type positive_inf: bool, optional
    :return: The asymptotic terms of the Fourier Integral for all times, given as an array of shape (M, X1, X2, ...)
    :rtype: np.ndarray

    \*Though the units s and rad/s are used here, any coherent set of time and angular frequency units will work
    """

    sign = (1 if positive_inf else -1)

    times = np.asarray(times)
    func_value_end = np.asarray(func_value_end)
    func_derivative_end = np.asarray(func_derivative_end)

    # Add axes from function output
    for _ in range(func_value_end.ndim):
        times = times[..., np.newaxis]

    # Add time axis
    func_value_end = func_value_end[np.newaxis, ...]
    func_derivative_end = func_derivative_end[np.newaxis, ...]

    return sign * np.exp(1j*times*omega_end) * (1j*func_value_end/times - func_derivative_end/times**2)

def fourier_integral_fixed_sampling(
    times: ArrayLike,
    frequencies: ArrayLike,
    func_values: ArrayLike,
    pos_inf_correction_term: bool,
    neg_inf_correction_term: bool,
    interpolation: str
) -> np.ndarray:
    """Calculates the fourier integral of a function for the time values given as input:

    integral from fmin to fmax of : exp(2*pi*j*f*t)*func(f)*df,

    where t is the time, fmin is the first frequency in the input ``frequencies``, fmax is either the highest frequency or (positive) infinity, depending on ``inf_correction_term``,
    and func(f) is the input function of frequency. The function is given as an array of outputs corresponding to the array of frequencies given.

    A Filon type algorithm using a either a piecewise cubic Hermite interpolating polynomial ([PCHIP]_) or a piecewise linear polynomial, depending on the interpolation argument.
    Optionally, an asymptotic correction term can also be computed at each end.
    For details on implementation, see [N.Mounet-PhD]_.

    :param times: Float or 1D array of floats of length M, the time(s) [s]* to compute the fourier integral for
    :type times: ArrayLike
    :param frequencies: Float or 1D array of floats of length N, the frequencies [Hz]* the function to be transformed have been evaluated at
    :type frequencies: ArrayLike
    :param func_values: Complex or ND array of complex of shape (N, X1, X2, ...), the outputs of the function to be transformed at the frequencies given as input
    :type func_values: ArrayLike
    :param pos_inf_correction_term: True if an asymptotic correction term towards +infinity should be added, otherwise the integral is effectively truncated at the highest frequency
    :type pos_inf_correction_term: bool
    :param neg_inf_correction_term: True if an asymptotic correction term towards -infinity should be added, otherwise the integral is effectively truncated at the lowest (closest to -inf) frequency
    :type neg_inf_correction_term: bool
    :param interpolation: String either equal to "pchip" or "linear", to select the integration methods using PCHIP or piecewise linear interpolation, respectively.
    :type interpolation: str
    :return: The fourier integral of the input function at the input times, given as an array of shape (M, X1, X2, ...)
    :rtype: np.ndarray

    \*Though the units s and Hz are used here, any coherent set of time and frequency units will work
    """

    assert interpolation in [mode.value for mode in InterpolationMode]
    interpolation_mode = InterpolationMode(interpolation)

    # Turn inputs into arrays if they are not already, switch to angular frequencies
    # Filter out infinities and nan's
    isfinite = np.isfinite(frequencies) & np.all(np.isfinite(func_values), axis=tuple(range(1, len(func_values.shape))))
    if not np.all(isfinite):
        logging.info("Removing infinite frequencies and values from arrays.")

    omegas = 2 * np.pi * np.asarray(frequencies[isfinite])
    func_values = np.asarray(func_values[isfinite])
    times = np.asarray(times)

    # Set up result array, shape (M, X1, X2, ...)
    result = np.zeros((len(times), *[axis_size for axis_size in func_values.shape[1:]]), dtype=complex)

    # Do mode specific setup
    if interpolation_mode == InterpolationMode.PCHIP:
        # Calculate derivatives
        func_derivatives = complex_pchip(omegas, func_values, omegas, derivative_order=1)
        func_derivative_high_end = func_derivatives[-1]
        func_derivative_low_end = func_derivatives[0]

    elif interpolation_mode == InterpolationMode.LINEAR:
        # Set derivative at end to 0
        func_derivative_high_end = 0
        func_derivative_low_end = 0

    else:
        raise NotImplementedError(f"This state should be unreachable. Need to implement derivative calculation for {interpolation_mode}")

    # Add asymptotic correction terms
    if pos_inf_correction_term:
        result += _fourier_integral_inf_correction(
            times=times,
            omega_end=omegas[-1],
            func_value_end=func_values[-1],
            func_derivative_end=func_derivative_high_end,
            positive_inf=True
        )

    if neg_inf_correction_term:
        result += _fourier_integral_inf_correction(
            times=times,
            omega_end=omegas[0],
            func_value_end=func_values[0],
            func_derivative_end=func_derivative_low_end,
            positive_inf=False
        )

    # Reshape arrays for correct broadcasting
    # Now in all arrays except result:
    # - Axis 0  : times
    # - Axis 1  : frequencies
    # - Axis 2+ : func output dims
    # In result, the frequency axis is absent, and axes 1+ correspond to the func output dims

    # Add frequency axis
    times = times[..., np.newaxis]

    # Add time axis
    omegas = omegas[np.newaxis, ...]
    func_values = func_values[np.newaxis, ...]

    # Add new axes for each axis of func_values except the frequency and time axes
    for _ in range(func_values.ndim - 2):
        omegas = omegas[..., np.newaxis]
        times = times[..., np.newaxis]

    # Do the fourier integration
    if interpolation_mode == InterpolationMode.PCHIP:
        func_derivatives = func_derivatives[np.newaxis, ...] # Add time axis
        result += _fourier_integral_fixed_sampling_pchip(times, omegas, func_values, func_derivatives)

    elif interpolation_mode == InterpolationMode.LINEAR:
        result += _fourier_integral_fixed_sampling_linear(times, omegas, func_values)

    else:
        raise NotImplementedError(f"This state should be unreachable. Need to implement fourier integral calculation for {interpolation_mode}")

    return result

def _fourier_integral_fixed_sampling_pchip(
    times: np.ndarray,
    omegas: np.ndarray,
    func_values: np.ndarray,
    func_derivatives: np.ndarray,
) -> np.ndarray:

    # Calculate intermediate values
    delta_omegas = np.diff(omegas, axis=1)
    exp_omegas = np.exp(1j * omegas[:, :-1] * times)
    x = delta_omegas*times
    exp_x = np.exp(1j * x)
    phi_x, psi_x = _phi_and_psi(x)
    phi_minus_x, psi_minus_x = _phi_and_psi(-x)

    # Calculate integral by summing over frequency axis
    result = np.sum(delta_omegas*exp_omegas*(
        func_values[:, :-1] * phi_minus_x * exp_x +
        func_values[:, 1: ] * phi_x -
        delta_omegas*func_derivatives[:, :-1] * psi_minus_x * exp_x +
        delta_omegas*func_derivatives[:, 1: ] * psi_x
    ), axis=1) # Sum over frequency axis

    return result

def _fourier_integral_fixed_sampling_linear(
    times: np.ndarray,
    omegas: np.ndarray,
    func_values: np.ndarray
) -> np.ndarray:

    delta_omegas = np.diff(omegas, axis=1)

    # Calculate integral by summing over frequency axis
    result = np.sum(delta_omegas*(
        func_values[:, :-1]  * np.exp(1j*omegas[:, 1: ]*times) * _lambda(-delta_omegas*times)
        + func_values[:, 1:] * np.exp(1j*omegas[:, :-1]*times) * _lambda( delta_omegas*times)
    ), axis=1) # Sum over frequency axis

    return result


import numpy as np
from math import *
import math as m
import cmath as cm
from numba import cuda

@cuda.jit
def single_sideband_kernel(G, strides, Qx_all, Qy_all, Kx_all, Ky_all, aberrations, aberration_angles, theta_rot, alpha,
                           Ψ_Qp, Ψ_Qp_left_sb, Ψ_Qp_right_sb, eps, lam, scale):
    """

    :param G: array_like , 4D G-function used in ptychography
    :param strides: array_like shape (4,) contains the strides of the G array
    :param Qx_all: array_like, shape (G.shape[1],)
    :param Qy_all: array_like, shape (G.shape[0],)
    :param Kx_all: array_like, shape (G.shape[3],)
    :param Ky_all: array_like, shape (G.shape[2],)
    :param aberrations: array_like, shape (16,)
    :param aberration_angles: array_like, shape (12,)
    :param theta_rot: float, STEM rotation angle in radians
    :param alpha: float, converge half-angle in radians
    :param Ψ_Qp: array_like, shape (G.shape[0], G.shape[1],) to store the result of the weak phase approximation
    :param Ψ_Qp_left_sb: array_like, shape (G.shape[0], G.shape[1],) to store the result of the left sideband
    :param Ψ_Qp_right_sb: array_like, shape (G.shape[0], G.shape[1],) to store the result of the right sideband
    :param eps: float, threshold for which Γ-function values to include in the summation Γ_abs > eps
    :param lam: float, wavelength in Angstrom
    :param scale: float, scale factor to scale the aperture to the measured intensity
    :return: nothing
    """
    def aperture2(qx, qy, lam, alpha_max, scale):
        qx2 = qx ** 2
        qy2 = qy ** 2
        q = m.sqrt(qx2 + qy2)
        ktheta = m.asin(q * lam)
        return (ktheta < alpha_max) * scale

    def chi3(qx, qy, lam, a, phi):
        """
        Creates an aberration surface from aberration coefficients.

        :param qx: M1 x M2 tensor of x coefficients of reciprocal space
        :param qy: M1 x M2 tensor of y coefficients of reciprocal space
        :param lam: wavelength in Angstrom
        :param a10: shift                               in Angstrom
        :param a11: shift                               in Angstrom
        :param a20: defocus                             in Angstrom
        :param a22: two-fold Astigmatism                in Angstrom
        :param a31: axial coma                          in Angstrom
        :param a33: three-fold astigmatism              in Angstrom
        :param a40: spherical aberration                in um
        :param a42: star aberration                     in um
        :param a44: four-fold astigmatism               in um
        :param a51: fourth order axial coma             in um
        :param a53: three-lobe aberration               in um
        :param a55: five-fold astigmatism               im um
        :param a60: fifth-order spherical               in mm
        :param a62: fifth-order two-fold astigmatism    in mm
        :param a64: fifth-order four-fold astigmatism   in mm
        :param a66: siz-fold astigmatism                in mm
        :param phi11: shift angle
        :param phi22: two-fold Astigmatism angle
        :param phi33: three-fold astigmatism angle
        :param phi44: four-fold astigmatism angle
        :param phi55: five-fold astigmatism angle
        :param phi51: fourth order axial coma angle
        :param phi66: Chromatic aberration angle
        :param phi62: fifth-order two-fold astigmatism angle
        :param phi64: fifth-order four-fold astigmatism angle
        :param phi31: axial coma angle
        :param phi42: star aberration angle
        :param phi53: three-lobe aberration angle
        :return: M1 x M1 x (maximum size of all aberration tensors)
        """
        qx2 = qx ** 2
        qy2 = qy ** 2
        q = sqrt(qx2 + qy2)
        ktheta = m.asin(q * lam)

        kphi = m.atan2(qy, qx)
        # print(a0.shape, scales.shape)
        cos = m.cos
        chi = 2.0 * np.pi / lam * (1.0 * (a[1] * cos(2 * (kphi - phi[0])) + a[0]) * ktheta +
                                   1.0 / 2 * (a[3] * cos(2 * (kphi - phi[1])) + a[2]) * ktheta ** 2 +
                                   1.0 / 3 * (a[5] * cos(3 * (kphi - phi[2])) + a[4] * cos(
                    1 * (kphi - phi[3]))) * ktheta ** 3 +
                                   1.0 / 4 * (a[8] * cos(4 * (kphi - phi[4])) + a[7] * cos(2 * (kphi - phi[5])) + a[
                    6]) * ktheta ** 4 +
                                   1.0 / 5 * (a[11] * cos(5 * (kphi - phi[6])) + a[10] * cos(3 * (kphi - phi[7])) + a[
                    9] * cos(
                    1 * (kphi - phi[8]))) * ktheta ** 5 +
                                   1.0 / 6 * (a[15] * cos(6 * (kphi - phi[9])) + a[14] * cos(4 * (kphi - phi[10])) + a[
                    13] * cos(
                    2 * (kphi - phi[11])) + a[12]) * ktheta ** 6)

        return chi

    gs = G.shape
    N = gs[0] * gs[1] * gs[2] * gs[3]
    n = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x
    iqy = n // strides[0]
    iqx = (n - iqy * strides[0]) // strides[1]
    iky = (n - (iqy * strides[0] + iqx * strides[1])) // strides[2]
    ikx = (n - (iqy * strides[0] + iqx * strides[1] + iky * strides[2])) // strides[3]

    if n < N:

        Qx = Qx_all[iqx]
        Qy = Qy_all[iqy]
        Kx = Kx_all[ikx]
        Ky = Ky_all[iky]

        Qx_rot = Qx * cos(theta_rot) - Qy * sin(theta_rot)
        Qy_rot = Qx * sin(theta_rot) + Qy * cos(theta_rot)

        Qx = Qx_rot
        Qy = Qy_rot

        A = aperture2(Ky, Kx, lam, alpha, scale) * cm.exp(1j * chi3(Ky, Kx, lam, aberrations, aberration_angles))
        A_KplusQ = aperture2(Ky + Qy, Kx + Qx, lam, alpha, scale) * cm.exp(1j *
                                                                           chi3(Ky + Qy, Ky + Qx, lam, aberrations,
                                                                                aberration_angles))
        A_KminusQ = aperture2(Ky - Qy, Kx - Qx, lam, alpha, scale) * cm.exp(1j *
                                                                            chi3(Ky - Qy, Kx - Qx, lam, aberrations,
                                                                                aberration_angles))

        Γ = A.conjugate() * A_KminusQ - A * A_KplusQ.conjugate()

        Kplus = sqrt((Kx + Qx) ** 2 + (Ky + Qy) ** 2)
        Kminus = sqrt((Kx - Qx) ** 2 + (Ky - Qy) ** 2)
        K = sqrt(Kx ** 2 + Ky ** 2)
        bright_field = K < alpha / lam
        double_overlap1 = (Kplus < alpha / lam) * bright_field * (Kminus > alpha / lam)
        double_overlap2 = (Kplus > alpha / lam) * bright_field * (Kminus < alpha / lam)

        # print(Γ.real, Γ.imag)
        # Gamma[iqy, iqx, iky, ikx] = Γ
        Γ_abs = abs(Γ)
        take = Γ_abs > eps and bright_field
        if take:
            val = G[iqy, iqx, iky, ikx] * Γ.conjugate()
            cuda.atomic.add(Ψ_Qp.real, (iqy, iqx), val.real)
            cuda.atomic.add(Ψ_Qp.imag, (iqy, iqx), val.imag)
        if double_overlap1:
            val = G[iqy, iqx, iky, ikx]
            cuda.atomic.add(Ψ_Qp_left_sb.real, (iqy, iqx), val.real)
            cuda.atomic.add(Ψ_Qp_left_sb.imag, (iqy, iqx), val.imag)
        if double_overlap2:
            val = G[iqy, iqx, iky, ikx]
            cuda.atomic.add(Ψ_Qp_right_sb.real, (iqy, iqx), val.real)
            cuda.atomic.add(Ψ_Qp_right_sb.imag, (iqy, iqx), val.imag)
        if iqx == 0 and iqy == 0:
            val = abs(G[iqy, iqx, iky, ikx]) + 1j * 0
            cuda.atomic.add(Ψ_Qp.real, (iqy, iqx), val.real)
            cuda.atomic.add(Ψ_Qp_left_sb.real, (iqy, iqx), val.real)
            cuda.atomic.add(Ψ_Qp_right_sb.real, (iqy, iqx), val.real)