import matplotlib.pyplot as plt
import math
import numpy as np
from matplotlib import animation

delta_x, c ,r , k = 0.001, 300 , 1 , 1000
delta_t=delta_x/c
T=0.04
N=int(T*c/delta_x)
t=np.linspace(0,T,N+1)

def next_step(y_previous,y_current):
    y_next=[0]
    c1,c2=2*(1-r**2),r**2
    for i in range(1,len(y_current)-1):
        y_next.append(c1*y_current[i]-y_previous[i]+c2*(y_current[i-1]+y_current[i+1]))
    y_next.append(0)
    return y_next

def initial_Gaussian(x_excite):#initialize or excite the string in some point in the gaussian way
    y_initial=[]
    for i in range(1+int(1./delta_x)):
        y_initial.append(math.exp(-k*(i*delta_x-x_excite)**2))
    return y_initial

def initial_real(x_excite):#initialize or excite the string in some point in the real way
    y_initial=[]
    for i in range(int(x_excite/delta_x)):
        y_initial.append(i*delta_x/x_excite)
    for i in range(int(x_excite/delta_x),1+int(1./delta_x)):
        y_initial.append((i*delta_x-1.)/(x_excite-1))
    return y_initial

def motion_of_a_point(x_observe,x_excite):
    y_initial=initial_real(x_excite) #this can be changed accrodingly
    y_observe=[]                  #calculate the displacement at x=0.05 or y(i=I,n)
    I=int(x_observe/delta_x)
    y0=y_initial
    y1=y_initial
    y_observe.append(y1[I])
    for i in range(N):
        y2=next_step(y0,y1)
        y0,y1=y1,y2
        y_observe.append(y1[I])
    return y_observe

#plot
x_observe_list,x_ex=[0.05,0.1,0.2,0.3],0.45

x_ob=x_observe_list[0]
yx=motion_of_a_point(x_ob,x_ex)
ps=abs(np.fft.rfft(yx))**2
freq= np.linspace(0, c/delta_x/2, len(ps))

plt.subplot(1,2,1)
plt.plot(t,yx)
plt.title('String Signal versus Time')
plt.xlabel('time(s)')
plt.ylabel('Signal(arbitrary units)')
#plt.text(0.,0.4,r'R Wavepacket,$y_0(x)=exp[-1000\times(x-%s)^2]$'%x_ex+'\n$x_{observe}=%s$'%x_ob)
plt.text(0,0.12,'Real Wavepacket of Two Straight Lines')
plt.xlim(0,T)

plt.subplot(1,2,2)
plt.plot(freq,ps)
plt.xlim(0,3000)
plt.xlabel('Frequency(Hz)')
plt.ylabel('Power')
plt.title('Power spectrum')
#plt.text(1500,200000,r'Gaussian Wavepacket'+'\n$y_0(x)=exp[-1000*(x-%s)^2]$'%x_ex+'\n$x_{observe}=%s$'%x_ob)
plt.text(1500,200000,r'Real Wavepacket'+'\n$x_{excite}=$%s'%x_ex+'\n$x_{observe}=%s$'%x_ob)

plt.show()


"""

CornerChange.py

Takes a JSON file and a randomization list and
changes one corner connection, leaving the rest
identical.

The Group Formerly Known as 'The Prince Statement'
Ben Groot, Boy Stekelbos, Momo Schaap

"""

from code.algorithms.LineTrackRandominput import TrackRandom
import random as r
import os as o
import json

def hillSort(inputFile, previousScore, randomizationList):
    """ Generates a set of turns and changes them. Takes
        a path to a json file as input file, a previous
        score, and a list of randomization choices made
        by the previous algorithm."""

    # Set variables to be measured
    totalCost = 0

    cableCost = 9

    # Load JSON file
    with open(inputFile, 'r') as JSON:
        json_dict = json.load(JSON)

    # Makes one random change in the inputfile
    randomNetwork = json_dict[r.randint(0, len(json_dict) - 1)]
    randomHouse = randomNetwork["huizen"][r.randint(0,
                            len(randomNetwork["huizen"]) - 1)]
    if randomHouse["corner"] == 0:
        randomHouse["corner"] = 1
    else:
        randomHouse["corner"] = 0

    networkID = 0
    for network in json_dict:

        houseList = []
        cables = set()

        # Sets the battery as the first cable
        sourceCable = network["locatie"].strip("()").split(",")
        sourceCable = (int(sourceCable[0]), int(sourceCable[1]))
        cables.add(sourceCable)

        # Finds all houses associated with network
        houseId = 0
        for house in network["huizen"]:
            house["kabels"] = []
            coordinatesHouse = house["locatie"].split(",")
            coordinatesHouse = (int(coordinatesHouse[0]),
                                int(coordinatesHouse[1]))
            house["distance"] = abs(coordinatesHouse[0] - sourceCable[0]) + \
                                abs(coordinatesHouse[1] - sourceCable[1])
            houseList.append(house)

        # Sorts the houses based on distance to battery
        houseList = sorted(houseList, key=lambda x: x["distance"])

        # Finds closest point on network for each house
        for house in houseList:

            cableDistance = []
            cableLocation = []
            coordinatesHouse = house["locatie"].split(",")
            coordinatesHouse = (int(coordinatesHouse[0]),
                                int(coordinatesHouse[1]))

            for cable in cables:

                # calculates distance between cable and house
                distanceCable = abs(coordinatesHouse[0] - cable[0]) + \
                                abs(coordinatesHouse[1] - cable[1])
                cableDistance.append(distanceCable)
                cableLocation.append(cable)

            # Finds nearest cable in this network
            shortestCableDistance = min(cableDistance)
            shortestCableIndex = cableDistance.index(shortestCableDistance)

            # Connect the house to the nearest cable
            for cable in TrackRandom(coordinatesHouse,
                        cableLocation[shortestCableIndex], house["corner"]):
                cables.add(cable)
                house["kabels"].append(cable)

            totalCost += shortestCableDistance * cableCost
            houseId += 1

        networkID += 1

    # Creates correct output format
    # Finds filename for results
    inputFile = inputFile.strip(".json")

    finalOutput = [{
        "locatie": network["locatie"],
        "capaciteit": network["capaciteit"],
        "huizen": [
            {
                "locatie": house["locatie"],
                "output": house["output"],
                "corner": house["corner"],
                "kabels": [
                    str(cable) for cable in house["kabels"]
                ]
            } for house in network["huizen"]
        ]
    } for network in json_dict]

    return finalOutput, totalCost, f"{inputFile}.json", randomizationList, \
                                                                    houseList


import subprocess
import matplotlib.pyplot as plt
import numpy as np

outputs_min = [[], []]
outputs_avg = [[], []]
threads = np.arange(1, 17)
points = 100
repetitions = 2
func = 2
for thread in threads:
    times_1 = []
    for _ in range(repetitions):
        result = subprocess.run(["./bin/integrate_parallel", f"func{func}.cfg", str(func), str(thread)],
                                capture_output=True, text=True).stdout.split("\n")
        times_1.append(int(result[3]))

    outputs_min[0].append(min(times_1))
    outputs_avg[0].append(sum(times_1) / repetitions)

    print(thread)

for thread in threads:
    print(str(thread), str(points))
    times_2 = []
    for _ in range(repetitions):
        result = subprocess.run(["./bin/template", f"func{func}.cfg", str(func), str(thread), str(points)],
                                capture_output=True, text=True).stdout.split("\n")
        times_2.append(int(result[3]))

    print(thread)

    outputs_min[1].append(min(times_2))
    outputs_avg[1].append(sum(times_2)/repetitions)

plt.bar(threads-0.2, outputs_min[0], 0.4, label="Minimal time - no queue")
plt.bar(threads+0.2, outputs_min[1], 0.4, label="Minimal time - queue")
plt.xlabel('Threads')
plt.ylabel('Time')
plt.legend()
plt.show()

plt.bar(threads-0.2, outputs_avg[0], 0.4, label="Average time - no queue")
plt.bar(threads+0.2, outputs_avg[1], 0.4, label="Average time - queue")
plt.xlabel('Threads')
plt.ylabel('Time')
plt.legend()
plt.show()


import copy
import os
import pickle
from collections import OrderedDict

import numpy as np
import scipy.misc as misc
from PIL import Image
from torchvision.datasets import SBDataset
from torchvision.datasets import VOCSegmentation
from tqdm import tqdm

from augmentation import SquareScale, RandomHorizontallyFlip, Compose
from dataset.transforms import ToTensor, Normalize
from utils import replace_array_ele_as_dict

# freeze seed for reproducity
np.random.seed(1234)

