import torch
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
import random
import math
import copy
import time
from collections import namedtuple
from itertools import count, compress
from tensorboardX import SummaryWriter
from prop.buffers.priority_replay_buffer import PrioritizedReplayBuffer

Transition = namedtuple('Transition',
                        ('state', 'action', 'next_state', 'reward', 'mask'))

class Agent:
    def __init__(self,
                 env,
                 net,
                 name="",
                 double=True,
                 learning_rate=3e-4,
                 batch_size=128,
                 optimizer=optim.Adam,
                 loss_cutoff=0.1,
                 max_std_dev=-1,
                 epsilon_start=1,
                 epsilon_end=0.1,
                 epsilon_decay=1000,
                 discount=0.99,
                 target_net_update=5000,
                 eval_episodes_count=1000,
                 eval_every=1000,
                 replay_buffer=PrioritizedReplayBuffer,
                 replay_buffer_capacity=1000000,
                 extra_metrics=None,
                 logdir=None,
                 dev=None):
        global device
        device = dev

        self.name = name
        self.double = double                # double q learning
        self.loss_cutoff = loss_cutoff      # training stops at loss_cutoff
        self.max_std_dev = max_std_dev      # max std deviation allowed to stop training; >= 0 to activate
        self.learning_rate = learning_rate  # alpha
        self.batch_size = batch_size
        self.optimizer = optimizer
        self.epsilon_start = epsilon_start  # start with 100% exploration
        self.epsilon_end = epsilon_end      # end with 10% exploration
        self.epsilon_decay = epsilon_decay  # higher value = slower decay
        self.discount = discount            # gamma
        self.target_net_update = target_net_update     # number of steps to update target network
        self.eval_episodes_count = eval_episodes_count # number of episodes to evaluate
        self.eval_every = eval_every # number of steps to run evaluations at
        self.replay_buffer = replay_buffer(replay_buffer_capacity)
        self.env = env
        self.policy_net = net(self.env.observation_space_n, self.env.action_space_n).to(device) # what drives current actions; uses epsilon.
        self.target_net = net(self.env.observation_space_n, self.env.action_space_n).to(device) # copied from policy net periodically; greedy.
        self.logdir = logdir

        # init target_net
        self.target_net.load_state_dict(self.policy_net.state_dict())
        self.target_net.eval()

    def train(self):
        writer = SummaryWriter(logdir=self.logdir, comment=f"-{self.name}" if self.name else "")
        steps = 1
        recent_loss = []
        recent_eval = []
        avg_rewards = 0
        while True:
            # fill replay buffer with one episode from the current policy (epsilon is used)
            self.load_replay_buffer(policy=self.policy_net, steps=steps)

            # sample transitions
            transitions, idxs, is_weights = self.replay_buffer.sample(self.batch_size)
            if len(transitions) < self.batch_size:
                continue

            # optimize policy_net
            loss = self.optimize(transitions, idxs, is_weights)
            # keep track of recent losses and truncate list to latest `eval_every` losses
            recent_loss.append(loss)
            recent_loss = recent_loss[-self.eval_every:]

            # tensorboard metrics
            epsilon = Agent.eps(self.epsilon_start, self.epsilon_end, self.epsilon_decay, steps)
            writer.add_scalar("env/epsilon", epsilon, steps)
            writer.add_scalar("env/replay_buffer", len(self.replay_buffer), steps)
            writer.add_scalar("train/loss", loss, steps)

            # update the target network, copying all weights and biases in policy_net to target_net
            if steps % self.target_net_update == 0:
                self.target_net.load_state_dict(self.policy_net.state_dict())

            # run evaluation
            if steps % self.eval_every == 0:
                avg_rewards, stddev = self.evaluate_policy(self.policy_net)

                writer.add_scalar("train/avg_rewards", avg_rewards, steps)
                writer.add_scalar("train/ep_rewards_std", stddev, steps)

                recent_eval.append(avg_rewards)
                recent_eval = recent_eval[-10:]

                loss_achieved = sum(recent_loss)/len(recent_loss) <= self.loss_cutoff
                avg_rewards_achieved = sum(recent_eval)/len(recent_eval) >= self.env.spec.reward_threshold
                std_dev_achieved = (self.max_std_dev < 0) or (self.max_std_dev >= 0 and stddev <= self.max_std_dev)

                if loss_achieved and avg_rewards_achieved and std_dev_achieved:
                    break

            steps = steps + 1

        # save model
        policy_name = self.name if self.name else "dqn"
        torch.save(self.policy_net.state_dict(), f"policies/{policy_name}")
        writer.close()

    @staticmethod
    def eps(start, end, decay, steps):
        # compute epsilon threshold
        return end + (start - end) * math.exp(-1. * steps / decay)

    @staticmethod
    def legal_actions_to_mask(legal_actions, action_space_n):
        mask = [0]*action_space_n
        for n in legal_actions:
            mask[n] = 1
        return mask

    def load_replay_buffer(self, policy=None, episodes_count=1, steps=0):
        """ load replay buffer with episodes_count """
        for eps_idx in range(episodes_count):
            state = self.env.reset()
            while True:
                legal_actions = self.env.legal_actions
                action = self.select_action(
                    policy=policy,
                    state=state,
                    epsilon=True,
                    steps=steps,
                    legal_actions=legal_actions).item()

                # perform action
                next_state, reward, done, _ = self.env.step(action)

                # insert into replay buffer
                mask = Agent.legal_actions_to_mask(legal_actions, self.env.action_space_n)
                transition = Transition(state, action, next_state if not done else None, reward, mask)
                # set error of new transitions to a very high number so they get sampled
                self.replay_buffer.push(self.replay_buffer.tree.total, transition)

                if done:
                    break
                else:
                    # transition
                    state = next_state

    def evaluate_policy(self, policy):
        ep_rewards = []
        for _ in range(self.eval_episodes_count):
            self.env.seed(time.time())
            state = self.env.reset()
            ep_reward = 0
            while True:
                legal_actions = self.env.legal_actions
                action = self.select_action(
                    policy=policy,
                    state=state,
                    epsilon=False,
                    legal_actions=legal_actions).item()
                next_state, reward, done, _ = self.env.step(action)
                ep_reward += reward

                if done:
                    ep_rewards.append(ep_reward)
                    break
                else:
                    state = next_state

        return np.mean(ep_rewards), np.std(ep_rewards)

    def select_action(self, policy, state, epsilon=False, steps=None, legal_actions=[]):
        """
        selects an action with a chance of being random if epsilon is True,
        otherwise selects the action produced by policy.
        """
        if epsilon:
            if steps == None:
                raise ValueError(f"steps must be an integer. Got = {steps}")

            # pick a random number
            sample = random.random()
            # see what the dice rolls
            threshold = Agent.eps(self.epsilon_start, self.epsilon_end, self.epsilon_decay, steps)
            if sample <= threshold:
                # explore
                action = random.choice([i for i in range(self.env.action_space_n+1) if i in legal_actions])
                return torch.tensor([[action]], device=device, dtype=torch.long)

        # greedy action
        with torch.no_grad():
            # index of highest value item returned from policy -> action
            state = torch.Tensor(state).to(device)
            mask = torch.zeros(self.env.action_space_n).index_fill(0, torch.LongTensor(legal_actions),  1)
            return policy(state, mask).argmax().view(1, 1)

    def optimize(self, transitions, idxs, is_weights):
        # n transitions -> 1 transition with each attribute containing all the
        # data point values along its axis.
        # e.g. batch.action = list of all actions from each row
        batch = Transition(*zip(*transitions))

        # Compute state action values; the value of each action in batch according
        # to policy_net (feeding it a state and emitting an probability distribution).
        # These are the values that our current network think are right and we want to correct.
        state_action_values = self.state_action_values(batch)
        # compute expected state action values (reward + value of next state according to target_net)

        expected_state_action_values = self.expected_state_action_values(batch)

        # calculate difference between actual and expected action values
        batch_loss = F.smooth_l1_loss(state_action_values, expected_state_action_values, reduction='none')
        loss = (sum(batch_loss * torch.FloatTensor(is_weights).unsqueeze(1))/self.batch_size).squeeze()

        # update priority
        for i in range(self.batch_size):
            self.replay_buffer.update(idxs[i], batch_loss[i].item())

        # optimizer
        optimizer = self.optimizer(params=self.policy_net.parameters(), lr=self.learning_rate)
        optimizer.zero_grad()
        # calculate gradients
        loss.backward()
        for param in self.policy_net.parameters():
            # clip gradients
            param.grad.data.clamp_(-1, 1)
        # optimize policy_net
        optimizer.step()

        return loss

    def state_action_values(self, batch):
        """
        Compute Q(s_t, a) - the model computes Q(s_t), then we select the
        columns of actions taken. These are the actions which would've been taken
        for each batch state according to policy_net.
        """
        # list -> tensor
        state_batch = torch.Tensor(batch.state).to(device)
        mask_batch = torch.Tensor(batch.mask).to(device)
        action_batch = torch.Tensor(batch.action).to(device)
        # get action values for each state in batch
        state_action_values = self.policy_net(state_batch, mask_batch)
        # select action from state_action_values according to action_batch value
        return state_action_values.gather(1, action_batch.unsqueeze(1).long())

    def expected_state_action_values(self, batch):
        """
        Compute V(s_{t+1}) for all next states.
        Expected values of actions for non_final_next_states are computed based
        on the "older" target_net; selecting their best reward with max(1)[0].
        This is merged based on the mask, such that we'll have either the expected
        state value or 0 in case the state was final.
        """
        # a bool list indicating if next_state is final (s is not None)
        non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, batch.next_state)), device=device, dtype=torch.bool)
        non_final_next_states = torch.Tensor([s for s in batch.next_state if s is not None]).to(device)
        # get legal actions for non final states; (i, v) -> (list of legal actions, non_final_state)
        next_mask = torch.Tensor([i for (i, v) in zip(list(batch.mask), non_final_mask.tolist()) if v]).to(device)
        # initialize next_state_values to zeros
        next_state_values = torch.zeros(self.batch_size).to(device)

        if len(non_final_next_states) > 0:
            if self.double:
                # double q learning: get actions from policy_net and get their values according to target_net; decoupling
                #                    action selection from evaluation reduces the bias imposed by max in single dqn.
                # next_state_actions: action selection according to policy_net; Q(st+1, a)
                next_state_actions = self.policy_net(non_final_next_states, next_mask).max(1)[1].unsqueeze(-1)
                # next_state_values: action evaluation according to target_net; max Q`(st+1, max Q(st+1, a) )
                next_state_values[non_final_mask] = self.target_net(non_final_next_states, next_mask).gather(1, next_state_actions).squeeze(-1)
            else:
                # max Q`(st+1, a)
                next_state_values[non_final_mask] = self.target_net(non_final_next_states, next_mask).max(1)[0].detach()

        # Compute the expected Q values
        # reward + max Q`(st+1, a) * discount
        reward_batch = torch.Tensor([[r] for r in batch.reward]).to(device)
        state_action_values = reward_batch + (next_state_values.unsqueeze(1) * self.discount)

        return state_action_values


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        size = 0
        current = head
        while current:
            size += 1
            current = current.next

        if size == 0:
            return head

        prev = None
        for i in range(k%size):
            curr = head
            while curr.next:
                prev = curr
                curr = curr.next

            curr.next = head
            head = curr
            prev.next = None

        return head


from datetime import datetime, timedelta
from project.models import User,Fulltext,Paragraphtext
#清除所有超过30天的数据
def cleaning():
    nowtime = datetime.now().date()
    cleantime = (nowtime - timedelta(30))
    fulltext = Fulltext.objects.filter(time__lt=cleantime)
    for text in fulltext:
        Paragraphtext.objects.filter(textid=text).delete()
        text.delete()
    print(fulltext)


from typing import List

import pytest
from pytest_cookies.plugin import Result

_DEPENDENCY_FILE = "pyproject.toml"
_MAKEFILE = "Makefile"
_PYTHON_VERSION_FILE = ".python-version"

@pytest.fixture
def context():
    return {
        "full_name": "Test User",
        "email": "test.user@testland.com",
        "project_name": "Test Bake Project",
        "project_description": "A test project with which to test."
    }

def test_bake(cookies, context):
    # given
    expected_dependencies = ['pytest', 'flake8', 'mypy', 'pre-commit']

    # when
    r: Result = cookies.bake(extra_context=context)
    top_level_files: List[str] = [f.basename for f in r.project.listdir()]

    # then
    assert r.exit_code == 0
    assert r.exception is None
    assert r.project.basename == "test-bake-project"
    assert all([dep in r.project.join(_DEPENDENCY_FILE).read() for dep in expected_dependencies])
    assert '.pre-commit-config.yaml' in top_level_files
    assert '.circleci' in top_level_files