class VOCSBDSegmentation5(VOCSegmentation):
    """
    VOCSBDSegmentation5 Dataset. Select the 15 classes + background during training,
    and all the 21 classes in test. For more detail how to hold out the 5 classes for each fold,
    please refer to One-Shot Learning for Semantic Segmentation <https://arxiv.org/pdf/1709.03410>.
    We construct it based on VOC2012 and SBD dataset.

    Args:
        roots, root_voc, root_sbd: the root(s) path for saving dataset. Only support roots or
            (root_voc and root_sbd).
        roots: the path where both VOC and SBD are saved in.
        root_voc: the path where VOC is saved in.
        root_sbd: the path where SBD is saved in.
        fold: during training, for fold X, the classes in range(5*X + 1, 5*(X+1) + 1) are hold
            out from training. If None, return to normal settings.
        ignore_idx: integer to fill the ignore places.
        image_set: either `train` or `test` for this dataset.
        download: whether to download the dataset.
        transform, target_transform, transforms: inherit directly from VOCSegmentation.
        rebuild_mask: whether to rebuild the mask of the dataset.
    """
    ID2CLASS_NAME = {
        0: 'background',
        1: 'aeroplane',
        2: 'bicycle',
        3: 'bird',
        4: 'boat',
        5: 'bottle',
        6: 'bus',
        7: 'car',
        8: 'cat',
        9: 'chair',
        10: 'cow',
        11: 'dining table',
        12: 'dog',
        13: 'horse',
        14: 'motorbike',
        15: 'person',
        16: 'potted plant',
        17: 'sheep',
        18: 'sofa',
        19: 'train',
        20: 'tv monitor',
    }

    def __init__(self,
                 roots=None,
                 root_voc=None,
                 root_sbd=None,
                 fold=None,
                 ignore_idx=255,
                 image_set='train',
                 download=False,
                 transform=None,
                 target_transform=None,
                 transforms=None,
                 rebuild_mask=False):
        # There is no validation in this dataset.
        if image_set == 'val' or image_set == 'validation':
            image_set = 'test'
        self._check_roots(roots=roots, root_voc=root_voc, root_sdb=root_sbd)
        super().__init__(root=roots or root_voc,
                         year='2012',
                         image_set='train' if image_set == 'train' else 'val',
                         download=download,
                         transform=transform,
                         target_transform=target_transform,
                         transforms=transforms)
        self.sbd = SBDataset(root=roots or root_sbd,
                             image_set='train_noval' if image_set == 'train' else 'val',
                             mode='segmentation',
                             download=download,
                             transforms=None)
        self.roots = roots or root_voc
        self.image_set = image_set
        self.fold = fold
        self.ignore_idx = ignore_idx
        self.rebuild_mask = rebuild_mask
        self.voc_image_base_dir = os.path.split(self.images[0])[0]
        self.sbd_image_base_dir = os.path.join(self.sbd.root, 'img')
        self.images = self._combine_voc_and_sbd(self.images, self.sbd.images)
        self.masks = self._combine_voc_and_sbd(self.masks, self.sbd.masks)
        self.voc_mask_base_dir, self.sbd_mask_base_dir = self._get_both_mask_base_dirs()
        assert len(self.images) == len(self.masks), 'images len: %d, masks len: %d' % (len(self.images),
                                                                                len(self.masks))
        if self.fold is not None:
            self._fold()
            assert len(self.images) == len(self.masks), 'images len: %d, masks len: %d' % (len(self.images),
                                                                                len(self.masks))

    def __getitem__(self, index):
        self.assert_image_mask_consistent(self.images[index], self.masks[index])
        img = Image.open(self.images[index]).convert('RGB')
        if self.masks[index].endswith('.mat'):
            target = self.sbd._get_target(self.masks[index])
        else:
            target = Image.open(self.masks[index])
        if self.transforms is not None:
            img, target = self.transforms(img, target)

        return img, target

    def assert_image_mask_consistent(self, image_path, mask_path):
        image_idx = self._get_image_or_mask_id(image_path)
        mask_idx = self._get_image_or_mask_id(mask_path)
        assert image_idx == mask_idx, 'got inconsistent image_path: %s and mask_path: %s' % (image_path,
                                                                                mask_path)

    def _combine_voc_and_sbd(self, voc, sbd):
        """
        Combine the PASCAL dataset and the SBD dataset.

        Args:
            voc: images or masks of voc.
            sbd: images or masks of sbd.

        Returns:
            combined_images or combined_masks.
        """
        combined = list()
        joint_idx = set()
        for i in voc + sbd:
            idx = self._get_image_or_mask_id(i)
            if idx not in joint_idx:
                joint_idx.add(idx)
                combined.append(i)

        return combined

    @staticmethod
    def _get_image_or_mask_id(image_or_mask):
        return image_or_mask.split('/')[-1].split('.')[0]

    @staticmethod
    def _check_roots(roots, root_voc, root_sdb):
        if roots is not None and (root_voc is not None or root_sdb is not None):
            raise ValueError('when given roots, root_voc and root_sdb must be both None.')
        if roots is None and (root_voc is None or root_sdb is None):
            raise ValueError('when not given roots, root_voc and root_sdb must be both specified.')

    @staticmethod
    def squeeze(Is, Ys, Iq, Yq):
        return Is.squeeze(), Ys.squeeze(), Iq, Yq

    def _get_both_mask_base_dirs(self):
        voc_mask_base_dir = os.path.split([i for i in self.masks if i.split('.')[-1] == 'png'][0])[0]
        sbd_mask_base_dir = os.path.split([i for i in self.masks if i.split('.')[-1] == 'mat'][0])[0]
        return voc_mask_base_dir, sbd_mask_base_dir

    def _get_both_image_base_dirs(self):

        voc_image_base_dir = os.path.split([i for i in self.images if i.split('.')[-1] == 'png'][0])[0]
        sbd_image_base_dir = os.path.split([i for i in self.images if i.split('.')[-1] == 'mat'][0])[0]
        return voc_image_base_dir, sbd_image_base_dir

    def _fold(self):
        # logic is as follow
        # root path refer to the root of both masks if the root path doesn't exist or the root image
        # exists but has nothing under it, we build the dataset otherwise just get the listdir of
        # the root, and rebuild the image path.
        voc_fold_path = os.path.join(self.voc_mask_base_dir, self.image_set, str(self.fold))
        sbd_fold_path = os.path.join(self.sbd_mask_base_dir, self.image_set, str(self.fold))
        if not os.path.exists(voc_fold_path) or not os.path.exists(sbd_fold_path) or self.rebuild_mask:
            os.makedirs(voc_fold_path, exist_ok=True)
            os.makedirs(sbd_fold_path, exist_ok=True)
            fold_images = list()
            fold_masks = list()
            ignore_classes = self.get_classes_to_ignore()
            ignore_classes2ignore_idx = {ic: self.ignore_idx for ic in ignore_classes}
            if self.image_set == 'train':
                print('\nIgnoring classes for fold %d in split %s: ' % (self.fold, self.image_set), ignore_classes)
            else:
                print('\nPreserving all classes for fold %d in split %s' % (self.fold, self.image_set))
            print('Preparing data for fold %d in split %s' % (self.fold, self.image_set))
            for index in tqdm(range(len(self)), position=0, leave=True):
                original_mask_path = self.masks[index]
                if original_mask_path.endswith('.mat'):
                    fold_mask_root = sbd_fold_path
                else:
                    fold_mask_root = voc_fold_path
                fold_mask_path = os.path.join(fold_mask_root,
                                              os.path.basename(original_mask_path).split('.')[0] + '.png')
                if self.masks[index].endswith('.mat'):
                    target = self.sbd._get_target(self.masks[index])
                else:
                    target = Image.open(self.masks[index])
                target = np.array(target, dtype=np.int)
                # set the unknow to background
                target[target == 255] = 0
                # filter out the SBD boundary, set all to background
                target[target > 20] = 0
                if self.image_set == 'train':  # only filter out some classes during training
                    # old style, slow
                    # for ic in ignore_classes:
                    #     target[target == ic] = self.ignore_idx
                    # new style, maybe faster
                    target = replace_array_ele_as_dict(target, ignore_classes2ignore_idx)
                    # Images with only background and ignored will not be used
                    if target[target != self.ignore_idx].sum() == 0:
                        continue
                if not os.path.exists(fold_mask_path) or self.rebuild_mask:
                    target = misc.toimage(target, low=target.min(), high=target.max())
                    misc.imsave(fold_mask_path, target)
                fold_images.append(self.images[index])
                fold_masks.append(fold_mask_path)

            self.images = fold_images
            self.masks = fold_masks
        else:
            voc_mask_path = [os.path.join(voc_fold_path, i) for i in os.listdir(voc_fold_path)]
            sbd_mask_path = [os.path.join(sbd_fold_path, i) for i in os.listdir(sbd_fold_path)]
            self.masks = voc_mask_path + sbd_mask_path
            new_images = []
            for m_path in voc_mask_path:
                image_path = os.path.join(self.voc_image_base_dir, os.path.basename(m_path).split('.')[0] + '.jpg')
                assert os.path.exists(m_path), 'mask %s not exist' % m_path
                assert os.path.exists(image_path), 'image %s not exist' % image_path
                new_images.append(image_path)
            for m_path in sbd_mask_path:
                image_path = os.path.join(self.sbd_image_base_dir, os.path.basename(m_path).split('.')[0] + '.jpg')
                assert os.path.exists(m_path), 'mask %s not exist' % m_path
                assert os.path.exists(image_path), 'image %s not exist' % image_path
                new_images.append(image_path)
            self.images = new_images

    def get_classes_to_ignore(self):
        ignore_when_train = [i for i in range(self.fold * 5 + 1, (self.fold + 1) * 5 + 1)]
        return ignore_when_train