def test_python_version(cookies, context):
    # given
    python_version = "3.8.5"
    context["python_version"] = python_version

    # when
    r: Result = cookies.bake(extra_context=context)

    # then
    assert r.exit_code == 0
    assert python_version in r.project.join(_DEPENDENCY_FILE).read()
    assert python_version in r.project.join(_PYTHON_VERSION_FILE).read()

def test_no_pre_commit(cookies, context):
    # given
    context["use_pre_commit"] = "n"

    # when
    r: Result = cookies.bake(extra_context=context)
    top_level_files: List[str] = [f.basename for f in r.project.listdir()]

    # then
    assert r.exit_code == 0
    assert '.pre-commit-config.yaml' not in top_level_files
    assert 'pre-commit' not in r.project.join(_DEPENDENCY_FILE).read()

def test_no_circle_ci(cookies, context):
    # given
    context["use_circle_ci"] = 'n'

    # when
    r: Result = cookies.bake(extra_context=context)
    top_level_files: List[str] = [f.basename for f in r.project.listdir()]

    # then
    assert r.exit_code == 0
    assert '.circleci' not in top_level_files

@pytest.mark.xfail
def test_email_validation(cookies, context):
    # given
    invalid_email = 'invalid@email@gmail.com'
    context["email"] = invalid_email

    # expecting, when
    with pytest.raises(Exception):
        cookies.bake(extra_context=context)


from PyQt5 import QtCore, QtGui
import numpy as np
from grale.constants import ANGLE_ARCSEC
import openglhelper
import tools
import os

def qImageToArray(img):
    img = img.convertToFormat(QtGui.QImage.Format_ARGB32)
    ptr = img.constBits()
    ptr.setsize(img.byteCount())
    arr = np.array(ptr).reshape(img.height(), img.bytesPerLine())
    if img.bytesPerLine() != img.width()*4:
        arr = arr[:,:img.width()*4]
    arr = arr.reshape(img.height(), img.width(), 4)
    arr = arr.astype(np.float)
    return arr

def arrayToQImage(img):
    img = img.copy()
    img = img.astype(np.uint8)
    destImg = QtGui.QImage(img, img.shape[1], img.shape[0], img.shape[1]*4, QtGui.QImage.Format_ARGB32)
    return destImg

def _retraceCPU(ip, img, center, sizes, subSample):

    import grale.images as images

    cx, cy = center
    cx *= ANGLE_ARCSEC
    cy *= ANGLE_ARCSEC
    w, h = sizes
    w *= ANGLE_ARCSEC
    h *= ANGLE_ARCSEC
    arr = qImageToArray(img)

    totalPlane = None
    for idx in range(4):
        data = arr[:,:,idx].reshape(arr.shape[:2])
        src = images.DiscreteSource(data, w, h, [cx, cy])

        plane = ip.renderImages([src], subSamples=subSample)
        if totalPlane is None:
            totalPlane = np.empty(plane.shape + (4,))
        totalPlane[:,:,idx] = plane

    return arrayToQImage(totalPlane)

def _old_backprojectAndRetrace(imgPlane, img, minXY, maxXY, bpXY,useCPU, subSample, numXYGPU):

    centerX, centerY = [ (minXY[i] + maxXY[i])*0.5 for i in range(2) ]
    widthArcsec, heightArcsec = [ (maxXY[i] - minXY[i]) for i in range(2) ]
    ip = imgPlane
    imgSrc, bl, tr = openglhelper.backProject(ip, img, [centerX, centerY],
                                      [widthArcsec, heightArcsec], [bpXY, bpXY])

    # Recalculate

    w, h = [(tr[i]-bl[i]) for i in [0,1]]
    cx, cy = [ (tr[i]+bl[i])/2 for i in [0,1]]

    if useCPU:
        img = _retraceCPU(ip, imgSrc, [cx, cy], [w, h], subSample)
    else:
        img = imgSrc.mirrored(False, True)
        dims = [ round(numXYGPU*w/h), numXYGPU ] if w < h else [ numXYGPU, round(numXYGPU*h/w) ]
        img = openglhelper.trace(ip, img, [cx, cy], [w, h], dims, subSample)
        img = img.mirrored(False, True)

    return imgSrc, bl, tr, img

def _createRGBLayerForImage(img, fn, layerName, bl, tr, yMirror):
    import imagelayer

    if fn is not None:
        img.save(fn)
        l = imagelayer.RGBImageLayer(fn, layerName)
    else:
        l = imagelayer.RGBImageLayer(img, layerName)

    if yMirror:
        mp = [  [ [0, img.height()], bl],
                [ [0, 0], [ bl[0], tr[1]] ],
                [ [img.width(), img.height()], [tr[0], bl[1]] ],
                [ [img.width(), 0], tr ] ]
    else:
        mp = [  [ [0, 0], bl],
                [ [0, img.height()], [ bl[0], tr[1]] ],
                [ [img.width(), 0], [tr[0], bl[1]] ],
                [ [img.width(), img.height()], tr ] ]

    l.matchToPoints(mp, True)
    return l

def getImageRegions(scene, layers, splitLayers, extra, numImgPix): # Uses aspect ratio

    exportTimeDelays = False
    exportGroups = False
    imgDat, usedLayers = tools.layersToImagesData(layers, splitLayers, exportGroups, exportTimeDelays, pointsLeftInfo=None, ignoreRemainingPoints=True)

    borders = [ ]
    for i in range(imgDat.getNumberOfImages()):
        border = None
        if imgDat.getNumberOfImagePoints(i) < 3: # Not enough points for triangulation or hull, use first point
            border = [ imgDat.getImagePointPosition(i, 0) ]
        else:
            for fn in [ imgDat.getBorder, imgDat.getConvexHull ]:
                try:
                    border = fn(i)
                except Exception as e:
                    print("Warning:", e)

        if not border:
            raise Exception("Unable to get a border for image {}".format(i+1))
        borders.append(border)

    import grale.images as images
    borders = [ np.array(images.enlargePolygon(b, extra))/ANGLE_ARCSEC for b in borders ]

    imgs = [ ]
    for idx in range(len(borders)):
        border = borders[idx]
        img = scene.getSceneRegionImage_minMax(border.min(0), border.max(0), [ numImgPix, None], border)
        imgs.append(img)

    return list(zip(borders, imgs)), usedLayers

def backprojectAndRetrace(imgPlane, bordersAndImages,
                       numBPPix = 1024, # same used in x and y direction, is this ok? perhaps we'd lose information otherwise?
                       numRetracePix = 1024, # Again scaled according to aspect ratio
                       numResample = 1,
                       relensSeparately = True,
                       overWriteFiles = False,
                       bpFileNameTemplate = "img_{srcidx}_backproj.png",
                       bpLayerNameTemplate = "Source shape for image {srcidx}: {fn}",
                       relensFileNameTemplate = "img_{srcidx}_to_{tgtidx}_relensed.png",
                       relensLayerNameTemplate = "Relensed source from image {srcidx} to {tgtidx}: {fn}",
                       newImageDir = None,
                       progressCallback = None):

    ip = imgPlane
    numGPUXY = numRetracePix

    if newImageDir is None:
        newImageDir = os.getcwd()
    if progressCallback is None:
        def dummyCb(msg):
            pass
        progressCallback = dummyCb

    newLayers = [ ]

    if not overWriteFiles: # Check what would be overwritten
        filesToWrite = [ ]
        if relensSeparately:
            for idx in range(len(bordersAndImages)):
                filesToWrite.append(os.path.join(newImageDir, bpFileNameTemplate.format(srcidx=idx+1, tgtidx=0)))

                for tgtidx in range(len(bordersAndImages)):
                    filesToWrite.append(os.path.join(newImageDir, relensFileNameTemplate.format(srcidx=idx+1, tgtidx=tgtidx+1)))
        else:
            for idx in range(len(bordersAndImages)):
                for tmpl in [ bpFileNameTemplate, relensFileNameTemplate ]:
                    filesToWrite.append(os.path.join(newImageDir, tmpl.format(srcidx=idx+1, tgtidx=0)))

        filesToOverwrite = [ fn for fn in filesToWrite if os.path.exists(fn) ]
        if filesToOverwrite:
            raise Exception(f"{len(filesToOverwrite)} files would be overwritten, first are:\n" + "\n".join(filesToOverwrite[:10]))

        if len(set(filesToWrite)) != len(filesToWrite):
            raise Exception("Some output files would overwrite each other")

    def addImg(img, srcidx, tgtidx, bl, tr, isSrc):
        # TODO: yMirror is unused; did I forget something or was this not needed after all?
        #yMirror = True if isSrc else False
        fnTemplate = bpFileNameTemplate if isSrc else relensFileNameTemplate
        layerNameTemplate = bpLayerNameTemplate if isSrc else relensLayerNameTemplate

        fn = None if fnTemplate is None else fnTemplate.format(srcidx=srcidx+1, tgtidx=tgtidx+1)
        newLayers.append(_createRGBLayerForImage(
            img, fn, layerNameTemplate.format(srcidx=srcidx+1, tgtidx=tgtidx+1, fn=fn),
            bl, tr, isSrc))

    srcAreas = []
    for idx in range(len(bordersAndImages)):
        border, img = bordersAndImages[idx]

        if ip:
            progressCallback(f"Creating source shape from image {idx+1}")
            centerX, centerY = 0.5*(border.max(0)+border.min(0))
            widthArcsec, heightArcsec = border.max(0)-border.min(0)
            imgSrc, srcbl, srctr = openglhelper.backProject(ip, img, [centerX, centerY],
                                              [widthArcsec, heightArcsec], [numBPPix, numBPPix])

            srcbl, srctr = np.array(srcbl), np.array(srctr)
        else:
            imgSrc = img.mirrored(False, True)
            srcbl = border.min(0)
            srctr = border.max(0)

        srcCtr = (srcbl+srctr)*0.5
        srcSize = srctr-srcbl

        srcAreas.append([srcbl, srctr])

        addImg(imgSrc, idx, -1, srcbl, srctr, True)

        if numRetracePix <= 0:
            continue

        def getDims(tgtBl, tgtTr):
            tgtW, tgtH = tgtTr-tgtBl
            return [ round(numGPUXY*tgtW/tgtH), numGPUXY ] if tgtW < tgtH else [ numGPUXY, round(numGPUXY*tgtH/tgtW) ]

        if relensSeparately:
            for tgtidx in range(len(bordersAndImages)):
                tgtBorder = bordersAndImages[tgtidx][0]
                tgtBl, tgtTr = tgtBorder.min(0), tgtBorder.max(0)

                dims = getDims(tgtBl, tgtTr)
                tmpImg = imgSrc.mirrored(False, True)

                progressCallback(f"Re-tracing image {tgtidx+1} based on source shape from image {idx+1}")
                tmpImg = openglhelper.trace(ip, tmpImg, srcCtr, srcSize, dims, numResample, tgtBl, tgtTr)
                imgRelens = tmpImg.mirrored(False, True)

                addImg(imgRelens, idx, tgtidx, tgtBl, tgtTr, False)
        else:

            tgtidx = -1

            ri = ip.getRenderInfo()
            tgtBl = np.array(ri["bottomleft"])/ANGLE_ARCSEC
            tgtTr = np.array(ri["topright"])/ANGLE_ARCSEC

            dims = getDims(tgtBl, tgtTr)
            tmpImg = imgSrc.mirrored(False, True)

            progressCallback(f"Re-tracing entire image plane based on source shape from image {idx+1}")
            tmpImg = openglhelper.trace(ip, tmpImg, srcCtr, srcSize, dims, numResample, tgtBl, tgtTr)
            imgRelens = tmpImg.mirrored(False, True)

            addImg(imgRelens, idx, tgtidx, tgtBl, tgtTr, False)

    return newLayers, srcAreas