class VOCSBDSegmentation5i(VOCSBDSegmentation5):
    """
    VOCSBDSegmentation5i Segmentation Dataset. Comparing with VOCSBDSegmentation5, this dataset
    also return the binary mask according to the query class in a pair style.

    Args:
        train_size: due to the fact that we are randomly sample the pair to construct the final
            dataset, we can sample up to O(n^2) image to train the model, which makes it into
            1~million pairs and may not be feasible if you don't need that, so just specify the
            train_size. We sample the classes in a balanced way, the final classes may has a
            smaller number of images & masks.
        test_size: analogy to train_size, but specify for test.
        rebuild_pair: whether to rebuild query support pair.
    """

    def __init__(self,
                 train_size=15000,
                 test_size=1000,
                 rebuild_pair=False,
                 rebuild_whole_pair=False,
                 shot=1,
                 way=1,
                 *args, **kwargs):
        self.support_query = None
        self.train_size = train_size
        self.test_size = test_size
        self.threshold = 0.02
        self.rebuild_pair = rebuild_pair
        self.rebuild_whole_pair = rebuild_whole_pair
        self.shot = shot
        self.way = way
        super().__init__(*args, **kwargs)
        self.ignore_classes = self.get_classes_to_ignore()
        if self.way <= 0 or self.way > len(self.ignore_classes):
            raise ValueError("way should be in the range of [1, %d], but found %d." % (len(self.ignore_classes), way))
        if self.image_set == 'train' and self.way > 1:
            raise ValueError("n-way should only be set when testing, but now is used in training.")
        self.item_class = self.item_with_class()
        self.whole_item_class = self.whole_item_with_class()
        self.support_query = self.construct_support_query()

    def whole_item_with_class(self):
        # only used when testing
        if self.image_set == 'train':
            return None

        file_name = os.path.basename
        item_class_dir = os.path.join(self.roots, 'item_with_class', self.image_set, str(self.fold))
        if not os.path.exists(item_class_dir):
            os.makedirs(item_class_dir)
        whole_pair = 'whole_item_class.pair'
        whole_pair_path = os.path.join(item_class_dir, whole_pair)
        if not os.path.exists(whole_pair_path) or self.rebuild_whole_pair:
            item_class = dict()
            print('Caching whole item class pair to %s' % whole_pair_path)
            for index in tqdm(range(super().__len__()), position=0, leave=True):
                image_path, mask_path = self.images[index], self.masks[index]
                _, mask = super().__getitem__(index)
                mask = np.array(mask, dtype=np.int)
                uniques = np.unique(mask).tolist()
                for c in uniques:
                    # make sure we only include the novel classes
                    if c in self.ignore_classes and c != 0:
                        if c not in item_class.keys():
                            item_class.update({c: [(file_name(image_path), file_name(mask_path))]})
                        else:
                            cls_list = item_class.get(c)
                            cls_list.append((file_name(image_path), file_name(mask_path)))
            for k in item_class.keys():
                np.random.shuffle(item_class[k])
            with open(whole_pair_path, mode='wb') as f:
                pickle.dump(item_class, file=f)

        with open(whole_pair_path, mode='rb') as f:
            item_class = pickle.loads(f.read())
        return item_class

    def item_with_class(self):
        file_name = os.path.basename
        ignore_classes = self.ignore_classes

        def get_mask_fraction_has_at_least_threshold(mask, threshold, ignore_idx):
            m = mask.flatten()
            from collections import Counter
            id2cnt = Counter(m)
            total = sum(id2cnt.values())
            id2fra = {i: c / total for i, c in id2cnt.items()}
            id2fra = {i: f for i, f in id2fra.items() if f >= threshold}
            # always preserve ignore and background
            return list(id2fra.keys()) + [ignore_idx, 0]

        item_class_dir = os.path.join(self.roots, 'item_with_class', self.image_set, str(self.fold),
                                      str(self.threshold))
        if not os.path.exists(item_class_dir):
            os.makedirs(item_class_dir)
        item_class_pair = 'item_class.pair'
        if self.image_set == 'test' and self.way > 1:
            item_class_pair = 'unlimited_item_class.pair'
        item_class_path = os.path.join(item_class_dir, item_class_pair)
        if not os.path.exists(item_class_path) or self.rebuild_pair:
            item_class = list()
            print('Caching item class pair to %s' % item_class_path)
            for index in tqdm(range(super().__len__()), position=0, leave=True):
                image_path, mask_path = self.images[index], self.masks[index]
                _, mask = super().__getitem__(index)
                mask = np.array(mask, dtype=np.int)
                # uniques = np.unique(mask).tolist()
                uniques = get_mask_fraction_has_at_least_threshold(
                    mask, self.threshold, self.ignore_idx)
                if self.image_set == 'train':
                    # depart the root dir considering the portability
                    item_class.extend([(file_name(image_path), file_name(mask_path), c) for c in uniques if
                                       c != self.ignore_idx and c != 0])
                elif self.image_set == 'test' and self.way == 1:
                    # we only select the images that have base classes and
                    # only 1 novel class when testing on 1-way setting.
                    old_length = len(set(ignore_classes))
                    if len(set(ignore_classes) - set(uniques)) != old_length - 1:
                        continue
                    # depart the root dir considering the portability
                    item_class.extend([(file_name(image_path), file_name(mask_path), c) for c in uniques if
                                       c in ignore_classes])
                else:
                    # i.e., image_set == 'test' and way > 1
                    # we relieve the limitation when testing on n-way setting.
                    item_class.extend([(file_name(image_path), file_name(mask_path), c) for c in uniques if
                                       c in ignore_classes])
            np.random.shuffle(item_class)
            with open(item_class_path, mode='wb') as f:
                pickle.dump(item_class, file=f)

        with open(item_class_path, mode='rb') as f:
            item_class = pickle.loads(f.read())
        return item_class

    def construct_support_query(self):
        ignore_classes = self.ignore_classes
        unique_cls = sorted(list(set([item[-1] for item in self.item_class])))
        size = self.test_size if self.image_set == 'test' else self.train_size
        if size % len(unique_cls) == 0:
            num_per_class = size // len(unique_cls)
            num_last_class = num_per_class
        else:
            num_per_class = size // len(unique_cls)
            num_last_class = num_per_class + size - len(unique_cls) * num_per_class

        cls2items = OrderedDict()
        for cls in unique_cls:
            items = []
            for image, mask, cls_id in self.item_class:
                if cls_id == cls:
                    items.append([image, mask])
            cls2items[cls] = items

        support_query = []
        for idx, cls in enumerate(unique_cls):
            items = cls2items[cls]
            num = num_per_class if idx != len(unique_cls) - 1 else num_last_class
            # some classes may have very small number of samples
            # such as 1, then no pair can be constructed
            # for the classes that n^2 < num, there is a lot of duplication
            # we ignore it, however during test, we need to return every classes
            if len(items) ** 2 < num and self.image_set != 'test':
                continue
            cnt = 0
            num_item = len(items)
            while cnt < num:
                randoms_s = np.random.randint(0, num_item, size=self.shot)
                random_q = np.random.randint(0, num_item)
                # assure items in the pair do not duplicated
                total = len({*randoms_s, random_q})
                cls_id = cls
                if total == self.shot + 1:
                    images_s = [items[i][0] for i in randoms_s]
                    masks_s = [items[i][1] for i in randoms_s]

                    if 1 < self.way <= len(ignore_classes):
                        # deep copy the ignore classes
                        all_other_classes = [_ for _ in ignore_classes]
                        # remove the current class id
                        all_other_classes.remove(cls)
                        # sample some classes without replacement
                        other_classes = np.random.choice(all_other_classes, self.way - 1, replace=False)
                        cls_id = [cls, *other_classes]

                        # validate query image contains at most the classes in support set
                        while True:
                            # the classes that should be exclude in query image
                            exclude_classes = set(ignore_classes) - set(cls_id)
                            validate = [tuple(items[random_q]) in self.whole_item_class[i] for i in exclude_classes]
                            # assure items in the pair do not duplicated
                            total = len({*randoms_s, random_q})
                            if True not in validate and total == self.shot + 1:
                                break
                            # some conditions are not satisfied, then re-sample a query image
                            random_q = np.random.randint(0, num_item)

                        for oc in other_classes:
                            oc_items = cls2items[oc]
                            oc_num_item = len(oc_items)
                            # assure items in the support pair do not duplicated
                            while True:
                                oc_randoms_s = np.random.randint(0, oc_num_item, size=self.shot)
                                oc_total = len({*oc_randoms_s})
                                if oc_total == self.shot:
                                    break
                            images_s.extend([oc_items[i][0] for i in oc_randoms_s])
                            masks_s.extend([oc_items[i][1] for i in oc_randoms_s])
                    # when way > 1, cls_id is a list,
                    # when way ==1, cls_id is an integer for compatibility.
                    support_query.append([images_s, masks_s, *items[random_q], cls_id])
                    cnt += 1
        np.random.shuffle(support_query)

        # add base dir
        old_support_query = copy.deepcopy(support_query)
        support_query = []
        for _ in range(len(old_support_query)):
            image_s, mask_s, image_q, mask_q, cls = old_support_query[_]
            images_s, masks_s = [], []
            for img, mask in zip(image_s, mask_s):
                i, m = self._add_root_dir(img, mask)
                images_s.append(i)
                masks_s.append(m)
            image_q, mask_q = self._add_root_dir(image_q, mask_q)
            support_query.append([images_s, masks_s, image_q, mask_q, cls])
        return support_query

    def _add_root_dir(self, img_path, mask_path):
        # first find the mask root, then decide to use voc or sbd
        join = os.path.join
        voc_fold_path = join(self.voc_mask_base_dir, self.image_set, str(self.fold))
        sbd_fold_path = join(self.sbd_mask_base_dir, self.image_set, str(self.fold))
        voc_mask_path = join(voc_fold_path, mask_path)
        sbd_mask_path = join(sbd_fold_path, mask_path)
        if os.path.exists(voc_mask_path):
            mask_path = voc_mask_path
            img_path = join(self.voc_image_base_dir, img_path)
        else:
            mask_path = sbd_mask_path
            img_path = join(self.sbd_image_base_dir, img_path)

        return img_path, mask_path

    def __len__(self):
        if self.support_query is not None:
            return len(self.support_query)
        else:
            return super().__len__()

    def __getitem__(self, item):
        image_s, mask_s, image_q, mask_q, cls = self.support_query[item]
        images_s, masks_s, shot_masks_s = [], [], []
        cnt = 0
        for img, mask in zip(image_s, mask_s):
            cls_id = cls[cnt // self.shot] if isinstance(cls, list) else cls
            i = Image.open(img).convert('RGB')
            m = Image.open(mask)
            if self.transforms is not None:
                i, m = self.transforms(i, m)
            shot_m = copy.deepcopy(m)
            shot_m = np.array(shot_m)
            shot_m[shot_m != cls_id] = 0
            shot_m[shot_m == cls_id] = 1
            images_s.append(i)
            masks_s.append(m)
            shot_masks_s.append(shot_m)
            cnt += 1
        image_q = Image.open(image_q).convert('RGB')
        mask_q = Image.open(mask_q)
        if self.transforms is not None:
            image_q, mask_q = self.transforms(image_q, mask_q)
        if self.way > 1:
            query_classes = np.unique(mask_q).tolist()
            # obtain all the test classes
            test_classes = set(query_classes).intersection(set(self.ignore_classes))
            num_classes = len(test_classes)
            assert 1 <= num_classes <= self.way, \
                "test classes in query image should be in range of [1, way], but found %d." % num_classes
            shot_masks_q = [np.array(copy.deepcopy(mask_q)) for _ in range(self.way)]
            # assure the class order corresponds to the list -- cls.
            for test, shot_mq in zip(cls, shot_masks_q):
                shot_mq[shot_mq != test] = 0
                shot_mq[shot_mq == test] = 1
        else:
            # i.e., way == 1
            shot_masks_q = copy.deepcopy(mask_q)
            shot_masks_q = np.array(shot_masks_q)
            shot_masks_q[shot_masks_q != cls] = 0
            shot_masks_q[shot_masks_q == cls] = 1

        images_s = np.stack(images_s, axis=0)
        shot_masks_s = np.stack(shot_masks_s, axis=0)
        masks_s = np.stack(masks_s, axis=0)
        if self.way > 1:
            cls = np.stack(cls, axis=0)  # [way]
            shot_masks_q = np.stack(shot_masks_q, axis=0)  # [way, h, w]
        images_s = np.reshape(images_s, (self.way, self.shot, *images_s.shape[1:]))
        shot_masks_s = np.reshape(shot_masks_s, (self.way, self.shot, *shot_masks_s.shape[1:]))
        masks_s = np.reshape(masks_s, (self.way, self.shot, *masks_s.shape[1:]))
        return images_s, shot_masks_s, image_q, shot_masks_q, cls, masks_s, mask_q

class WrappedVOCSBDSegmentation5(VOCSBDSegmentation5i):
    """
    WrappedVOCSBDSegmentation5 dataset for normal segmentation task.

    Args:
        split: same as image_set, but with different name.
        img_size: expected img_size for both image and mask.
   """

    def __init__(self, root, fold, split, img_size, transforms=None, *args, **kwargs):
        if transforms is None:
            transforms = Compose([
                SquareScale(size=img_size),
                RandomHorizontallyFlip(p=0.5),
                ToTensor(),
                Normalize(mean=(0.485, 0.456, 0.406),
                          std=(0.229, 0.224, 0.225))
            ])
        super().__init__(roots=root,
                         fold=fold,
                         image_set=split,
                         transforms=transforms,
                         *args, **kwargs)
        self.support_query = None
        self.query = self.construct_query()

    def construct_query(self):
        unique_cls = sorted(list(set([item[-1] for item in self.item_class])))
        size = self.test_size if self.image_set == 'test' else self.train_size
        if size % len(unique_cls) == 0:
            num_per_class = size // len(unique_cls)
            num_last_class = num_per_class
        else:
            num_per_class = size // len(unique_cls)
            num_last_class = num_per_class + size - len(unique_cls) * num_per_class

        cls2items = OrderedDict()
        for cls in unique_cls:
            items = []
            for image, mask, cls_id in self.item_class:
                if cls_id == cls:
                    items.append([image, mask])
            cls2items[cls] = items
        done = 0
        query = []
        for idx, cls in enumerate(unique_cls):
            items = cls2items[cls]
            num = num_per_class if idx != len(unique_cls) - 1 else num_last_class
            cnt = 0
            num_item = len(items)
            while cnt < num:
                index = np.random.randint(0, num_item)
                query.append([*items[index], cls])
                cnt += 1
            done += 1
        np.random.shuffle(query)

        # add base dir
        old_query = copy.deepcopy(query)
        query = []
        for _ in range(len(old_query)):
            image, mask, _ = old_query[_]
            image, mask = self._add_root_dir(image, mask)
            query.append([image, mask])
        return query

    def __len__(self):
        if self.query is not None:
            return len(self.query)
        else:
            return super().__len__()

    def __getitem__(self, item):
        image, mask = self.query[item]
        image = Image.open(image).convert('RGB')
        mask = Image.open(mask)
        if self.transforms is not None:
            image, mask = self.transforms(image, mask)
        return image, mask

    @staticmethod
    def to_cuda(*args):
        return [a.cuda() for a in args]

class WrappedVOCSBDSegmentation5i(VOCSBDSegmentation5i):
    """
    WrappedVOCSBDSegmentation5i dataset for easy out of the box.

    Args:
        split: same as image_set, but with different name.
        img_size: expected img_size for both image and mask.
    """

    def __init__(self, root, fold, split, img_size, transforms=None, *args, **kwargs):
        if transforms is None:
            transforms = Compose([
                SquareScale(size=img_size),
                RandomHorizontallyFlip(p=0.5),
                ToTensor(),
                Normalize(mean=(0.485, 0.456, 0.406),
                          std=(0.229, 0.224, 0.225))
            ])
        super().__init__(roots=root,
                         fold=fold,
                         image_set=split,
                         transforms=transforms,
                         *args, **kwargs)

    @staticmethod
    def to_cuda(*args):
        return [a.cuda() for a in args]

if __name__ == '__main__':
    #################################################################
    # The following functions are just some basic test functions
    # to see if the codes work. But to avoid the annoying
    # Pycharm auto test triggered, we use the `run` instead of `test`.
    ##################################################################

    # specify root path for the data path
    ROOT = '/path/to/data'
    roots_path = os.path.join(ROOT, 'VOC')

    def run_VOCSBDSegmentation5i_from_scratch():
        fold = 0
        voc = VOCSBDSegmentation5i(roots=roots_path,
                                   image_set='train',
                                   fold=fold,
                                   download=True)
        print('split: train, fold: %d, len: ' % fold, len(voc))

        voc = VOCSBDSegmentation5i(roots=roots_path,
                                   image_set='test',
                                   fold=fold,
                                   download=False)  # already download above
        print('split: test, fold: %d, len: ' % fold, len(voc))

        for _ in range(1, 4):
            fold = _
            voc = VOCSBDSegmentation5i(roots=roots_path,
                                       image_set='train',
                                       fold=fold,
                                       download=False)  # already download above
            print('split: train, fold: %d, len: ' % fold, len(voc))

            voc = VOCSBDSegmentation5i(roots=roots_path,
                                       image_set='test',
                                       fold=fold,
                                       download=False)  # already download above
            print('split: test, fold: %d, len: ' % fold, len(voc))

    def run_VOCSBDSegmentation5i_wrapper():
        dataset = WrappedVOCSBDSegmentation5i(root=roots_path,
                                              fold=1,
                                              split='train',
                                              img_size=224)
        # test sbd
        idxes = [_ for _ in range(0, len(dataset))]
        import random
        random.shuffle(idxes)

        for i in idxes[:10]:
            image_s, shot_mask_s, image_q, shot_mask_q, cls, mask_s, mask_q = dataset[i]
            print(image_s.shape, shot_mask_s.shape, image_q.shape, shot_mask_q.shape,
                  cls, mask_s.shape, mask_q.shape)

    def run_WrappedVOCSBDSegmentation5i_dataloader():
        from torch.utils import data
        fold = 1
        split = 'train'
        # split = 'test'
        dataset = WrappedVOCSBDSegmentation5i(root=roots_path,
                                              fold=fold,
                                              split=split,
                                              way=1,
                                              img_size=224)

        train_loader = data.DataLoader(dataset, batch_size=4, shuffle=False, num_workers=4)

        for d in train_loader:
            image_s, shot_mask_s, image_q, shot_mask_q, cls, mask_s, mask_q = d
            print(image_s.shape, shot_mask_s.shape, image_q.shape, shot_mask_q.shape,
                  cls.shape, mask_s.shape, mask_q.shape)

    def run_WrappedVOCSBDSegmentation5_dataloader():
        from torch.utils import data
        dataset = WrappedVOCSBDSegmentation5(root=roots_path,
                                             fold=1,
                                             split='train',
                                             img_size=224)
        train_loader = data.DataLoader(dataset, batch_size=4, shuffle=False, num_workers=4)

        for d in train_loader:
            imgs, masks = d
            print(imgs.shape, masks.shape)

    def run_WrappedVOCSBDSegmentation5i_network():
        from torch.utils import data
        from model.head.pgn import PGN
        import torch.nn.functional as F
        from loss import cross_entropy2d
        from optimizer import get_optimizer

        batch_size = 4
        epoch = 1

        train_set = WrappedVOCSBDSegmentation5i(root=roots_path,
                                                fold=1,
                                                # remember to run both train and test set
                                                split='test',
                                                rebuild_mask=False,
                                                img_size=224)
        train_loader = data.DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=8)

        model = PGN()

        optim = get_optimizer()(model.parameters(), lr=0.0025, momentum=0.9,
                                dampening=0,
                                weight_decay=0, nesterov=False)

        for e in range(epoch):
            for i_iter, data in enumerate(train_loader):
                Is, Ys, Iq, Yq, sample_class, _, _ = data
                Ys, Yq = Ys.unsqueeze(1).float(), Yq.unsqueeze(1).float()

                pred = model(Is, Ys, Iq)

                pred = F.interpolate(pred, size=Yq.size()[-2:], mode='bilinear',
                                     align_corners=True)

                loss = cross_entropy2d(pred, Yq.long())
                optim.zero_grad()
                loss.backward()
                optim.step()
                print(loss.item(), sample_class)

    def run_WrappedVOCSBDSegmentation5_network():
        from torch.utils import data
        from model.head.amp import AMP
        import torch.nn.functional as F
        from loss import cross_entropy2d
        from optimizer import get_optimizer

        batch_size = 4
        epoch = 1

        train_set = WrappedVOCSBDSegmentation5(root=roots_path,
                                               fold=1,
                                               split='train',
                                               rebuild_mask=False,
                                               img_size=224)
        train_loader = data.DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=8)

        model = AMP(maximum_num_classes=21)

        optim = get_optimizer()(model.parameters(), lr=0.0025, momentum=0.9,
                                dampening=0,
                                weight_decay=0, nesterov=False)

        for e in range(epoch):
            for i_iter, data in enumerate(train_loader):
                I, Y = data
                Y = Y.unsqueeze(1).float()

                pred = model(I, phase='train')
                pred = F.interpolate(pred, size=Y.size()[-2:], mode='bilinear',
                                     align_corners=True)
                loss = cross_entropy2d(pred, Y.long())
                optim.zero_grad()
                loss.backward()
                optim.step()
                print(loss.item())

    def run_VOCSBDSegmentation5i_rebuild_pair():
        fold = 0
        image_set = 'test'
        VOCSBDSegmentation5i(roots=roots_path,
                             fold=fold,
                             image_set=image_set,
                             rebuild_pair=True,
                             rebuild_mask=True)

    def run_VOCSBDSegmentation5i_2_way_images():
        fold = 1
        dataset = WrappedVOCSBDSegmentation5i(root=roots_path,
                                              fold=fold,
                                              way=2,
                                              split='test',
                                              img_size=224)
        # test sbd
        idxes = [_ for _ in range(0, len(dataset))]
        import random
        random.shuffle(idxes)

        import matplotlib.pyplot as plt
        # n-way data shape (n>1)
        # image_s: (way, shot, 3, h, w)
        # shot_mask_s: (way, shot, h, w)
        # cls: list, length: way
        for i in [0, 200, 400, 600, 800]:
            image_s, shot_mask_s, image_q, shot_mask_q, cls, mask_s, mask_q = dataset[i]
            print(image_s.shape, shot_mask_s.shape, image_q.shape, shot_mask_q.shape,
                  cls, mask_s.shape, mask_q.shape)

            img1 = image_s[0].squeeze().transpose([1, 2, 0])
            img1 = (img1 - np.min(img1)) / (np.max(img1) - np.min(img1))
            img2 = image_s[1].squeeze().transpose([1, 2, 0])
            img2 = (img2 - np.min(img2)) / (np.max(img2) - np.min(img2))
            msk1 = shot_mask_s[0].squeeze()
            msk2 = shot_mask_s[1].squeeze()
            qry = image_q.cpu().numpy().transpose([1, 2, 0])
            qry = (qry - np.min(qry)) / (np.max(qry) - np.min(qry))
            qry_msk1 = shot_mask_q[0]
            qry_msk2 = shot_mask_q[1]

            plt.figure()
            plt.subplot(3, 2, 1)
            plt.imshow(img1)
            plt.title("support img1")
            plt.axis("off")
            plt.subplot(3, 2, 2)
            plt.imshow(msk1)
            plt.title("support msk1 -- cls: " + str(cls[0]))
            plt.axis("off")
            plt.subplot(3, 2, 3)
            plt.imshow(img2)
            plt.title("support img2")
            plt.axis("off")
            plt.subplot(3, 2, 4)
            plt.imshow(msk2)
            plt.title("support msk2 -- cls: " + str(cls[1]))
            plt.axis("off")
            plt.subplot(3, 3, 7)
            plt.imshow(qry)
            plt.title("query img")
            plt.axis("off")
            plt.subplot(3, 3, 8)
            plt.imshow(qry_msk1)
            plt.title("query msk -- cls: " + str(cls[0]))
            plt.axis("off")
            plt.subplot(3, 3, 9)
            plt.imshow(qry_msk2)
            plt.title("query msk -- cls: " + str(cls[1]))
            plt.axis("off")
            path = "./2_way_sample_fold" + str(fold) + "_[" + str(i) + "].jpg"
            plt.savefig(path)
            print("demo image save to %s." % path)

    ###########################
    # run the functions below #
    ###########################

    run_VOCSBDSegmentation5i_from_scratch()
    # run_VOCSBDSegmentation5i_wrapper()
    # run_WrappedVOCSBDSegmentation5i_dataloader()
    # run_WrappedVOCSBDSegmentation5_dataloader()
    # run_WrappedVOCSBDSegmentation5i_network()
    # run_WrappedVOCSBDSegmentation5_network()

    # run_VOCSBDSegmentation5i_rebuild_pair()

    # run_VOCSBDSegmentation5i_2_way_images()