from typing import List

class Solution:
    @staticmethod
    def is_on_grid(r: int, c: int, m: int, n: int) -> bool:
            return (0 <= r < m) and (0 <= c < n)

    def orangesRotting(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        fresh = set()
        for row in range(m):
            for col in range(n):
                if grid[row][col] == 1:
                    fresh.add((row, col))

        minutes = 0
        updated = True
        while updated:
            updated = False
            to_rot = set()
            for row, col in fresh:
                neighbours = ((row+1, col), (row-1, col), (row, col+1), (row, col-1))
                neighbours = [grid[r][c] for r, c in neighbours if self.is_on_grid(r,c,m,n)]
                if any(neighbour == 2 for neighbour in neighbours):
                    to_rot.add((row, col))

            if len(to_rot) > 0:
                updated = True
                minutes += 1
                for row, col in to_rot:
                    grid[row][col] = 2
                    fresh.remove((row, col))

        return -1 if len(fresh) > 0 else minutes


import CNNModels.icentia11k_make_wfdb as wfdbcreator
import numpy as np
import random
import scipy.signal as siglib
import numpy as np
from ecgdetectors import Detectors

# This function is called by the data generator to create one batch of data.
# patient: number of the patient in the icentia dataset
# subset: number of the subset from the patient that is used
# batchsize: the size of the batch
def getData_1dcnn(patient, subset, batchsize=1024, multi=True, nperseg_value=64):
    number_as_string = f'{patient:05d}'
    path = '/shared_data/icentia11k/'+number_as_string+'_batched.pkl.gz'
    signals, signal_lab = icentiaDataProcessor(path)
    startindex = subset*batchsize
    endindex = (subset+1)*batchsize
    signals = signals[startindex:endindex]
    signal_lab = signal_lab[startindex:endindex]
    # ab hier kommt dein eigener code, es wird über signals iteriert und für jedes einzelen signal dann die passende vorverarbeitung durchgeführt, so wie du sie brauchst
    # zusätzlich wird für jedes label ein codierung anglegt, aber das wäre dann wieder abhängig von deiner verarbeitung
    spectro = []
    spectro_label = []
    for i in range(len(signals)):
        signal = signals[i]
        signal = siglib.resample(signal, int(len(signal)*1.2))
        signal = np.ravel(signal)
        signal = preprocessData(signal, 9000, 300, False)
        f, t, Sxx = siglib.spectrogram(signal, fs=300, nfft=512, nperseg=64)
        Sxx = 10*np.log10(Sxx+1e-12)
        Sxx = np.reshape(Sxx, (Sxx.shape[0], Sxx.shape[1], 1))

        if signal_lab[i] == 'N':  # Zuordnung zu "Normal"
            label = np.asarray([1, 0, 0, 0])

        if signal_lab[i] == 'A':  # Zuordnung zu "Vorhofflimmern"
            label = np.asarray([0, 1, 0, 0])

        if multi:  # Zuordnung zu "Noise"
            if signal_lab[i] == '~':
                label = np.asarray([0, 0, 1, 0])

            if signal_lab[i] == 'O':  # Zuordnung zu "Other"
                label = np.asarray([0, 0, 0, 1])
        spectro.append(Sxx)
        spectro_label.append(label)
        del f, t, Sxx, label
    return spectro, spectro_label

# This function is called by the data generator to create one batch of data.
# patient: number of the patient in the icentia dataset
# subset: number of the subset from the patient that is used
# batchsize: the size of the batch
def getData_spectro(patient, subset, batchsize=1024, multi=True, nperseg_value=64):
    number_as_string = f'{patient:05d}'
    path = '/shared_data/icentia11k/'+number_as_string+'_batched.pkl.gz'
    signals, signal_lab = icentiaDataProcessor(path)
    startindex = subset*batchsize
    endindex = (subset+1)*batchsize
    signals = signals[startindex:endindex]
    signal_lab = signal_lab[startindex:endindex]
    spectro = []
    spectro_label = []
    for i in range(len(signals)):
        signal = signals[i]
        signal = siglib.resample(signal, int(len(signal)*1.2))
        signal = np.ravel(signal)
        signal = preprocessData(signal, 9000, 300, False)
        f, t, Sxx = siglib.spectrogram(signal, fs=300, nfft=512, nperseg=64)
        Sxx = 10*np.log10(Sxx+1e-12)
        Sxx = np.reshape(Sxx, (Sxx.shape[0], Sxx.shape[1], 1))

        if signal_lab[i] == 'N':  # Zuordnung zu "Normal"
            label = np.asarray([1, 0, 0, 0])

        if signal_lab[i] == 'A':  # Zuordnung zu "Vorhofflimmern"
            label = np.asarray([0, 1, 0, 0])

        if multi:  # Zuordnung zu "Noise"
            if signal_lab[i] == '~':
                label = np.asarray([0, 0, 1, 0])

            if signal_lab[i] == 'O':  # Zuordnung zu "Other"
                label = np.asarray([0, 0, 0, 1])
        spectro.append(Sxx)
        spectro_label.append(label)
        del f, t, Sxx, label
    return spectro, spectro_label

# function opens corresponding file and returns the fitting signals and labels

def icentiaDataProcessor(path):
    lischte = wfdbcreator.make_wfdb(path)

    # import all data
    records = []
    labels = []
    timestamps = []
    for i in range(len(lischte)):
        records.append(lischte[i][0])
        labels.append(lischte[i][1])
        timestamps.append(lischte[i][2])
        where = np.where(labels[i] != '')
        labels[i] = labels[i][where]
        timestamps[i] = timestamps[i][where]

    # create segments
    segments = []
    segments_label = []

    for j in range(len(records)):
        for i in range(len(labels[j])-1):
            if(labels[j][i] == '(N' and labels[j][i+1] == ')'):
                segments_label.append('N')
                segments.append(
                    records[j][timestamps[j][i]:timestamps[j][i+1]])
            elif(labels[j][i] == '(AFIB' and labels[j][i+1] == ')'):
                segments_label.append('A')
                segments.append(
                    records[j][timestamps[j][i]:timestamps[j][i+1]])
            elif(labels[j][i] == '(AFL' and labels[j][i+1] == ')'):
                segments_label.append('O')
                segments.append(
                    records[j][timestamps[j][i]:timestamps[j][i+1]])
            elif(labels[j][i] == 'None' or labels[j][i] == ')'):
                pass
            else:
                pass

    # length of recordings that I want to create, similar to the length of recordings in the original trainingsset
    random_numbers = np.load('patientdata_evaluated/random_numbers.npy')
    max_duration = 60

    signals = []
    signals_label = []
    fs = 250
    for i in range(len(segments)):
        current_segment = segments[i]
        index = 0
        while(len(current_segment) > fs*max_duration):
            time = random_numbers[index]
            new_signal = current_segment[0:fs*time]
            current_segment = current_segment[fs*time:-1]
            signals.append(new_signal)
            signals_label.append(segments_label[i])
            index = index+1

    return (signals, signals_label)

# Preprocessing of the incoming data
# the signal is cut before the first and after the last detected r_peak if it is to short
# than the signal is repeated multiple times to reach the desired length
# afterwards the signal is normalized

def preprocessData(data, targetsize, samplefreq, debug=False):
    ret_signal = 0
    repeat_signal = 0
    # looking for r_peaks and repeating the signal
    if(data.size < targetsize):
        if(debug):
            print('Length of the signal ->'+str(data.size) +
                  '<- is shorter than '+str(targetsize))
        detector = Detectors(samplefreq)
        try:
            r_peaks = detector.hamilton_detector(data)
            if(debug):
                print('R-Peaks are detected!')
            repeat_signal = data[r_peaks[0]:r_peaks[-1]]
        except:
            if(debug):
                print('R-Peaks could not be detected!')
            repeat_signal = data

        if(repeat_signal.size == 0):
            if(debug):
                print('Something went wrong, just use the original signal to repeat.')
            repeat_signal = data
            if(debug and repeat_signal.size == 0):
                print('I have no idea.')
        repeats = int(np.ceil(targetsize / repeat_signal.size))
        if(debug):
            print('Signal is repeated ' + str(repeats) + ' times.')
        for i in range(repeats):
            ret_signal = np.append(ret_signal, repeat_signal)
    else:
        if(debug):
            print('Length of the signal ->'+str(data.size) +
                  '<- is bigger than '+str(targetsize))
        ret_signal = data

    # cutting the signal to the desired length
    # same ammount is cut at the start and the end of the signal
    if(ret_signal.size > targetsize):
        if(debug):
            print('Signal is cut to length: ' + str(targetsize))
        cutindex = int(((ret_signal.size-targetsize)/2))
        ret_signal = ret_signal[cutindex:-1]
        ret_signal = ret_signal[0:targetsize]
        if(debug):
            print('Size of the new signal: ' + str(ret_signal.size))

    # normalising the signal
    ret_signal = (ret_signal - np.min(ret_signal)) / \
        (np.max(ret_signal) - np.min(ret_signal))

    return ret_signal


#!/usr/bin/env python

'''Build/run ubxlib for Linux and report results.'''
import os                    # For sep(), getcwd(), listdir()
from logging import Logger
from scripts import u_connection, u_monitor, u_report, u_utils
from scripts.u_logging import ULog
from tasks import nrfconnect

# Prefix to put at the start of all prints
PROMPT = "u_run_linux_"

# The logger
U_LOG: Logger = None

DEFAULT_CMAKE_DIR = f"{u_utils.UBXLIB_DIR}/port/platform/zephyr/runner_linux"
DEFAULT_BOARD_NAME = "native_posix"
DEFAULT_OUTPUT_NAME = f"runner_{DEFAULT_BOARD_NAME}"
DEFAULT_BUILD_DIR = os.path.join("_build", "nrfconnect")

# The guard time waiting for a lock on the HW connection seconds
CONNECTION_LOCK_GUARD_TIME_SECONDS = u_connection.CONNECTION_LOCK_GUARD_TIME_SECONDS

# The guard time for running tests in seconds
RUN_GUARD_TIME_SECONDS = u_utils.RUN_GUARD_TIME_SECONDS

# The inactivity time for running tests in seconds
RUN_INACTIVITY_TIME_SECONDS = u_utils.RUN_INACTIVITY_TIME_SECONDS

# Regex to capture a string of the form:
#
# UART_1 connected to pseudotty: /dev/pts/5
#
# ...where it would return "1" in the first capture group and "/dev/pts/5"
# in the second capture group. This tested on https://regex101.com/ with
# Python the selected regex flavour.
UART_TO_DEVICE_REGEX = r"(?:^UART_([0-9]*) connected to pseudotty: (.*?))$"

# List of device redirections that need to be terminated when done
DEVICE_REDIRECTS = []

# The baud rate to use: this is necessary since the redirection process
# doesn't seem to work for real TTYs unless it is given as a parameter
# to the socat utility which does the redirection
UART_BAUD_RATE = 115200

def uart_to_device_list_create(u_flags, logger):
    '''Create a UART context by parsing u_flags'''
    uart_to_device_list = []

    # Parse u_flags to find values for the following:
    # U_CFG_TEST_UART_A: if present, create an object in the
    # uart_to_device array of type "U_CFG_TEST_UART_A" and
    # set the value of "uart" to this
    # U_CFG_TEST_UART_B: as for U_CFG_TEST_UART_A
    # U_CFG_APP_xxxx_UART: for each value present, find the
    # corresponding U_CFG_APP_xxxx_UART_DEV and create an
    # object in uart_to_device list for each one, including
    # the type, the UART number and the device, e.g.:
    #
    # {"type": "U_CFG_APP_CELL_UART", "uart": "1", "device": "/dev/tty/3"}

    for flag in u_flags:
        if flag.startswith("U_CFG_TEST_UART_A") or flag.startswith("U_CFG_TEST_UART_B"):
            parts = flag.split("=")
            if parts and len(parts) > 1:
                uart_to_device = {}
                uart_to_device["type"] = parts[0]
                uart_to_device["uart"] = parts[1]
                uart_to_device_list.append(uart_to_device)
                logger.info(uart_to_device["type"] + f': will redirect UART_' + \
                            uart_to_device["uart"])
        elif flag.startswith("U_CFG_APP_") and "_UART=" in flag:
            parts = flag.split("=")
            if parts and len(parts) > 1:
                uart_to_device = {}
                uart_to_device["type"] = parts[0]
                uart_to_device["uart"] = parts[1]
                uart_to_device_list.append(uart_to_device)
        elif flag.startswith("U_CFG_APP_") and "_UART_DEV=" in flag:
            parts = flag.split("=")
            if parts and len(parts) > 1:
                uart_type = parts[0].split("_DEV")[0]
                for uart_to_device in uart_to_device_list:
                    if uart_to_device["type"] == uart_type:
                        uart_to_device["device_to"] = parts[1]
                        logger.info(uart_to_device["type"] + f': redirecting UART_' + \
                                    uart_to_device["uart"] + f' to ' + uart_to_device["device_to"])
                        break
    # When done, check if there were any U_CFG_APP_xxx_UART
    # entries without a corresponding U_CFG_APP_xxx_UART_DEV entry
    # and delete them
    delete_list = []
    for idx, uart_to_device in enumerate(uart_to_device_list):
        flag = uart_to_device["type"]
        if flag.startswith("U_CFG_APP_") and flag.endswith("_UART") and \
           "device_to" not in uart_to_device:
            delete_list.append(idx)
    # Remove the largest index items first so as not to change
    # the order of the list as we go
    delete_list.sort(reverse=True)
    for item in delete_list:
        uart_to_device_list.pop(item)

    return uart_to_device_list

def callback(match, uart_to_device_list, results, reporter):
    '''Redirect a UART based on the match and the list of UARTs we care about'''

    del results

    # match group 1 will contain the UART number and match group 2
    # the device it appears on.  Need to search through the
    # UART device list to determine what to do with them
    # Note that the following are all string compares, which
    # should be fine
    uart_number = match.group(1)
    device_from = match.group(2)
    for uart_to_device in uart_to_device_list:
        if uart_to_device["uart"] == uart_number:
            if "done" not in uart_to_device:
                # This is a UART number for which we have a mapping that's not
                # been done. First store the device_from string
                uart_to_device["device_from"] = device_from
                message = ""
                device_a = ""
                device_b = ""
                if uart_to_device["type"].startswith("U_CFG_TEST_UART_"):
                    # This is one we want to loop back: see if we have
                    # the other UART loopback entry in the list also
                    other_end = {}
                    wanted = "_B"
                    if uart_to_device["type"].endswith("_B"):
                        wanted = "_A"
                    for temp in uart_to_device_list:
                        if temp["type"].endswith(wanted):
                            other_end = temp
                            break
                    if other_end:
                        if "device_from" in other_end and "done" not in other_end:
                            # Have both ends and the loop-back has
                            # not been done so do it now
                            message = uart_to_device["type"] + "/" + other_end["type"] + \
                                      ": " + uart_to_device["device_from"] + \
                                      " (UART_" + uart_to_device["uart"] + \
                                      ") will be looped back to " + \
                                      other_end["device_from"] + " (UART_" + \
                                      other_end["uart"] + ")"
                            device_a = uart_to_device["device_from"]
                            device_b = other_end["device_from"]
                            # Mark both as done
                            other_end["done"] = True
                            uart_to_device["done"] = True
                    else:
                        # Don't have the other end in our list so if this
                        # is the UART of U_CFG_TEST_UART_A then loop it back
                        # on itself
                        if uart_to_device["type"] == "U_CFG_TEST_UART_A":
                            message = uart_to_device["type"] + ": " + \
                                      uart_to_device["device_from"] + " (UART_" + \
                                      uart_to_device["uart"] + ") will be looped back on itself"
                            device_a = uart_to_device["device_from"]
                            device_b = uart_to_device["device_from"]
                            # Mark it as done
                            uart_to_device["done"] = True
                else:
                    # This is not a looped-back one, it is a simple
                    # forwarding case
                    message = uart_to_device["type"] + ": " + \
                              uart_to_device["device_from"] + " (UART_" + \
                              uart_to_device["uart"] + ") will be redirected to " + \
                              uart_to_device["device_to"]
                    device_a = uart_to_device["device_from"]
                    device_b = uart_to_device["device_to"]
                    # Mark it as done
                    uart_to_device["done"] = True
                if device_a and device_b:
                    # Actually do it
                    DEVICE_REDIRECTS.append(u_utils.device_redirect_start(device_a, device_b, UART_BAUD_RATE))
                if reporter and message:
                    reporter.event(u_report.EVENT_TYPE_TEST,
                                   u_report.EVENT_INFORMATION,
                                   message)
            break

def run(ctx, instance, platform, board_name=DEFAULT_BOARD_NAME, build_dir=DEFAULT_BUILD_DIR,
        output_name=DEFAULT_OUTPUT_NAME, defines=None, connection=None, connection_lock=None):
    '''Build/run on Linux'''
    return_value = -1
    instance_text = u_utils.get_instance_text(instance)

    # "global" should be avoided, but we make an exception for the logger
    global U_LOG # pylint: disable=global-statement
    U_LOG = ULog.get_logger(PROMPT + instance_text)

    # Currently only support Linux beneath Zephyr
    if platform.lower() == "zephyr":
        ctx.reporter.event(u_report.EVENT_TYPE_BUILD,
                           u_report.EVENT_START,
                           "Linux")
        # Perform the build
        exe_path = os.path.abspath(os.path.join(build_dir, "zephyr", "zephyr.exe"))
        if os.path.isfile(exe_path):
            os.remove(exe_path)
        nrfconnect.build(ctx, cmake_dir=DEFAULT_CMAKE_DIR, board_name=board_name,
                         output_name=output_name, build_dir=build_dir, u_flags=defines)
        # Build has succeeded, we should have an executable
        if os.path.isfile(exe_path):
            # Lock the connection in order to run
            with u_connection.Lock(connection, connection_lock,
                                   CONNECTION_LOCK_GUARD_TIME_SECONDS,
                                   logger=U_LOG) as locked_connection:
                if locked_connection:
                    # For Linux-under-Zephyr we need to capture the first two
                    # lines that the Zephyr executable prints out, which will
                    # be of the following form:
                    #
                    # UART_1 connected to pseudotty: /dev/pts/5
                    # UART_0 connected to pseudotty: /dev/pts/3
                    #
                    # ...and redirect those devices either to loop-back(s) or
                    # to other devices, as defined by flags set in DATABASE.md.
                    uart_to_device_list = uart_to_device_list_create(defines, logger=U_LOG)
                    if uart_to_device_list:
                        u_monitor.callback(callback, UART_TO_DEVICE_REGEX, uart_to_device_list)

                    # Start the .exe and monitor what it spits out
                    try:
                        with u_utils.ExeRun([exe_path], logger=U_LOG) as process:
                            return_value = u_monitor.main(process,
                                                          u_monitor.CONNECTION_PROCESS,
                                                          RUN_GUARD_TIME_SECONDS,
                                                          RUN_INACTIVITY_TIME_SECONDS,
                                                          None, instance,
                                                          ctx.reporter,
                                                          ctx.test_report)
                            if return_value == 0:
                                ctx.reporter.event(u_report.EVENT_TYPE_TEST,
                                                   u_report.EVENT_COMPLETE)
                            else:
                                ctx.reporter.event(u_report.EVENT_TYPE_TEST,
                                                   u_report.EVENT_FAILED)
                        # Remove the redirections
                        for device_redirect in DEVICE_REDIRECTS:
                            u_utils.device_redirect_stop(device_redirect)
                    except KeyboardInterrupt as ex:
                        # Remove the redirections in case of CTRL-C
                        for device_redirect in DEVICE_REDIRECTS:
                            u_utils.device_redirect_stop(device_redirect)
                        raise KeyboardInterrupt from ex
                else:
                    ctx.reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE,
                                       u_report.EVENT_FAILED,
                                       "unable to lock a connection")
        else:
            return_value = 1
            ctx.reporter.event(u_report.EVENT_TYPE_BUILD,
                               u_report.EVENT_FAILED,
                               "check debug log for details")
    else:
        return_value = 1
        ctx.reporter.event(u_report.EVENT_TYPE_INFRASTRUCTURE,
                           u_report.EVENT_FAILED,
                           "unsupported platform \"" + platform + "\"")

    return return_value