from __future__ import absolute_import #NEEDS TO STAY AS TOP LEVEL MODULE FOR Py2-3 COMPATIBILITY
import vtk
import sys

from vmtk import pypes
import pickle
import os

try:
    import numpy as np
except ImportError:
    raise ImportError('Unable to Import vmtknumpyreader module, numpy is not installed')

class vmtkNumpyReader(pypes.pypeScript):

    def __init__(self):

        pypes.pypeScript.__init__(self)

        self.InputFileName = ''
        self.ArrayDict = None
        self.Format = ''
        self.GuessFormat = 1

        self.SetScriptName('vmtkNumpyReader')
        self.SetScriptDoc('reads a pickled (or hdf5) file containing a nested dictionary of numpy arrays generated by'
                          'vmtkcenterlinestonumpy, vmtkimagetonumpy, or vmtksurfacetonumpy from disk and stores the'
                          'object in memory into a standard dictionary of numpy arrays.')

        self.SetInputMembers([
            ['Format','f','str',1,'["pickle","hdf5"]','file format'],
            ['GuessFormat','guessformat','bool', 1,'','guess file format from extension'],
            ['InputFileName','ifile','str',1,'','the input file name']])
        self.SetOutputMembers([
            ['ArrayDict','o','dict',1,'','the output dictionary']
        ])

    def ReadPickleFile(self):
        self.PrintLog('Reading Pickle File')
        with open(self.InputFileName, 'rb') as infile:
            self.ArrayDict = pickle.load(infile)

    def ReadHDF5File(self):
        """
        Load a dictionary from an HDF5 file.
        """

        try:
            import h5py
        except ImportError:
            self.PrintError('ImportError: Unable to Write to hdf5. h5py module not installed')
            raise ImportError('Unable to Write to hdf5. h5py module not installed')

        def recursively_load_dict_contents_from_group(h5file, path):
            """
            Load contents of an HDF5 group. If further groups are encountered,
            treat them like dicts and continue to load them recursively.
            """
            ans = {}
            for key, item in h5file[path].items():
                if isinstance(item, h5py._hl.dataset.Dataset):
                    ans[key] = item.value
                elif isinstance(item, h5py._hl.group.Group):
                    ans[key] = recursively_load_dict_contents_from_group(h5file, path + key + '/')
            return ans

        with h5py.File(self.InputFileName, 'r') as h5file:
            self.ArrayDict = recursively_load_dict_contents_from_group(h5file, '/')

    def Execute(self):
        extensionFormats = {'pickle':'pickle',
                            'hdf5':'hdf5'}

        if self.InputFileName == 'BROWSER':
            import tkinter.filedialog
            import os.path
            initialDir = pypes.pypeScript.lastVisitedPath
            self.InputFileName = tkinter.filedialog.askopenfilename(title="Input file",initialdir=initialDir)
            pypes.pypeScript.lastVisitedPath = os.path.dirname(self.InputFileName)
            if not self.InputFileName:
                self.PrintError('Error: no InputFileName.')

        if self.GuessFormat and self.InputFileName and not self.Format:
            import os.path
            extension = os.path.splitext(self.InputFileName)[1]
            if extension:
                extension = extension[1:]
                if extension in list(extensionFormats.keys()):
                    self.Format = extensionFormats[extension]

        if (self.InputFileName == ''):
            self.PrintError('Error: no InputFileName.')

        self.PrintLog('Reading File')
        if self.Format == 'pickle':
            self.ReadPickleFile()
        elif self.Format == 'hdf5':
            self.ReadHDF5File()
        else:
            self.PrintError('Error: unsupported format '+ self.Format + '.')

        self.Output = self.ArrayDict