import requests
import json
import re
from operator import itemgetter

# Python program to convert API endpoint to Stata help document
url = 'https://educationdata-stg.urban.org/api/v1/api-endpoints/'
res = requests.get(url).text
data = json.loads(res)
keep_str = []

# Temporarily hide endpoints
hide = ["college-university ipeds academic-charges-professional","college-university ipeds academic-charges-general","college-university ipeds program-year-charges","college-university ipeds program-year-charges-cip","college-university ipeds student-financial-aid"]

# Translate from long name to short name
translate = {"college-university":"college", "schools":"school", "school-districts":"district"}

# Break description up for Stata to read
def break_desc(t1, t2):
	max_len = 80
	lineone = len(t1)
	temp_str = ""
	tsplit = re.sub(' +', ' ', t2)
	tsplit = tsplit.split()
	cur_line = " " + t1
	for t in tsplit:
		test_str = cur_line + " " + t
		if len(test_str) > max_len:
			temp_str = temp_str + cur_line[1:] + '\n'
			cur_line = " " + t
		else:
			cur_line = cur_line + " " + t
	temp_str = temp_str + cur_line[1:] + '\n'
	return temp_str.replace(t1 + " ", '')

for i in data["results"]:
	res_str = ""
	ep = i["endpoint_url"].replace('/api/v1/','').split('/')
	for j in ep:
		if len(j) > 0:
			if j[0] != '{':
				res_str = res_str + " " + j
	res_str = res_str[1:]
	if i["description"] == "": i["description"] = "No description at this time."
	i["description"] = break_desc(res_str,i["description"])
	rsplit = res_str.split()
	term = translate[rsplit[0]]
	for j in range(1,len(rsplit)):
		term += " " + rsplit[j]
	if res_str not in hide and i["hide"] == 0:
		keep_str.append(['{bf:"' + term + '"}: ' + i["description"] + '\n'.replace('\n\n\n','\n\n').replace('\n\n\n','\n\n'),res_str.split()[0],int(i["order"])])