if __name__=='__main__':
    main = pypes.pypeMain()
    main.Arguments = sys.argv
    main.Execute()


import math

isup = True
def to(x, y):
    a = x#x + y
    b = y#x - y
    f = 4000 if isup else 1000
    print('G1 F{:.0f} X{:.2f} Y{:.2f}'.format(f, a, b))
def up():
    global isup
    isup = True
    print('M280 S30')
def down():
    global isup
    isup = False
    print('M280 S5')

angle = 0
pos = (0, 0)
def turn(deg):
    global angle
    angle += deg / 180 * math.pi
def forward(dist):
    global pos, angle
    x, y = pos
    pos = (x + dist * math.cos(angle), y + dist * math.sin(angle))
    x, y = pos
    to(x, y)

L = {
    'A': '− B F + A F A + F B −',
    'B': '+ A F − B F B − F A +',
}
dist = 1

def lsystem(L, str, n):
    if n <= 0:
        return
    for char in str:
        if char == 'F':
            forward(dist)
        elif char == '-':
            turn(90)
        elif char == '+':
            turn(-90)
        elif char in L:
            lsystem(L, L[char], n-1)

to(0, 0)
lsystem(L, 'A', 3)

# return to the origin
up()
to(0, 0)


import io
import itertools
import struct

from .constants import *

def bytes_to_ints(b):
    return [
        struct.unpack('I', bytes(x))[0]
        for x in itertools.zip_longest(*([iter(b)] * 4))]

def read_at(fp, offset, n):
    if fp.seek(offset) != offset:
        raise RuntimeError(f'seek({offset}) operation failed')
    b = fp.read(n)
    if len(b) < n:
        raise RuntimeError(f'read({n}) operation failed')
    return b

class SectorReader:
    def __init__(self, src, sector_size, sectors, offset_resolver, size=None):
        self.src = src
        self.sector_size = sector_size
        self.sectors = sectors
        self.offset_resolver = offset_resolver

        self.offset = 0
        self.max_offset = min(sector_size*len(sectors), size or float('inf'))

    def tell(self):
        return self.offset

    def seek(self, offset, whence=0):
        if whence == 0:
            new_offset = offset
        elif whence == 1:
            new_offset = self.offset + offset
        elif whence == 2:
            new_offset = self.max_offset + offset
        else:
            raise ValueError(f'whence value {whence} unsupported')

        if new_offset < 0:
            raise ValueError(f'invalid offset {new_offset}')

        self.offset = new_offset
        return self.offset

    def read(self, size=-1):
        if self.offset >= self.max_offset:
            return b''

        if size == -1 or size > self.max_offset - self.offset:
            size = self.max_offset - self.offset

        chunks = []
        read = 0
        while read < size:
            offset = self.offset_resolver(self.sectors[self.offset // self.sector_size])
            sector_offset = self.offset % self.sector_size

            to_read = min(size - read, self.sector_size - sector_offset)
            chunks.append(read_at(self.src, offset + sector_offset, to_read))
            read += to_read
            self.offset += to_read

        return b''.join(chunks)

def sector_chain(fat, starting_sector):
    sectors = []
    sector = starting_sector
    while sector != ENDOFCHAIN:
        sectors.append(sector)
        sector = fat[sector]
    return sectors


import os
import cv2
import numpy as np
import uuid
from dotenv import load_dotenv
import time
import threading
import datetime
import pandas as pd

from flask import Flask, Response, render_template, request, jsonify, session
from flask_session import Session
from flask_httpauth import HTTPBasicAuth

from src import *

load_dotenv()

auth = HTTPBasicAuth()
# Define your username and password here
USER_AUTH = {os.getenv("FLASK_USERNAME"): os.getenv("FLASK_PASSWORD")}

@auth.verify_password
def verify_password(username, password):
    if username in USER_AUTH and USER_AUTH[username] == password:
        return username

app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv(
    "FLASK_SECRET_KEY"
)  # Set this to a secure random value
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

fps = 2
target_height = 640
compression_quality = 60
deteciton_buffer = 10
motion_threshold = 0.5
sleep_data_path = "baby_sleep_data.csv"

user_data = {}
user_data[0] = {
    "rgb_frame": None,
    "night_frame": None,
    "is_baby_moving": False,
    "baby_in_crib": {},
}
user_data[0]["motion_detector"] = BabyMotionDetector(
    buffer_size=deteciton_buffer, motion_threshold=motion_threshold
)

# camear_hardware = "depthai-oak-d"
camear_hardware = "droidcam"

# droidcam_ip = "http://192.168.50.2:4747"
droidcam_ip = "http://192.168.50.2:8080"
# droidcam_ip = 0

if camear_hardware == "depthai-oak-d":
    camera_instance = DepthAI()
elif camear_hardware == "droidcam":
    camera_instance = DroidCam(droidcam_ip, buffer_size=1, target_height=target_height)
    # camera_instance.toggle_flash = 1

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))

# draw a square image with a red warning sign in the middle
warning_image = missing_frame_placeholder(256)

# running in a separate thread to constantly update the frame
def update_frame():
    global user_data

    while True:
        frame_rgb = camera_instance.get_frame()

        if frame_rgb is not None:
            user_data[0]["rgb_frame"] = date_and_time(frame_rgb.copy())

            # a series of image processing steps to improve the image quality
            # convert to grayscale
            frame_gray = cv2.cvtColor(frame_rgb, cv2.COLOR_BGR2GRAY)
            frame_clahe = clahe.apply(frame_gray)
            # Apply gamma correction
            frame_gamma = np.power(frame_clahe / 255.0, 1.0 / 1.5)
            frame_gray = np.uint8(frame_gamma * 255)

            is_baby_moving, frame_gray = user_data[0]["motion_detector"].is_baby_moving(
                frame_gray
            )
            user_data[0]["is_baby_moving"] = 1 if is_baby_moving else 0
            # print(f"is baby moving: {is_baby_moving}")

            # add date and time
            user_data[0]["night_frame"] = date_and_time(frame_gray)

        else:
            user_data[0]["night_frame"] = warning_image
            user_data[0]["rgb_frame"] = warning_image
            # print("No frame received from camera. Trying again...")

        time.sleep(1 / fps)  # Sleep for a duration of 1/fps

def reset_timer(time, flag_key):
    flags[flag_key] = 1
    timer = threading.Timer(time, reset_timer, args=[time, flag_key])
    timer.start()

def save_and_predict():
    global user_data
    positive_counter = 0
    moving_counter = 0
    total_counter = 0

    while True:
        frame = user_data[0]["night_frame"]
        # check if frame is a real image
        if frame is None:
            time.sleep(30)
            continue

        filename = "current_frame.jpg"
        try:
            cv2.imwrite(filename, frame)
            class_name, confidence_score = predict_image(filename)
            # print("Class:", class_name, "Confidence Score:", confidence_score)
        except:
            class_name = "not_in_crib"
            confidence_score = 0.0

        # Increase total counter
        total_counter += 1
        moving_counter += user_data[0]["is_baby_moving"]
        # If baby is in crib, increase positive counter
        if class_name == "in_crib":
            positive_counter += 1

        user_data[0]["baby_in_crib"] = {
            "class_name": class_name,
            "confidence_score": float(confidence_score),
        }

        # If total counter reaches 5 (indicating 5 minutes have passed)
        # record the result for this five-minute slot
        if total_counter == 5:
            slot_result = 1 if positive_counter >= 3 else 0
            moving_result = 1 if moving_counter >= 3 else 0

            # Prepare data to be stored - in this case, current time, prediction and confidence_score
            sleep_date, sleep_time = date_time_five_min()
            data_to_save = {
                "date": sleep_date,
                "time": sleep_time,
                "baby_in_crib": slot_result,
                "baby_moving": moving_counter,
            }

            # Convert your data into a DataFrame
            df = pd.DataFrame([data_to_save])

            # Append the data to your CSV file
            df.to_csv(sleep_data_path, mode="a", header=False, index=False)

            # Reset counters for the next slot
            positive_counter = 0
            total_counter = 0
            moving_counter = 0

        time.sleep(60)  # sleep for 1 minute

update_thread = threading.Thread(target=update_frame)
update_thread.daemon = True
update_thread.start()

predict_thread = threading.Thread(target=save_and_predict)
predict_thread.daemon = True
predict_thread.start()

# data collection
flags = {"save_negative": 1, "save_positive": 0, "save_frame": 1, "predict_crib": 1}
# this resets the flag to 1 again after 600 seconds
reset_timer(600.0, "save_negative")

def gen_frames(user_uuid):
    global user_data, save_negative, save_positive

    # Create a dictionary for the user in user_data if it doesn't exist
    # if user_uuid not in user_data:
    #     user_data[user_uuid] = {
    #         "motion_detector": BabyMotionDetector(buffer_size=deteciton_buffer, motion_threshold=motion_threshold),
    #     }

    # motion_detector = user_data[user_uuid]['motion_detector']

    while True:
        selected_camera = user_data[user_uuid]["selected_camera"]
        current_camera = user_data[user_uuid]["current_camera"]

        # print(f'current camera: {current_camera}, selected camera: {selected_camera}')
        if current_camera != selected_camera:
            camera_instance.set_current_camera(selected_camera)
            user_data[user_uuid]["is_baby_moving"] = False
            user_data[user_uuid]["current_camera"] = selected_camera
            continue

        # frame = camera_instance.get_frame()
        if current_camera == "night_cam":
            frame = user_data[0]["night_frame"]
        else:
            frame = user_data[0]["rgb_frame"]

        # Apply Non-local Means Denoising
        # frame = cv2.fastNlMeansDenoising(frame, None, h=10, templateWindowSize=7, searchWindowSize=21)

        ret, buffer = cv2.imencode(
            ".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), compression_quality]
        )

        # debug: Calculate the size of the resulting buffer (in kilobytes)
        # size = buffer.nbytes
        # size_kb = size / 1024
        # print(f"Quality: {compression_quality}, Size: {size_kb:.2f} KB")

        frame_bytes = buffer.tobytes()
        yield (
            b"--frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + frame_bytes + b"\r\n"
        )

        # data collection
        if flags["save_negative"] == 1:
            flags["save_negative"] = 0
            save_frame_to_file(frame, prefix="negative_")

        if flags["save_positive"] == 1:
            flags["save_positive"] = 0
            save_frame_to_file(frame, "positive_")

        # Sleep for a duration corresponding to 10 FPS
        time.sleep(1 / 10)

def set_user_uuid():
    global user_data

    # Generate a random code for each user session
    new_uuid = str(uuid.uuid4())
    session["user_uuid"] = new_uuid

    motion_detector = BabyMotionDetector(
        buffer_size=deteciton_buffer, motion_threshold=motion_threshold
    )

    user_data[new_uuid] = {
        "selected_camera": "day_cam",
        "current_camera": "day_cam",
        "is_baby_moving": False,
        "motion_detector": motion_detector,
    }

    return session["user_uuid"]

@app.route("/")
@auth.login_required
def index():
    return render_template("index.html")

@app.route("/video_feed")
@auth.login_required
def video_feed():
    user_uuid = session.get("user_uuid")
    # check if user_uuid in user_data
    if user_uuid not in user_data:
        user_uuid = set_user_uuid()

    return Response(
        gen_frames(user_uuid), mimetype="multipart/x-mixed-replace; boundary=frame"
    )

@app.route("/moving_status")
@auth.login_required
def moving_status():
    global user_data
    user_uuid = session.get("user_uuid")
    if user_uuid not in user_data:
        user_uuid = set_user_uuid()

    is_baby_moving = user_data[0]["is_baby_moving"]
    return jsonify(is_moving=is_baby_moving)

@app.route("/in_crib")
def prediction():
    return jsonify(user_data[0]["baby_in_crib"])

@app.route("/sleep_grid")
def drawSleepGrid():
    # get the past week's sleep data
    sleep_dict = get_sleep_data()
    # sleep_dict is {"dates": dates, "sleep_data": sleep_data}
    return jsonify(sleep_dict)

@app.route("/switch_camera", methods=["POST"])
@auth.login_required
def switch_camera():
    global user_data, flags
    command = request.form.get("command")
    if command == "toggle_flash":
        camera_instance.toggleFlash()
    elif command in ["day_cam", "night_cam"]:
        user_data[session["user_uuid"]]["selected_camera"] = command
    elif command == "save_positive_sample":
        flags["save_positive"] = 1
    elif command == "save_negative_sample":
        flags["save_negative"] = 1
    return "", 204  # Return an empty response with a 204 No Content status

@app.errorhandler(400)
def bad_request(error):
    response = jsonify({"error": "Bad Request", "message": error.description})
    response.status_code = 400
    print("print inside errorhandler")
    return response

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False)