# To do - sort and convert to string, and convert long names to short
keep_str = sorted(keep_str, key=itemgetter(1,2))
prev_cat = ""
perm_str = ""
sover = 0
dsname = {"college":"colleges","district":"school-districts","school":"schools"}
for i in range(len(keep_str)):
	if keep_str[i][1] != prev_cat:
		sover = 0
		prev_cat = keep_str[i][1]
	if sover == 0:
		perm_str += '{bf:' + translate[keep_str[i][1]].title() + '} - {browse "https://educationdata.urban.org/documentation/' + dsname[translate[keep_str[i][1]]] + '.html":Read Complete Documentation}\n\n'
		sover = 1
	perm_str += keep_str[i][0]

# replace url from HTML to STATA helper function language
urls = {"https://developers.arcgis.com/rest/geocode/api-reference/geocoding-service-output.htm": "here",
		"https://www.urban.org/research/publication/ipeds-finance-user-guide": "here",
		"http://www.doe.virginia.gov/statistics_reports/index.shtml": "website"}

for url in urls:
	url_html = fr'<a\s+href="{url}">{urls[url]}</a>'
	url_sthlp = f'{{browse "{url}":{urls[url]}}}'
	perm_str = re.sub(url_html, url_sthlp, perm_str)

perm_str = re.sub("–", "–", perm_str)

if ('href' in perm_str) or ("–" in perm_str):
	raise ValueError('Please fix the HTML URLs in the text.')

with open('sthlp_table.txt', 'w') as f:
	f.write(perm_str)


# coding:utf-8
# 第1种解法
# 把列表转换为整数再加1，然后把列表转为str类型，利用str的迭代，转换为列表
def plusOne(digits):
    sums = 0
    for i in digits:
        sums = sums * 10 + i #10进制乘以10，进行累和；
    sums_str = str(sums + 1)
    return [int(j) for j in sums_str]
print(plusOne([4,5,9]))

# 第2种解法
'''
1.熟悉python''.join()函数的用法
2.由于要加1，所以需先转换为整数才能加1
'''
def plusOne2(digits):
    return [int(j) for j in str(int(''.join(str(i) for i in digits)) + 1)]
print(plusOne2([1,2,3]))