"""recreating deleted revision

Revision ID: fdc1cbf4bbde
Revises: 0b86175e066f
Create Date: 2023-09-06 12:51:46.232440

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'fdc1cbf4bbde'
down_revision = '0b86175e066f'
branch_labels = None
depends_on = None

def upgrade():
    op.drop_column('users', 'site_visits')
    op.add_column('users', sa.Column('ip', sa.String(), nullable=True))
    op.add_column('games', sa.Column('ip', sa.String(), nullable=True))

def downgrade():
    op.add_column('users', sa.Column('site_visits', sa.Integer(), nullable=True))
    op.drop_column('users', 'ip')
    op.drop_column('games', 'ip')


from uno_env import UnoEnv

from stable_baselines3 import PPO
from stable_baselines3.common.callbacks import EvalCallback

import os
import datetime

timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
model_name = "PPO_" + timestamp

# Directory Setup
model_dir = "models/" + model_name
logdir = "logs"

if not os.path.exists(model_dir):
    os.makedirs(model_dir)
if not os.path.exists(logdir):
    os.makedirs(logdir)

# Environment Setup
env = UnoEnv()
env.reset()

eval_env = UnoEnv()
eval_env.reset()

# Model Setup

eval_callback = EvalCallback(eval_env, best_model_save_path=model_dir,
                             log_path=logdir, eval_freq=1000,
                             deterministic=True, render=False)

model = PPO('MlpPolicy', env, verbose=1, tensorboard_log=logdir)
model.learn(total_timesteps=10000000, tb_log_name=model_name)

env.close()


import pytest

from meltano.core.plugin import PluginRef, PluginType, PluginInstall
from meltano.core.plugin.setting import PluginSetting
from meltano.core.plugin.settings_service import (
    PluginSettingValueSource,
    REDACTED_VALUE,
)

def test_create(session):
    setting = PluginSetting(
        name="api_key.test.test", namespace="gitlab", value="C4F3C4F3", enabled=True
    )

    session.add(setting)
    session.commit()

    fetched = session.query(PluginSetting).first()
    assert setting == fetched

@pytest.fixture(scope="class")
def env_var(plugin_discovery_service, plugin_settings_service):
    def _wrapper(plugin: PluginRef, setting_name):
        plugin_def = plugin_discovery_service.find_plugin(plugin.type, plugin.name)
        setting_def = plugin_settings_service.find_setting(plugin, setting_name)

        return plugin_settings_service.setting_env(setting_def, plugin_def)

    return _wrapper

@pytest.fixture
def subject(session, project_add_service, tap, plugin_settings_service):
    plugin = project_add_service.add("extractors", tap.name)

    return plugin_settings_service

class TestPluginSettingsService:
    def test_get_value(
        self, session, subject, project, tap, env_var, monkeypatch, config_service
    ):
        profile = tap.add_profile("profile", label="Profile")
        config_service.update_plugin(tap)
        tap_with_profile = config_service.find_plugin(tap.name)
        tap_with_profile.use_profile(profile)

        # returns the default value when unset
        assert subject.get_value(session, tap, "test") == (
            "mock",
            PluginSettingValueSource.DEFAULT,
        )
        assert subject.get_value(session, tap_with_profile, "test") == (
            "mock",
            PluginSettingValueSource.DEFAULT,
        )

        # overriden by an PluginSetting db value when set
        setting = subject.set(session, tap, "test", "THIS_IS_FROM_DB")
        setting_with_profile = subject.set(
            session, tap_with_profile, "test", "THIS_IS_FROM_DB_WITH_PROFILE"
        )

        assert subject.get_value(session, tap, "test") == (
            "THIS_IS_FROM_DB",
            PluginSettingValueSource.DB,
        )
        assert subject.get_value(session, tap_with_profile, "test") == (
            "THIS_IS_FROM_DB_WITH_PROFILE",
            PluginSettingValueSource.DB,
        )

        # but only if enabled
        setting.enabled = False
        setting_with_profile.enabled = False
        session.merge(setting)
        session.merge(setting_with_profile)
        session.commit()

        assert subject.get_value(session, tap, "test") == (
            "mock",
            PluginSettingValueSource.DEFAULT,
        )
        assert subject.get_value(session, tap_with_profile, "test") == (
            "mock",
            PluginSettingValueSource.DEFAULT,
        )

        # overriden via the `meltano.yml` configuration
        with project.meltano_update() as meltano:
            extractor = meltano.plugins.extractors[0]
            extractor.config = {"test": 42}
            extractor.get_profile("profile").config = {"test": 43}

        assert subject.get_value(session, tap, "test") == (
            42,
            PluginSettingValueSource.MELTANO_YML,
        )
        assert subject.get_value(session, tap_with_profile, "test") == (
            43,
            PluginSettingValueSource.MELTANO_YML,
        )

        # revert back to the original
        with project.meltano_update() as meltano:
            extractor = meltano.plugins.extractors[0]
            extractor.config = {}
            extractor.get_profile("profile").config = {}

        # overriden via ENV
        subject = subject.with_env_override({env_var(tap, "test"): "N33DC0F33"})

        assert subject.get_value(session, tap, "test") == (
            "N33DC0F33",
            PluginSettingValueSource.ENV,
        )
        assert subject.get_value(session, tap_with_profile, "test") == (
            "N33DC0F33",
            PluginSettingValueSource.ENV,
        )

        # overridden via config override
        subject = subject.with_config_override({"test": "foo"})

        assert subject.get_value(session, tap, "test") == (
            "foo",
            PluginSettingValueSource.CONFIG_OVERRIDE,
        )
        assert subject.get_value(session, tap_with_profile, "test") == (
            "foo",
            PluginSettingValueSource.CONFIG_OVERRIDE,
        )

        # Verify that boolean settings set in env are cast correctly
        subject = subject.with_env_override({env_var(tap, "boolean"): "on"})

        assert subject.get_value(session, tap, "boolean") == (
            True,
            PluginSettingValueSource.ENV,
        )

        subject = subject.with_env_override({env_var(tap, "boolean"): "0"})

        assert subject.get_value(session, tap, "boolean") == (
            False,
            PluginSettingValueSource.ENV,
        )

    def test_as_config_custom(self, subject, session, config_service):
        EXPECTED = {"test": "custom", "start_date": None, "secure": None}
        tap = PluginInstall(
            PluginType.EXTRACTORS,
            name="tap-custom",
            namespace="tap_custom",
            config=EXPECTED,
        )
        config_service.add_to_file(tap)

        assert subject.as_config(session, tap) == EXPECTED

    def test_as_config(self, subject, session, tap):
        EXPECTED = {"test": "mock", "start_date": None, "secure": None}
        full_config = subject.as_config(session, tap)
        redacted_config = subject.as_config(session, tap, redacted=True)

        for k, v in EXPECTED.items():
            assert full_config.get(k) == v
            assert redacted_config.get(k) == v

    def test_as_config_redacted(self, subject, session, tap):
        # ensure values are redacted when they are set
        subject.set(session, tap, "secure", "thisisatest")
        config = subject.as_config(session, tap, redacted=True)

        assert config["secure"] == REDACTED_VALUE

        # although setting the REDACTED_VALUE does nothing
        subject.set(session, tap, "secure", REDACTED_VALUE)
        config = subject.as_config(session, tap)
        assert config["secure"] == "thisisatest"

    def test_as_env(self, subject, session, tap, env_var):
        config = subject.as_env(session, tap)

        assert config.get(env_var(tap, "test")) == "mock"
        assert config.get(env_var(tap, "start_date")) == None
        assert config.get(env_var(tap, "secure")) == None

    def test_unset(self, session, subject, tap):
        # overriden by an PluginSetting db value when set
        setting = subject.set(session, tap, "test", "THIS_IS_FROM_DB")
        assert session.query(PluginSetting).count() == 1

        subject.unset(session, tap, "test")
        assert session.query(PluginSetting).count() == 0

    def test_env_var_substitution(self, session, subject, project, tap):
        with project.meltano_update() as meltano:
            extractor = meltano.plugins.extractors[0]
            extractor.config = {
                "var": "$VAR",
                "foo": "${FOO}",
                "missing": "$MISSING",
                "multiple": "$A ${B} $C",
            }

        env = {
            "VAR": "hello world!",
            "FOO": 42,
            "A": "rock",
            "B": "paper",
            "C": "scissors",
        }

        subject = subject.with_env_override(env)

        config = subject.as_config(session, tap)
        assert config["var"] == env["VAR"]
        assert config["foo"] == str(env["FOO"])
        assert config["missing"] == None
        assert config["multiple"] == "rock paper scissors"


import imutils
import cv2
import numpy as np
from skimage import exposure
import matplotlib.pyplot as plt
import math
import scipy.spatial.distance
#import the necessary library :)

def show_image(image):
    """
    Function to plot an image
    Keyword arguments: image
    Return: None
    """
    cv2.imshow("Immagine",image)
    cv2.waitKey(0)
    return None

def find_4corners(approx_corners):
    """
    Function able to find the 4 external corners in a set of points.
    Keyword arguments: list of points
    Return: list of 4 more distant corners by each other
    """
    if approx_corners is None:
        print("\n ERROR 2: corners not found \n")
        return None
    #  find the corner points of the contours and print it
    approx_corners = sorted(np.concatenate(approx_corners).tolist())
    # find the most extreme corners, only 4 corner points
    x = [a[0] for a in approx_corners]
    y = [a[1] for a in approx_corners]
    # the top-left point has the smallest sum whereas the
    # bottom-right has the largest and compute the difference between
    # the points. the top-right will have the minumum difference and
    # the bottom-left will have the maximum difference
    diff = np.array(x) - np.array(y)
    addition = np.array(x) + np.array(y)
    tl = approx_corners[np.argmin(addition)] # top left point
    br = approx_corners[np.argmax(addition)] # bottom right point
    bl = approx_corners[np.argmin(diff)] # bottom left point
    tr = approx_corners[np.argmax(diff)] # top right point

    #print('\nAll the corner points are: ' + str([tl,bl,br,tr]))
    return [tl,bl,br,tr]

def find_corners(image):

    """
    Function to find all the corners in an image, not used in this case
    Keyword arguments: image
    Return: list of apporximated corners
    """
    if image is None:
        print("\n ERROR 1: image not found or missing \n")
        return None
    # grayscale image and blurring
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (5, 5), 0)
    # threshold the image, then perform a series of
    # dilations to remove any small regions of noise
    thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY_INV)[1] #aleatory param
    thresh = cv2.dilate(thresh, None, iterations=8)
    # find contours in thresholded image, then grab the largest one
    cnt = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnt = imutils.grab_contours(cnt)
    cnt = max(cnt, key=cv2.contourArea)
    epsilon = 0.015*cv2.arcLength(cnt, True)
    approx_corners = cv2.approxPolyDP(cnt, epsilon, True)
    return approx_corners

def find_edges(image):

    """
    Function to find the edges of an image
    Keyword arguments: image
    Return: list of approximated corners
    """

    if image is None:
        print("\n ERROR 1: image not found or missing \n")
        return None
    # grayscale image and blurring
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    #blur = cv2.bilateralFilter(gray,10,17,17) # try with bilateral filter
    gray = cv2.GaussianBlur(gray, (5, 5), 0)
    # threshold the image, then perform a series of erosions +
    # dilations to remove any small regions of noise
    thresh = cv2.Canny(image,0,50) # try with canny edge detection, not always the best solution
    thresh = cv2.dilate(thresh, None, iterations=2)
    # find contours in thresholded image, then grab the largest one
    cnt = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnt = imutils.grab_contours(cnt)
    cnt = max(cnt, key=cv2.contourArea)
    epsilon = 0.015*cv2.arcLength(cnt, True)
    approx_corners = cv2.approxPolyDP(cnt, epsilon, True)

    return approx_corners

def rectification_db(image, image_db):

    """
    Function to implement the rectification in an image prevously matched with an images db
    Keyword arguments: image, image_db
    Return: original image, rectified image
    """
    approx_corners = find_edges(image)
    approx_corners_db = find_edges(image_db)
    #take the source points and destination points
    point_src = np.float32(find_4corners(approx_corners))
    point_dst = np.float32(find_4corners(approx_corners_db))
    # homography process and warp prespective images
    H, mask = cv2.findHomography(point_src, point_dst, cv2.RANSAC, 5.0) # mask unusued
    final_width = image_db.shape[0]
    final_high = image_db.shape[1]
    rectified_image = cv2.warpPerspective(image, np.float32(H), (image_db.shape[1],image_db.shape[0]))
    rectified_image = exposure.rescale_intensity(rectified_image, out_range = (0, 255))

    return image, rectified_image

def perspective_correction(img):
    """
    Function to implement the rectification of an image. The formula was taken by a paper
    on the web. Link: https://www.microsoft.com/en-us/research/publication/whiteboard-scanning-image-enhancement/?from=http%3A%2F%2
    Fresearch.microsoft.com%2Fen-us%2Fum%2Fpeople%2Fzhang%2Fpapers%2Ftr03-39.pdf
    Keyword arguments: image
    Return: rectified image
    """
    (rows, cols, _) = img.shape
    u0 = (cols)/2.0
    v0 = (rows)/2.0

    #detected corners on the original image
    approx_corners = find_edges(img)
    tl, bl, br, tr = find_4corners(approx_corners)
    print("CORNERS:" + str([tl,bl,br,tr]))
    """
    # TESTING: drawing all the corner points in the image
    cv2.putText(img, "LT", tuple(tl), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1, cv2.LINE_AA)
    cv2.putText(img, "LD", tuple(bl), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
    cv2.putText(img, "RT", tuple(tr), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
    cv2.putText(img, "RD", tuple(br), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 1, cv2.LINE_AA)
    show_image(img)
    """
    #widths and heights of the projected image
    w1 = scipy.spatial.distance.euclidean(tl, tr)
    w2 = scipy.spatial.distance.euclidean(bl, br)
    h1 = scipy.spatial.distance.euclidean(tl, bl)
    h2 = scipy.spatial.distance.euclidean(tr, br)
    w = max(w1,w2)
    h = max(h1,h2)

    #visible aspect ratio
    ar_vis = float(w)/float(h)

    #make numpy arrays and append 1 for linear algebra
    m1 = np.array((tl[0], tl[1],1)).astype('float32')
    m2 = np.array((tr[0], tr[1],1)).astype('float32')
    m3 = np.array((bl[0], bl[1],1)).astype('float32')
    m4 = np.array((br[0], br[1],1)).astype('float32')

    #calculate the focal distance
    k2 = np.dot(np.cross(m1,m4),m3) / np.dot(np.cross(m2,m4),m3)
    k3 = np.dot(np.cross(m1,m4),m2) / np.dot(np.cross(m3,m4),m2)
    n2 = k2 * m2 - m1
    n3 = k3 * m3 - m1
    n21 = n2[0]
    n22 = n2[1]
    n23 = n2[2]
    n31 = n3[0]
    n32 = n3[1]
    n33 = n3[2]
    try:
        f = math.sqrt(np.abs((1.0/(n23*n33)) *
        ((n21*n31 - (n21*n33 + n23*n31)*u0 + n23*n33*u0*u0)
        + (n22*n32 - (n22*n33+n23*n32)*v0 + n23*n33*v0*v0))))

        A = np.array([[f,0,u0], [0,f,v0], [0,0,1]]).astype('float32')

        At = np.transpose(A)
        Ati = np.linalg.inv(At)
        Ai = np.linalg.inv(A)
        #calculate the real aspect ratio
        ar_real = math.sqrt(np.dot(np.dot(np.dot(n2,Ati),Ai),n2)
            /np.dot(np.dot(np.dot(n3,Ati),Ai),n3))
        if ar_real < ar_vis:
            W = int(w)
            H = int(float(W / ar_real))
        else:
            H = int(h)
            W = int(ar_real * H)
    except Exception:
        ar_real = ar_vis
        H = int(h)
        W = int(ar_real * H)

    pts1 = np.array([tl, tr, bl, br]).astype('float32')
    pts2 = np.float32([[0,0],[W,0],[0,H],[W,H]])
    #project the image with the new w/h
    M = cv2.getPerspectiveTransform(pts1,pts2)
    dst = cv2.warpPerspective(img, M, (W,H))
    rectified_image = dst
    rectified_image = exposure.rescale_intensity(dst, out_range = (0, 255))
    return rectified_image


#013. Largest Palindrome Product
#Sourcehttps://projecteuler.net/problem=4
'''A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.'''

palindrome = []
string = ''
for x in range(100,1000):
    for y in range(100,1000):
        a = x*y
        num = str(a)
        b = len(num)
        for c in range(-1,-b-1,-1):
            string = string + num[c]
            if string == num:
                palindrome.append(int(string))
        string= ''

print('The largest palindrome product made from two 3-digit numbers is', max(palindrome))

November 2022/013. Largest Palindrome Product.py


import re
VERB, NOUN, ADJECTIVE, ADVERB = "VB", "NN", "JJ", "RB"

VOWELS = ("a", "e", "i", "o", "u")
re_vowel = re.compile(r"a|e|i|o|u|y", re.I)
is_vowel = lambda ch: ch in VOWELS

#### PLURALIZE ######################################################################################

plural_irregular_en = set(("dag", "dak", "dal", "pad", "vat", "weg"))
plural_irregular_een = set(("fee", "genie", "idee", "orgie", "ree"))
plural_irregular_eren = set(("blad", "ei", "gelid", "gemoed", "kalf", "kind", "lied", "rad", "rund"))
plural_irregular_deren = set(("hoen", "been"))

plural_irregular = {
     "centrum": "centra",
    "escargot": "escargots",
      "gedrag": "gedragingen",
       "gelid": "gelederen",
       "kaars": "kaarsen",
       "kleed": "kleren",
         "koe": "koeien",
         "lam": "lammeren",
      "museum": "museums",
        "stad": "steden",
       "stoel": "stoelen",
         "vlo": "vlooien"
}

def pluralize(word, pos=NOUN, custom={}):
    """ Returns the plural of a given word.
        For example: stad => steden.
        The custom dictionary is for user-defined replacements.
    """
    if word in list(custom.keys()):
        return custom[word]
    w = word.lower()
    if pos == NOUN:
        if w in plural_irregular_en:    # dag => dagen
            return w + "en"
        if w in plural_irregular_een:   # fee => feeën
            return w + "ën"
        if w in plural_irregular_eren:  # blad => bladeren
            return w + "eren"
        if w in plural_irregular_deren: # been => beenderen
            return w + "deren"
        if w in plural_irregular:
            return plural_irregular[w]
        # Words ending in -icus get -ici: academicus => academici
        if w.endswith("icus"):
            return w[:-2] + "i"
        # Words ending in -s usually get -sen: les => lessen.
        if w.endswith(("es", "as", "nis", "ris", "vis")):
            return w + "sen"
        # Words ending in -s usually get -zen: huis => huizen.
        if w.endswith("s") and not w.endswith(("us", "ts", "mens")):
            return w[:-1] + "zen"
        # Words ending in -f usually get -ven: brief => brieven.
        if w.endswith("f"):
            return w[:-1] + "ven"
        # Words ending in -um get -ums: museum => museums.
        if w.endswith("um"):
            return w + "s"
        # Words ending in unstressed -ee or -ie get -ën: bacterie => bacteriën
        if w.endswith("ie"):
            return w + "s"
        if w.endswith(("ee","ie")):
            return w[:-1] + "ën"
        # Words ending in -heid get -heden: mogelijkheid => mogelijkheden
        if w.endswith("heid"):
            return w[:-4] + "heden"
        # Words ending in -e -el -em -en -er -ie get -s: broer => broers.
        if w.endswith(("é", "e", "el", "em", "en", "er", "eu", "ie", "ue", "ui", "eau", "ah")):
            return w + "s"
        # Words ending in a vowel get 's: auto => auto's.
        if w.endswith(VOWELS) or w.endswith("y") and not w.endswith("e"):
            return w + "'s"
        # Words ending in -or always get -en: motor => motoren.
        if w.endswith("or"):
            return w + "en"
        # Words ending in -ij get -en: boerderij => boerderijen.
        if w.endswith("ij"):
            return w + "en"
        # Words ending in two consonants get -en: hand => handen.
        if len(w) > 1 and not is_vowel(w[-1]) and not is_vowel(w[-2]):
            return w + "en"
        # Words ending in one consonant with a short sound: fles => flessen.
        if len(w) > 2 and not is_vowel(w[-1]) and not is_vowel(w[-3]):
            return w + w[-1] + "en"
        # Words ending in one consonant with a long sound: raam => ramen.
        if len(w) > 2 and not is_vowel(w[-1]) and w[-2] == w[-3]:
            return w[:-2] + w[-1] + "en"
        return w + "en"
    return w

#### SINGULARIZE ###################################################################################

singular_irregular = dict((v,k) for k,v in plural_irregular.items())

def singularize(word, pos=NOUN, custom={}):
    if word in list(custom.keys()):
        return custom[word]
    w = word.lower()
    if pos == NOUN and w in singular_irregular:
        return singular_irregular[w]
    if pos == NOUN and w.endswith(("ën", "en", "s", "i")):
        # auto's => auto
        if w.endswith("'s"):
            return w[:-2]
        # broers => broer
        if w.endswith("s"):
            return w[:-1]
        # academici => academicus
        if w.endswith("ici"):
            return w[:-1] + "us"
        # feeën => fee
        if w.endswith("ën") and w[:-2] in plural_irregular_een:
            return w[:-2]
        # bacteriën => bacterie
        if w.endswith("ën"):
            return w[:-2] + "e"
        # mogelijkheden => mogelijkheid
        if w.endswith("heden"):
            return w[:-5] + "heid"
        # artikelen => artikel
        if w.endswith("elen") and not w.endswith("delen"):
            return w[:-2]
        # chinezen => chinees
        if w.endswith("ezen"):
            return w[:-4] + "ees"
        # neven => neef
        if w.endswith("even") and len(w) > 4 and not is_vowel(w[-5]):
            return w[:-4] + "eef"
        if w.endswith("en"):
            w = w[:-2]
            # ogen => oog
            if w in ("og","om","ur"):
                return w[:-1] + w[-2] + w[-1]
            # hoenderen => hoen
            if w.endswith("der") and w[:-3] in plural_irregular_deren:
                return w[:-3]
            # eieren => ei
            if w.endswith("er") and w[:-2] in plural_irregular_eren:
                return w[:-2]
            # dagen => dag (not daag)
            if w in plural_irregular_en:
                return w
            # huizen => huis
            if w.endswith("z"):
                return w[:-1] + "s"
            # brieven => brief
            if w.endswith("v"):
                return w[:-1] + "f"
             # motoren => motor
            if w.endswith("or"):
                return w
            # flessen => fles
            if len(w) > 1 and not is_vowel(w[-1]) and w[-1] == w[-2]:
                return w[:-1]
            # baarden => baard
            if len(w) > 1 and not is_vowel(w[-1]) and not is_vowel(w[-2]):
                return w
            # boerderijen => boerderij
            if w.endswith("ij"):
                return w
            # idealen => ideaal
            if w.endswith(("eal", "ean", "eol", "ial", "ian", "iat", "iol")):
                return w[:-1] + w[-2] + w[-1]
            # ramen => raam
            if len(w) > 2 and not is_vowel(w[-1]) and is_vowel(w[-2]) and not is_vowel(w[-3]):
                return w[:-1] + w[-2] + w[-1]
            return w
    return w


# Basic Syntax, Conditional Statements and Loops - Lab
# Exercise 9

budget = float(input())
price_flour = float(input())

price_of_egs = price_flour * 0.75
price_of_milk = price_flour * 1.25

money_left = budget
needed_flour = 1
needed_egs = 1
needed_milk = 0.25

number_of_breads = 0
colored_egs = 0

while budget != 0:
    money_left -= price_flour + price_of_egs + (price_of_milk * needed_milk)
    if money_left > 0:
        number_of_breads += 1
        colored_egs += 3
        budget = money_left
    else:
        break
    if number_of_breads % 3 == 0:
        colored_egs -= (number_of_breads -2)

print(f'You made {number_of_breads} loaves of Easter bread! Now you have {colored_egs} eggs and {budget:.2f}BGN left.')


import time
from .displays import create_table_per_round, show_draft_picks_by_manager
from .picks import Pick

def snake_draft(number_picks, number_teams, league_name, managers_list):
    """The Entire Draft Process"""
    print(managers_list)
    _p_ = 0
    i = 0
    for _ in range(number_picks):
        print("{} is on the clock....".format(managers_list[i]))
        show_draft_picks_by_manager(league_name, managers_list[i])
        player_first_name = input("Please enter players first name: ").upper()
        player_last_name = input("Please enter players last name: ").upper()
        player_position = input("Please enter players position: ").upper()
        player_team = input("Please enter players team: ").upper()
        pick = Pick()
        pick.create_pick(player_first_name,
                         player_last_name,
                         player_position,
                         player_team)
        pick.add_pick(league_name, managers_list[i])
        _p_ += 1
        i += 1
        if _p_%int(number_teams) == 0:
            create_table_per_round(league_name, number_teams)
        time.sleep(1)


from typing import List

# LeetCode / 15. 3Sum
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        result_dict = {}
        nums_dict = {}
        nums.sort()

        for i in range(len(nums)):
            if nums[i] in nums_dict:
                nums_dict[nums[i]] += 1
            else:
                nums_dict[nums[i]] = 1

        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                tempSum = (nums[i] + nums[j]) * -1
                if tempSum in nums_dict:
                    count = 1
                    if tempSum == nums[i]:
                        count += 1
                    if tempSum == nums[j]:
                        count += 1
                    if nums_dict[tempSum] < count:
                        continue

                    temp = [nums[i], nums[j], tempSum]
                    temp.sort()
                    if not tuple(temp) in result_dict:
                        result_dict[tuple(temp)] = True
                        result.append(temp)

        return result

nums = [0,0,0]
print(Solution().threeSum(nums))


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

import pandas as pd

pd.set_option("display.max_rows", 5)
pd.set_option("display.max_columns", 6)
pd.plotting.register_matplotlib_converters()
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.backends.backend_pdf import PdfPages

pp = PdfPages("plots.pdf")
print("Setup Complete")

penguins = sns.load_dataset("penguins")
print(penguins.head())
plot = sns.histplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
plot.set_title("Fig 1: Histogram")
pp.savefig()
plot.figure.clear()

plot = sns.kdeplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
plot.set_title("Fig 2: KDE")
pp.savefig()
plot.figure.clear()

plot = sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
plot.figure.suptitle("Fig 3: Displot stacked histogram")
pp.savefig()
plot.figure.clear()

plot = sns.displot(
    data=penguins, x="flipper_length_mm", hue="species", multiple="stack", kind="kde"
)
plot.figure.suptitle("Fig 4: Displot KDE")
pp.savefig()
plot.figure.clear()

plot = sns.displot(data=penguins, x="flipper_length_mm", hue="species", col="species")
plot.figure.suptitle("")
pp.savefig()
plot.figure.clear()

f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw=dict(width_ratios=[4, 3]))
sns.scatterplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", ax=axs[0])
sns.histplot(
    data=penguins, x="species", hue="species", shrink=0.8, alpha=0.8, legend=False, ax=axs[1]
)
f.tight_layout()
pp.savefig()
f.figure.clear()

tips = sns.load_dataset("tips")
g = sns.relplot(data=tips, x="total_bill", y="tip")
g.ax.axline(xy1=(10, 2), slope=0.2, color="b", dashes=(5, 2))
pp.savefig()
g.figure.clear()

g = sns.relplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", col="sex")
g.set_axis_labels("Flipper length (mm)", "Bill length (mm)")
pp.savefig()
g.figure.clear()

sns.jointplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species")
pp.savefig()

sns.pairplot(data=penguins, hue="species")
pp.savefig()

sns.jointplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", kind="hist")
pp.savefig()

pp.close()


import os
import sys

base_path = os.path.dirname(os.path.abspath(__file__))

pyaloha_path = os.path.join(
    base_path, '..', '..'
)

sys.path.append(pyaloha_path)

PYSCRIPT_PATH = base_path

pyaloha = __import__('pyaloha.main')
pyaloha.main.cmd_run(plugin_dir=PYSCRIPT_PATH)


MM1_STEERING_MID = 1550
MM1_MAX_FORWARD = 1620  # Max is 2000
MM1_MAX_REVERSE = 1350
MM1_STOPPED_PWM = 1500
MM1_SHOW_STEERING_VALUE = False
MM1_SERIAL_PORT = '/dev/ttyS0'

THROTTLE_FORWARD_PWM = 410  # pwm value for max forward throttle
THROTTLE_STOPPED_PWM = 370  # pwm value for no movement
THROTTLE_REVERSE_PWM = 320  # pwm value for max reverse throttle

# SERVO_ESC|DC_STEER_THROTTLE|DC_TWO_WHEEL|DC_TWO_WHEEL_L298N|SERVO_HBRIDGE_PWM|PIGPIO_PWM|MM1|MOCK
DRIVE_TRAIN_TYPE = "PIGPIO_PWM"

JOYSTICK_MAX_THROTTLE = 1.0
JOYSTICK_THROTTLE_DIR = -1.0

CONTROLLER_TYPE = 'F710'  # (ps3|ps4)
DRIVE_LOOP_HZ = 20

if (CONTROLLER_TYPE == 'F710'):
    JOYSTICK_DEADZONE = 0.1

AUTO_CREATE_NEW_TUB = True
MAX_EPOCHS = 30

USE_SSD1306_128_32 = False  # Enable the SSD_1306 OLED Display

# DONKEY_GYM = True
# DONKEY_SIM_PATH = "remote"
# SIM_HOST="192.168.1.238"

CAMERA_VFLIP = True
CAMERA_HFLIP = True


from sqlalchemy import delete, select

from app.core.issue.models import Issue
from app.core.issue.vault import AbstractIssueVault
from app.service.postgres.issue.entity import IssueEntity

class IssuePostgresVault(AbstractIssueVault):
    """Implementation of abstract vault."""

    def __init__(self, db_session) -> None:
        self.db_session = db_session

    async def create(self, model: Issue):
        """Implementation of abstract method."""
        entity_to_create = IssueEntity(**model.dict())
        async with self.db_session() as session:
            session.add(entity_to_create)
            await session.commit()

    async def read(self, uid: int) -> Issue:
        """Implementation of abstract method."""
        async with self.db_session() as session:
            entity = (
                await session.select(IssueEntity).where(IssueEntity.uid == uid).first()
            )
            return Issue(entity)

    async def read_all(self, project_uid: int) -> list[Issue]:
        """Implementation of abstract method."""
        async with self.db_session() as session:
            query = select(IssueEntity).where(IssueEntity.project_id == project_uid)
            commit_list = await session.execute(query)
            return commit_list.scalars().unique().all()

    async def update(self, model: Issue):
        """Implementation of abstract method."""
        async with self.db_session() as session:
            await session.update(IssueEntity).where(
                IssueEntity.uid == model.uid
            ).values(model.dict())
            await session.commit()

    async def delete(self, uid: int):
        """Implementation of abstract method."""
        async with self.db_session() as session:
            await session.delete(IssueEntity).where(IssueEntity.uid == uid)
            await session.commit()

    async def delete_by_project(self, project_uid: int):
        """Implementation of abstract method."""
        async with self.db_session() as session:
            query = delete(IssueEntity).where(IssueEntity.project_id == project_uid)
            await session.execute(query)
            await session.commit()