import os
import cv2
import json
import time
import copy
import random
import argparse
import numpy as np

# from CHEN CVer
# 需要调整的参数
ADD_SGL_CLASS_TO_MUL = False         # 是否考虑将多类图片中没有而单类图片中有的类别的图片加入.
OTHER_CLASS_IN_IMG_ACCESSED = True   # 这里是考虑, 在选择多类图片时, 当基于一个类别的标注选出图片, 图片中另外类别标注量也要考虑进去
RAND_SELECT_IMAGE_FROM_MUL = 0.05    # 当OTHER_CLASS_IN_IMG_ACCESSED为True时才起作用
SINGLE_CATEGORY_TOP_K = 0.4          # 单类与多类都有的类别中抽取前50%中随机类别
MULTI_CATEGORY_TOP_K = 0.4           # 从多类别中标注数较少的图片中选取
MAX_PADDING_BBOX_NUMS = 3            # 允许一张多类图片里面最多填充的单类BBOX个数
MAX_SELECT_IMGS_NUMS = 5             # 每一张多类图片(原版)不能被重复选的最大次数
MAX_SELECT_SGL_IMGS_NUMS = 2         # 插入到多类图片的单类图片, 同一张单类图片被抽中的最大次数.
# 固定参数
TIME_CONSUME_UPPER = 5.0             # 超时设置
IOU_TH_LOWER = 0.1
IOU_TH_UPPER = 0.3
BORDER_PIXEL = 200
# 生成数据设置项
GENERATE_IMG_NUMS = 2000             # 生成图片张数
CURRENT_IMAGES_ID = 1                # 新生成的图片从序号1开始计数
DEBUG = False
# 打印输出与保存选项
MUL_IMG_SELECTED_NUMS = 50           # 每隔50次打印各多类图片被选中的情况, 并存储在文件里, train.json文件也是同样操作.

class PictureMixupStrategy(object):
    def __init__(self, args):
        """
        fuction: mixup the bbox that come from the single_class pictures into
                 multi_class pitures
        """
        self.args = args
        self.single_cls_images_path_root = os.path.join(self.args.single_cls_root, "images")
        self.multi_cls_images_path_root = os.path.join(self.args.multi_cls_root, "images")
        self.single_cls_anno_path = os.path.join(self.args.single_cls_root, "annotations", "train.json")
        self.multi_cls_anno_path = os.path.join(self.args.multi_cls_root, "annotations", "train.json")

        # 获取json文件
        single_cls_file = open(self.single_cls_anno_path, encoding='utf-8')
        multi_cls_file = open(self.multi_cls_anno_path, encoding='utf-8')
        self.single_file_json = json.load(single_cls_file)
        self.multi_file_json = json.load(multi_cls_file)
        # 数据处理
        self._data_preprocess()
        # 记录循环过程中, 多类图片(底片)不能被重复选中的次数
        self._mul_img_select_nums = {}
        # 记录循环过程中, 单类图片(底片)不能被重复选中的次数
        self._sgl_img_select_nums = {}

    def _data_preprocess(self):
        # 为方便计算, 将数据整理成如下形式: {"image_id": bboxes_list,...}
        # 为方便存储, 将数据整理成如下形式: {"image_id": [bbox_ann_dict,...]}

        # 单类图片数据处理
        sgl_anno_json = self.single_file_json["annotations"]
        self._single_img_id_bbox_dict = {}          # {"image_id": bboxes_list,...}
        self._sgl_img_id_bbox_ann_infos_dict = {}   # {"image_id": [bbox_ann_dict,...]}
        self._sgl_img_id_to_category_id_dict = {}   # {"img_id": category_id_list}
        self._sgl_category_id_to_num_bboxes = {}    # {"category_id": num_bboxes}
        self._sgl_category_id_to_img_id_dicts = {}  # {"category_id": image_name_list}
        for ann in sgl_anno_json:
            if ann["image_id"] not in self._single_img_id_bbox_dict.keys():
                self._single_img_id_bbox_dict[ann["image_id"]] = []
                self._single_img_id_bbox_dict[ann["image_id"]].append(ann["bbox"])
            else:
                self._single_img_id_bbox_dict[ann["image_id"]].append(ann["bbox"])

            if ann["image_id"] not in self._sgl_img_id_bbox_ann_infos_dict.keys():
                self._sgl_img_id_bbox_ann_infos_dict[ann["image_id"]] = []
                self._sgl_img_id_bbox_ann_infos_dict[ann["image_id"]].append(ann)
            else:
                self._sgl_img_id_bbox_ann_infos_dict[ann["image_id"]].append(ann)

            # 以下内容是组件如下数据结构: {"img_id": category_id_list}, {"category_id": num_bboxes}
            # 和{"category_id": image_name_list}
            if ann["image_id"] not in self._sgl_img_id_to_category_id_dict.keys():
                self._sgl_img_id_to_category_id_dict[ann["image_id"]] = []
                self._sgl_img_id_to_category_id_dict[ann["image_id"]].append(ann["category_id"])
            else:
                self._sgl_img_id_to_category_id_dict[ann["image_id"]].append(ann["category_id"])

            if str(ann["category_id"]) not in self._sgl_category_id_to_num_bboxes.keys():
                self._sgl_category_id_to_num_bboxes[str(ann["category_id"])] = 0
                self._sgl_category_id_to_num_bboxes[str(ann["category_id"])] += 1
            else:
                self._sgl_category_id_to_num_bboxes[str(ann["category_id"])] += 1

            if str(ann["category_id"]) not in self._sgl_category_id_to_img_id_dicts.keys():
                self._sgl_category_id_to_img_id_dicts[str(ann["category_id"])] = []
                self._sgl_category_id_to_img_id_dicts[str(ann["category_id"])].append(ann["image_id"])
            else:
                self._sgl_category_id_to_img_id_dicts[str(ann["category_id"])].append(ann["image_id"])

        # 多类图片数据处理
        mul_anno_json = self.multi_file_json["annotations"]
        self._multi_img_id_bbox_dict = {}          # {"image_id": bboxes_list,...}
        self._mul_img_id_bbox_ann_infos_dict = {}  # {"image_id": [bbox_ann_dict,...]}
        for ann in mul_anno_json:
            if ann["image_id"] not in self._multi_img_id_bbox_dict.keys():
                self._multi_img_id_bbox_dict[ann["image_id"]] = []
                self._multi_img_id_bbox_dict[ann["image_id"]].append(ann["bbox"])
            else:
                self._multi_img_id_bbox_dict[ann["image_id"]].append(ann["bbox"])

            if ann["image_id"] not in self._mul_img_id_bbox_ann_infos_dict.keys():
                self._mul_img_id_bbox_ann_infos_dict[ann["image_id"]] = []
                self._mul_img_id_bbox_ann_infos_dict[ann["image_id"]].append(ann)
            else:
                self._mul_img_id_bbox_ann_infos_dict[ann["image_id"]].append(ann)

        self.mul_bbox_nums = 0  # 多类别图片bbox数量
        for key, bbox_list in self._multi_img_id_bbox_dict.items():
            self.mul_bbox_nums += len(bbox_list)

        # 统计类别个数和每个类别的bbox
        self.complex_bbox = {}
        self.category_num = {}
        # 创建一个拥有类别的字典self.category_num
        # 创建一个存储类别bbox的字典 self.complex_bbox
        for ann_dict in mul_anno_json:
            self.complex_bbox[ann_dict["category_id"]] = []
            self.category_num[ann_dict["category_id"]] = 0
        # 统计每个类别的个数并存储每个类别的bbox
        for ann_dict in mul_anno_json:
            w = ann_dict["bbox"][2]
            h = ann_dict["bbox"][3]
            self.complex_bbox[ann_dict["category_id"]].append([w, h])
            self.category_num[ann_dict["category_id"]] += 1

    def _extract_imgid_to_category_dict(self):
        # 针对 self._mul_img_id_bbox_ann_infos_dict进行提取, 因为self._mul_img_id_bbox_ann_infos_dict在程序
        # 运行过程中是始终保持更新的.
        # 本函数目的是为控制抽取单类图片和多类图片服务, 具体法则如下:
        # step1: 抽取单类图片时, 优先抽取多类图片中没有而单类图片中有的类别的图片, 其次, 抽取多类图片中有且标注很少
        # (top_l), 同时单类图片中也同时存在该类的图片.
        # step2: 抽取多类图片时, 选择多类img中不存在top_k类别的图片.(超时限制, 同时加少k值.)
        # 基于上述规则: 需要组件的数据结构有如下:
        # {"img_id": category_id_list}, {"category_id": num_bboxes}
        self._mul_img_id_to_category_id_dict = {}   # {"img_id": category_id_list}
        self._mul_category_id_to_num_bboxes = {}    # {"category_id": num_bboxes}
        self._mul_category_id_to_img_id_dicts = {}  # {"category_id": image_id_list}
        for image_id, ann_infos_list in self._mul_img_id_bbox_ann_infos_dict.items():
            for idx, ann_info_dict in enumerate(ann_infos_list):
                if image_id not in self._mul_img_id_to_category_id_dict.keys():
                    self._mul_img_id_to_category_id_dict[image_id] = []
                    self._mul_img_id_to_category_id_dict[image_id].append(ann_info_dict["category_id"])
                else:
                    self._mul_img_id_to_category_id_dict[image_id].append(ann_info_dict["category_id"])

                if str(ann_info_dict["category_id"]) not in self._mul_category_id_to_num_bboxes.keys():
                    self._mul_category_id_to_num_bboxes[str(ann_info_dict["category_id"])] = 0
                    self._mul_category_id_to_num_bboxes[str(ann_info_dict["category_id"])] += 1
                else:
                    self._mul_category_id_to_num_bboxes[str(ann_info_dict["category_id"])] += 1

                if str(ann_info_dict["category_id"]) not in self._mul_category_id_to_img_id_dicts.keys():
                    self._mul_category_id_to_img_id_dicts[str(ann_info_dict["category_id"])] = []
                    self._mul_category_id_to_img_id_dicts[str(ann_info_dict["category_id"])].append(ann_info_dict["image_id"])
                else:
                    self._mul_category_id_to_img_id_dicts[str(ann_info_dict["category_id"])].append(ann_info_dict["image_id"])

    def _save_json_file(self, new_ann_dict, new_image_id):
        """
        :param new_ann_dict: {"image_id": [bbox_ann_dict,...]}
        :return:
        """
        # 更新self.multi_file_json文件, 主要是更新images和annotations.
        # images下包含每一张图片的字典:{"file_name", XXX.png, "height": 1080, "weight": 1920, "image_id": XXX, "id": XXX}
        # 更新images信息
        new_image_infos_dict = {"file_name": new_image_id + ".png",
                                "height": 1080,
                                "weight": 1920,
                                "image_id": new_image_id,
                                "id": new_image_id}
        self.multi_file_json["images"].append(new_image_infos_dict)

        # 更新annotations信息, 根据new_ann_dict[new_image_id]来进行更新, 将新增bbox信息添进来
        # self.multi_file_json["annotations] -> 对应每一个bbox的详细信息:{"area": 10, "iscrowd": 0, "image_id": "xxx",
        # "bbox": [], "categord_id": 101, "id": 1, "segmentation": []}
        # new_ann_dict[new_image_id] -> 对应每一张图片所有bbox信息:[bbox_ann_dict,...], 其中每个bbox_ann_dict包含信息:
        # {"area": 10, "iscrowd": 0, "image_id": xxx, "bbox": [], "categord_id": 101, "id": 1, "segmentation": []}
        # 由于其他函数中已经完成了针对图片new_image_id的所有bbox的信息更新, 这里只需要单独再拿出来进行操作即可.
        for idx, bbox_ann_dict in enumerate(new_ann_dict[new_image_id]):
            self.multi_file_json["annotations"].append(bbox_ann_dict)

        # 将json文件存储(实时保存)
        with open(self.multi_cls_anno_path, 'w') as f:
            json.dump(self.multi_file_json, f)

        # 保存json副本文件(每隔一段时间保存副本)
        if CURRENT_IMAGES_ID % MUL_IMG_SELECTED_NUMS == 0:
            new_json_file_path = self.multi_cls_anno_path[:-5] + "_" + str(CURRENT_IMAGES_ID) + "_.json"
            with open(new_json_file_path, 'w') as f:
                json.dump(self.multi_file_json, f)

    def _compute_area(self, sgl_bbox, mul_bbox):
        """
        :function: compute the area of (bbox_a ∩ bbox_b)
        :param sgl_bbox: 单类的bbox -> (x,y,w,h)
        :param mul_bbox: 多类的bbox -> (x,y,w,h)
        :return: area between sgl_bbox and mul_bbox
        """
        sgl_bbox = [int(x) for x in sgl_bbox]
        mul_bbox = [int(x) for x in mul_bbox]
        width0, height0 = sgl_bbox[2], sgl_bbox[3]
        width1, height1 = mul_bbox[2], mul_bbox[3]

        min_x = min(sgl_bbox[0], mul_bbox[0])
        min_y = min(sgl_bbox[1], mul_bbox[1])
        max_x = max(sgl_bbox[0] + sgl_bbox[2], mul_bbox[0] + mul_bbox[2])
        max_y = max(sgl_bbox[1] + sgl_bbox[3], mul_bbox[1] + mul_bbox[3])

        width = width0 + width1 - (max_x - min_x)
        height = height0 + height1 - (max_y - min_y)

        if width <= 0 or height <= 0:
            return 0
        else:
            return width * height

    def _compute_iou(self, sgl_bbox, mul_bbox):
        """
        :function: compute the iou between bbox_a and bbox_b
        :param sgl_bbox: sgl_bbox -> (x, y, w, h)
        :param mul_bbox: mul_bbox -> (x, y, w, h)
        :return: iou between bbox_x and bbox_y
        """
        width0, height0 = sgl_bbox[2], sgl_bbox[3]
        width1, height1 = mul_bbox[2], mul_bbox[3]

        min_x = min(sgl_bbox[0], mul_bbox[0])
        min_y = min(sgl_bbox[1], mul_bbox[1])
        max_x = max(sgl_bbox[0] + sgl_bbox[2], mul_bbox[0] + mul_bbox[2])
        max_y = max(sgl_bbox[1] + sgl_bbox[3], mul_bbox[1] + mul_bbox[3])

        width = width0 + width1 - (max_x - min_x)
        height = height0 + height1 - (max_y - min_y)

        if width <= 0 or height <= 0:
            return 0

        else:
            interArea = width * height
            boxAArea = width0 * height0
            boxBArea = width1 * height1
            iou = interArea / (boxAArea + boxBArea - interArea)
            return iou

    def _get_iou_points(self, iou_th_lower, iou_th_upper, bbox_a, mulcls_bbox_list, roi_list):
        """
        :param iou_th_lower: 设定的iou最低值
        :param iou_th_upper: 设定的iou最高值
        :param bbox_a: 来自于单类图片中的bbox:(x,y,w,h)
        :param mulcls_bbox_list: 所有多类图片中的bbox:(x,y,w,h)
        :param roi_list: 多类图片标注信息的区域  [left(x1), top(y1), right(x2), down(y2)]
        """
        # 该程序可能会出现陷入无线死循环的情况, 因为这里设置超时限制.
        iou_point = None
        time_start = time.time()
        flag = 0
        while True:
            if flag == 1:
                break
            x = random.randint(roi_list[0], roi_list[2])
            y = random.randint(roi_list[1], roi_list[3])
            new_bbox_a = [x, y, bbox_a[2], bbox_a[3]]

            for bbox in mulcls_bbox_list:
                # 计算new_bbox_a与bbox_b的iou
                iou = self._compute_iou(new_bbox_a, bbox)
                if iou_th_lower <= iou <= iou_th_upper:
                    iou_point = (x, y)
                    flag = 1
                    break

            time_end = time.time()
            if (time_end - time_start) > TIME_CONSUME_UPPER:
                print("time consuming: {0} > compute time out: {1}s, restarting!".format((time_end - time_start),
                                                                                TIME_CONSUME_UPPER))
                break

        return iou_point

    def _letterbox_image(self, image, input_dim):
        """
        :param image: 待resize的图像, 也即bbox_image
        :param input_dim: 聚类中心的尺寸
        :return: 依据短边w, 保持resize_w/image_w的进行resize.
        """
        image_w, image_h = image.shape[1], image.shape[0]
        resize_w, resize_h = [int(x) for x in input_dim]

        # 保证较短的边缩放后到正好的比例
        new_w = int(image_w * min(resize_w / image_w, resize_h / image_h))
        new_h = int(image_h * min(resize_w / image_w, resize_h / image_h))

        resized_image = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_CUBIC)
        return resized_image

    def _is_reasonable(self, sgl_bbox, mul_bbox_list):
        """
        :param sgl_bbox: 单类bbox
        :param mul_bbox_list: 多类的bbox_list
        :return: True or False
        """
        # 1.计算sgl_bbox和mul_bbox_list每一个bbox的交集是否等于bbox本身
        for idx, mul_bbox in enumerate(mul_bbox_list):
            # I.完全遮挡: (mul_bbox ∩ sgl_bbox) = mul_bbox
            if self._compute_area(sgl_bbox, mul_bbox) == int(mul_bbox[2]) * int(mul_bbox[3]):
                return True
            # II.对于mul_bbox: 被遮挡了的部分大于2/5.
            elif self._compute_area(sgl_bbox, mul_bbox) > 0.4 * int(mul_bbox[2]) * int(mul_bbox[3]):
                return True
            # III.对于sgl_bbox: 如果自身超过2/5的面积在别的mul_bbox内部.
            elif self._compute_area(sgl_bbox, mul_bbox) > 0.4 * int(sgl_bbox[2]) * int(sgl_bbox[3]):
                return True

        return False

    def _update_mul_bbox(self, sgl_bbox, old_mul_bbox):
        """
        该函数的功能是: 以sgl_bbox为基准(固定), 考虑old_mul_bbox与sgl_bbox呈现的多种相对关系.
        :param sgl_bbox: 单类bbox[x,y,w,h]
        :param old_mul_bbox: 多类bbox[x,y,w,h]
        :return: new_mul_bbox
        """
        # 将[x,y,w,h] -> [x1,y1,x2,y2]
        sgl_bbox[2] = sgl_bbox[0] + sgl_bbox[2]
        sgl_bbox[3] = sgl_bbox[1] + sgl_bbox[3]
        old_mul_bbox[2] = old_mul_bbox[0] + old_mul_bbox[2]
        old_mul_bbox[3] = old_mul_bbox[1] + old_mul_bbox[3]

        # 根据sgl_bbox与old_mul_bbox之间的相对关系, 更新old_mul_bbox数据
        if sgl_bbox[0] < old_mul_bbox[0] and old_mul_bbox[2] < sgl_bbox[2]:
            # 需要更新的情况:
            if old_mul_bbox[1] < sgl_bbox[1] < old_mul_bbox[3] < sgl_bbox[3]:
                old_mul_bbox[3] = sgl_bbox[1]
            elif old_mul_bbox[3] > sgl_bbox[3] > old_mul_bbox[1] > sgl_bbox[1]:
                old_mul_bbox[1] = sgl_bbox[3]
            # 需要删除的情况,整张图片都不会更新, old_mul_bbox被sgl_bbox截断
            elif old_mul_bbox[1] < sgl_bbox[1] and sgl_bbox[3] < old_mul_bbox[3]:
                return None
            # 不需要更新的情况
            else:
                pass
        elif sgl_bbox[1] < old_mul_bbox[1] and old_mul_bbox[3] < sgl_bbox[3]:
            # 需要更新的情况
            if old_mul_bbox[0] < sgl_bbox[0] < old_mul_bbox[2] < sgl_bbox[2]:
                old_mul_bbox[2] = sgl_bbox[0]
            elif old_mul_bbox[2] > sgl_bbox[2] > old_mul_bbox[0] > sgl_bbox[0]:
                old_mul_bbox[0] = sgl_bbox[2]
            # 需要删除的情况,整张图片都不会更新, old_mul_bbox被sgl_bbox截断
            elif old_mul_bbox[0] < sgl_bbox[0] and sgl_bbox[2] < old_mul_bbox[2]:
                return None
            # 不需要更新的情况
            else:
                pass
        # 不需要更新
        else:
            pass

        # 将[x1,y1,x2,y2]->[x,y,w,h]
        new_mul_bbox = old_mul_bbox
        new_mul_bbox[2] = old_mul_bbox[2] - old_mul_bbox[0]
        new_mul_bbox[3] = old_mul_bbox[3] - old_mul_bbox[1]
        sgl_bbox[2] = sgl_bbox[2] - sgl_bbox[0]
        sgl_bbox[3] = sgl_bbox[3] - sgl_bbox[1]

        return new_mul_bbox

    def update_sgl_bbox(self, sgl_bbox, old_mul_bbox):
        """
        该函数的功能是: 以old_mul_bbox为基准(固定住), 考虑old_mul_bbox与sgl_bbox呈现的多种相对关系,
        # 为什么考虑加入这个函数: 有一些sgl_bbox插入到多类图片中时, sgl_bbox的某一边完全在mul_bbox中.
        # 这样,是不是应该将sgl_bbox也像更新_update_mul_bbox那样, 将sgl_bbox在mul_bbox中裁掉呢?
        :param sgl_bbox: 单类bbox[x,y,w,h]
        :param old_mul_bbox: 多类bbox[x,y,w,h]
        :return: new_sgl_bbox
        """
        # 将[x,y,w,h] -> [x1,y1,x2,y2]
        sgl_bbox[2] = sgl_bbox[0] + sgl_bbox[2]
        sgl_bbox[3] = sgl_bbox[1] + sgl_bbox[3]
        old_mul_bbox[2] = old_mul_bbox[0] + old_mul_bbox[2]
        old_mul_bbox[3] = old_mul_bbox[1] + old_mul_bbox[3]

        # 考虑x方向, sgl_bbox在old_mul_bbox中间的情况:
        if old_mul_bbox[0] < sgl_bbox[0] < sgl_bbox[2] < old_mul_bbox[2]:
            # 需要更新的情况
            if sgl_bbox[1] < old_mul_bbox[1] < sgl_bbox[3] < old_mul_bbox[3]:
                old_mul_bbox[3] = sgl_bbox[1]

        # 将[x1,y1,x2,y2]->[x,y,w,h]
        new_mul_bbox = old_mul_bbox
        new_mul_bbox[2] = old_mul_bbox[2] - old_mul_bbox[0]
        new_mul_bbox[3] = old_mul_bbox[3] - old_mul_bbox[1]
        sgl_bbox[2] = sgl_bbox[2] - sgl_bbox[0]
        sgl_bbox[3] = sgl_bbox[3] - sgl_bbox[1]

        return new_mul_bbox

    def _update_bbox_ann_infos(self,
                               new_bbox,
                               bbox_img_data,
                               mul_cls_image_data,
                               sgl_bbox_ann_dict,
                               mul_cls_image_name):

        # step1: 由于有遮挡出现, 这里需要重新计算出多类bbox_ann_dict中"bbox"->values信息.
        # 根据mul_cls_image_name获取所有的bbox_dict信息.
        # 注意: 这里必须深拷贝, 否则经过self._update_mul_bbox()函数后, old_mul_bbox的值会变化, 但是这里是不能改变old_mul_bbox,
        # 因为old_mul_bbox存在与原图片中, 即将生成的new_mul_bbox值是插入进新图片的.
        mul_bbox_ann_list = copy.deepcopy(self._mul_img_id_bbox_ann_infos_dict[mul_cls_image_name[:-4]])
        # 循环计算每一个mul_bbox与new_bbox之间的遮挡情况, 并更新mul_bbox的大小
        new_mul_bbox_list = []
        for idx, mul_bbox_dict in enumerate(mul_bbox_ann_list):
            new_mul_bbox = self._update_mul_bbox(new_bbox, mul_bbox_dict["bbox"])
            # 如果new_mul_bbox is None, 中断循环, 停止更新
            if new_mul_bbox is None:
                return
            new_mul_bbox_list.append(new_mul_bbox)

        # step2: 更新sgl_bbox_dict中的数据, 方便后续直接将sgl_bbox_dict插入到多类别图片的ann信息中
        # 需要更新的信息: area, image_id, bbox, id, segmentation
        # 新生成的图片不能覆盖原图片, 因此图片文件名需要更换, 更换原则: mul_cls_image_name + "sgl_cls" + str(CURRENT_IMAGES_ID)
        global CURRENT_IMAGES_ID
        copy_sgl_bbox_ann_dict = copy.deepcopy(sgl_bbox_ann_dict)
        copy_sgl_bbox_ann_dict["area"] = new_bbox[2] * new_bbox[3]
        copy_sgl_bbox_ann_dict["bbox"] = new_bbox
        copy_sgl_bbox_ann_dict["segmentation"] = []
        # 计算当前self._multi_img_id_bbox_dict或self._mul_img_id_bbox_ann_infos_dict中bbox的总数量
        copy_sgl_bbox_ann_dict["id"] = self.mul_bbox_nums + 1  # 从多类别数据
        self.mul_bbox_nums += 1
        new_image_id = mul_cls_image_name[:-4] + "_s_" + str(CURRENT_IMAGES_ID)
        copy_sgl_bbox_ann_dict["image_id"] = new_image_id

        # step3: 如果step1没有被return回去, 则说明全部计算都没有问题, 此时我们需要将
        # self._mul_img_id_bbox_ann_infos_dict 和 self._multi_img_id_bbox_dict中
        # key为mul_cls_image_name[:-4]的信息深拷贝一份. 然后进行对key为new_image_id的内容进行更新
        self._multi_img_id_bbox_dict[new_image_id] = copy.deepcopy(
                                                     self._multi_img_id_bbox_dict[mul_cls_image_name[:-4]])
        self._mul_img_id_bbox_ann_infos_dict[new_image_id] = copy.deepcopy(
                                                     self._mul_img_id_bbox_ann_infos_dict[mul_cls_image_name[:-4]])

        for idx, mul_bbox in enumerate(new_mul_bbox_list):
            bbox_area = mul_bbox[2] * mul_bbox[3]
            self._multi_img_id_bbox_dict[new_image_id][idx] = mul_bbox
            self._mul_img_id_bbox_ann_infos_dict[new_image_id][idx]["image_id"] = new_image_id
            self._mul_img_id_bbox_ann_infos_dict[new_image_id][idx]["bbox"] = mul_bbox
            self._mul_img_id_bbox_ann_infos_dict[new_image_id][idx]["area"] = bbox_area
            self._mul_img_id_bbox_ann_infos_dict[new_image_id][idx]["id"] = self.mul_bbox_nums + 1
            self.mul_bbox_nums += 1

        # 将单类bbox的信息添加进self._mul_img_id_bbox_ann_infos_dict 和 self._multi_img_id_bbox_dict
        self._multi_img_id_bbox_dict[new_image_id].append(new_bbox)
        self._mul_img_id_bbox_ann_infos_dict[new_image_id].append(copy_sgl_bbox_ann_dict)

        # 不要忘记将mixup成功的图片进行存储.
        print("当前正在生成第-{0}-张图片, 总计需要生成-{1}-张图片, 完成百分比: {2:.2%}".format(CURRENT_IMAGES_ID, GENERATE_IMG_NUMS,
                                                                                CURRENT_IMAGES_ID / GENERATE_IMG_NUMS))
        CURRENT_IMAGES_ID += 1
        # 对图像数据和json数据同时进行存储
        if not DEBUG:
            multi_cls_img_path = os.path.join(self.multi_cls_images_path_root, new_image_id + ".png")
            cv2.imwrite(multi_cls_img_path, mul_cls_image_data)
            self._save_json_file(self._mul_img_id_bbox_ann_infos_dict, new_image_id)
            print("--------------------------save successfully----------------------------")
            # 对mul_cls_image_name进行分解，获取原始多类图片的图片名
            orig_mul_img_name = mul_cls_image_name[:-4].split("_")[0]
            if orig_mul_img_name not in self._mul_img_select_nums.keys():
                self._mul_img_select_nums[orig_mul_img_name] = 0
                self._mul_img_select_nums[orig_mul_img_name] += 1
            else:
                self._mul_img_select_nums[orig_mul_img_name] += 1

            # 每生成50张，打印下采样的图片和被采样的次数:
            if CURRENT_IMAGES_ID % MUL_IMG_SELECTED_NUMS == 0:
                # 打印输出报告情况并保存:
                for img_id, selected_nums in self._mul_img_select_nums.items():
                    print("img_id: {0}, selected times: {1}.".format(img_id, selected_nums))

        # 显示用, 可以关闭.
        if DEBUG:
            draw_mul_bbox_list = self._multi_img_id_bbox_dict[new_image_id]
            for idx, bbox1 in enumerate(draw_mul_bbox_list):
                cv2.rectangle(mul_cls_image_data, (int(bbox1[0]), int(bbox1[1])),
                              (int(bbox1[0] + bbox1[2]), int(bbox1[1] + bbox1[3])), (0, 255, 0), 2)
            cv2.namedWindow("mixup_img_win", 0)
            cv2.imshow("mixup_img_win", mul_cls_image_data)
            cv2.waitKey(0)

    def _extract_sgl_img_list(self):
        self._extract_imgid_to_category_dict()
        sgl_category_set = self._sgl_category_id_to_num_bboxes.keys()
        mul_category_set = self._mul_category_id_to_num_bboxes.keys()
        # 单类图片中有, 多类图片中没有的类别集合.
        yes_sgl_class_no_mul_class_set = sgl_category_set - mul_category_set
        # 第一步: 约定优先抽取单类图片中有, 而多类图片中没有的类别的图片:
        if yes_sgl_class_no_mul_class_set and ADD_SGL_CLASS_TO_MUL:
            random_sgl_category = random.choice(list(yes_sgl_class_no_mul_class_set))
            # 第二步：获取到所有单类中含有random_sgl_category的图片的集合
            sgl_img_list = self._sgl_category_id_to_img_id_dicts[random_sgl_category]
        # 其次抽取多类图片中有, 单类图片中也有的类别, 但是这些类别在多类中占比很少的情况
        else:
            yes_sgl_class_yes_mul_class_set = sgl_category_set & mul_category_set
            # 查询yes_sgl_class_yes_mul_class_set中类在多类中的各自的占比情况
            category_ratio_by_mul = []
            for category_id in yes_sgl_class_yes_mul_class_set:
                category_ratio_by_mul.append((category_id, self._mul_category_id_to_num_bboxes[category_id]))
            # 列表按照self._mul_category_id_to_num_bboxes[category_id]排序
            category_ratio_by_mul_sorted = sorted(category_ratio_by_mul, key=lambda x: x[1])
            # 获取标注top_k的类的对应的单类的图片集合
            top_k_category_list = category_ratio_by_mul_sorted[:int(len(category_ratio_by_mul_sorted) * SINGLE_CATEGORY_TOP_K)]
            random_sgl_category = random.choice(top_k_category_list)[0]  # 随机抽取一个类
            sgl_img_list = self._sgl_category_id_to_img_id_dicts[random_sgl_category]

        return sgl_img_list, random_sgl_category

    def _mixup_picture(self, bbox, bbox_img, image_name, mlclsimage,
                       iou_th_lower, iou_th_upper, border_pixel):
        """
        :param bbox: 单类图片的标注bbox ->(x,y,w,h)
        :param bbox_img: bbox中对应的image区域
        :param image_name: 多类图片的图片名
        :param mlclsimage: 多类图片数据
        :param iou_th_lower: iou最低值
        :param iou_th_upper: iou最高值
        :return: mixup_image
        """
        orig_multi_class_img = mlclsimage.copy()
        # step1: 查找出mlclsimage中bboxes的个数
        bboxes_list = self._multi_img_id_bbox_dict[image_name]
        # 插入bbox显示
        if DEBUG:
            for idx, bbox1 in enumerate(bboxes_list):
                cv2.rectangle(orig_multi_class_img, (int(bbox1[0]), int(bbox1[1])),
                              (int(bbox1[0] + bbox1[2]), int(bbox1[1] + bbox1[3])), (0, 0, 255), 2)

        # step2: 计算多类图片的bbox区域范围
        bboxes = np.array(bboxes_list)

        box_x1 = bboxes[:, 0]
        box_y1 = bboxes[:, 1]
        box_x2 = bboxes[:, 2] + bboxes[:, 0]
        box_y2 = bboxes[:, 3] + bboxes[:, 1]

        min_x1 = np.min(box_x1)
        min_y1 = np.min(box_y1)
        min_x2 = np.max(box_x2)
        min_y2 = np.max(box_y2)

        left = max(int(min_x1) - border_pixel, 0)
        top = max(int(min_y1) - border_pixel, 0)
        right = min(int(min_x2) + border_pixel, mlclsimage.shape[1])
        down = min(int(min_y2) + border_pixel, mlclsimage.shape[0])

        ROI = [left, top, right, down]  # [x1, y1, x2, y2]

        # step3: 通过单类的bbox和ROI计算可行的点
        iou_point = self._get_iou_points(iou_th_lower, iou_th_upper, bbox, bboxes_list, ROI)
        if iou_point is None:
            return None
        # 然后将单类图片的bbox的x和y坐标换成iou_point
        new_bbox = [iou_point[0], iou_point[1], bbox[2], bbox[3]]

        # step4: 判断图片的合理性
        # 1.越界检查
        if int(iou_point[1] + bbox[3]) > mlclsimage.shape[0]:
            new_bbox[3] = mlclsimage.shape[0] - iou_point[1]
        if int(iou_point[0] + bbox[2]) > mlclsimage.shape[1]:
            new_bbox[2] = mlclsimage.shape[1] - iou_point[0]

        # 填充sgl_bbox丢失一半以上(靠近边界).(融合失败)
        if (new_bbox[2] < 0.5 * bbox[2]) or (new_bbox[3] < 0.5 * bbox[3]):
            # print("The generated picture is not available...")
            return None

        # 2.遮挡检查(融合失败)
        elif self._is_reasonable(new_bbox, bboxes_list):
            # print("Image fusion fails due to mutual occlusion...")
            return None
        # 3.输出结果(正确融合)
        # step5: 在mlclsimage的new_bbox区域, 将bbox_img部分mixup进去, mlclsimage[y1:y2, x1:x2] = bbox_img
        else:
            bbox_img = bbox_img[:int(new_bbox[3]), :int(new_bbox[2])]
            mlclsimage[int(iou_point[1]): int(iou_point[1] + new_bbox[3]),
                       int(iou_point[0]): int(iou_point[0] + new_bbox[2])] = bbox_img

            if DEBUG:
                cv2.namedWindow("orig_multiclass_img_win", 0)
                cv2.imshow("orig_multiclass_img_win", orig_multi_class_img)

            return new_bbox, bbox_img, mlclsimage

    def main_process(self):
        # step1: 随机获取一张单类图片的bbox和bbox_img
        ##############################################################
        # 本函数目的是为控制抽取单类图片和多类图片服务, 具体法则如下:
        # step1: 抽取单类图片时, 优先抽取多类图片中没有而单类图片中有的类别的图片, 其次, 抽取多类图片中有且标注很少
        # (top_l), 同时单类图片中也同时存在该类的图片.
        # step2: 抽取多类图片时, 选择多类img中不存在top_k类别的图片.(超时限制, 同时加少k值.)
        # 抽取单类图片符合条件的列表
        sgl_img_list, random_sgl_category = self._extract_sgl_img_list()
        # 获取到单类图片的集合后, 从中任意抽取一张图片
        sgl_img_name = random.choice(sgl_img_list)
        # 这里需要查询sgl_img_name被抽中了几次.
        if sgl_img_name not in self._sgl_img_select_nums.keys():
            self._sgl_img_select_nums[sgl_img_name] = 0
            self._sgl_img_select_nums[sgl_img_name] += 1
        else:
            sgl_img_selected_nums = self._sgl_img_select_nums[sgl_img_name]
            if sgl_img_selected_nums >= MAX_SELECT_SGL_IMGS_NUMS:
                return None
        # 从self._single_img_id_bbox_dict拿到bbox信息
        single_cls_bbox = self._single_img_id_bbox_dict[sgl_img_name][0]
        # 从single_cls_bbox对应的完整信息, 根据sgl_img_name来获得
        sgl_bbox_ann_dict = self._sgl_img_id_bbox_ann_infos_dict[sgl_img_name][0]
        ##############################################################
        single_cls_image_path = os.path.join(self.single_cls_images_path_root, (str(sgl_img_name) + ".png"))
        single_cls_image = cv2.imread(single_cls_image_path)
        bbox_image = single_cls_image[int(single_cls_bbox[1]):int(single_cls_bbox[1] + single_cls_bbox[3]),
                                      int(single_cls_bbox[0]):int(single_cls_bbox[0] + single_cls_bbox[2])]

        # step2: 这里对bbox_image做一定程度上的处理, 处理过程如下：
        # 1.对bbox_image做随机hflip或vflip处理, bbox不需要处理
        # 2.计算bbox与聚类中心族centeriod中哪一个的iou值最大.
        # 3.随机给定一个variance. bbox变换目标为: max_iou_centeriod + variance
        # 4.依据letterbox(只保证短边aspect_ratio)的resize法则, 对bbox进行变换.
        random_index = 1 if random.random() > 0.5 else 0
        bbox_flip_image = cv2.flip(bbox_image, random_index)
        # 获取单类图片的类别
        single_cls_category = int(random_sgl_category)
        # 如果single_cls_category可能来自于以下两种情况:
        # ①单类图片类别不在多类图片中; ②单类图片类别在多类图片中
        # 如果单类图片不在多类图片中, 暂时先不考虑①情况.
        if single_cls_category not in self.complex_bbox.keys():
            return None
        new_center_bbox_wh = random.choice(self.complex_bbox[single_cls_category])
        # step3: 用letterbox进行变换
        letterbox_single_image = self._letterbox_image(bbox_flip_image, new_center_bbox_wh)
        # 同时更新single_cls_bbox
        new_single_cls_bbox = [single_cls_bbox[0], single_cls_bbox[1],
                               letterbox_single_image.shape[1], letterbox_single_image.shape[0]]
        # step4: 随机从多类中抽取一张图片
        # 获取文件夹图片列表, 从多类别中抽取图片时,要排除掉那些本身类别占比非常高的情况.
        sorted_mul_category_id_to_num_bboxes_list = list(sorted(self._mul_category_id_to_num_bboxes.items(), key=lambda x: x[1]))
        top_k_mul_category_list = sorted_mul_category_id_to_num_bboxes_list[: int(len(sorted_mul_category_id_to_num_bboxes_list) * MULTI_CATEGORY_TOP_K)]
        random_sgl_category = random.choice(top_k_mul_category_list)[0]  # 随机选择一个类别
        mul_img_list = self._mul_category_id_to_img_id_dicts[random_sgl_category]
        multi_cls_img_name = random.choice(mul_img_list)  # 随机选择一张图片.
        # 这里需要控制一下如果拿出来的这张图片已经被重复选取了很多次，那就不要这张图片了, 重新选取, 这里主要是看
        # 对multi_cls_img_name用"_"分割, 然后进行查询含有s的个数.
        multi_cls_img_name_split_list = multi_cls_img_name.split("_")
        padding_bbox_nums = multi_cls_img_name_split_list.count("s")
        if padding_bbox_nums >= MAX_PADDING_BBOX_NUMS:
            return None
        # 查询multi_cls_img_name的最初始多类图片被选中了几次
        if multi_cls_img_name_split_list[0] in self._mul_img_select_nums.keys():
            mul_img_selected_nums = self._mul_img_select_nums[multi_cls_img_name_split_list[0]]
            if mul_img_selected_nums >= MAX_SELECT_IMGS_NUMS:
                return None
        #  样本多样性控制
        # 这里还需要进行控制, 选择出来的图片中包含的其他的类别的bbox量不能太高, 但是为了增加样本多样性, 我们应该还需要从那些其他图片中
        # 随机抽取图片(这个作为选配).
        # 这里设置一个概率阈值, 也即只有当小于该阈值情况下才考虑率从图片数据集中任意拿.
        if OTHER_CLASS_IN_IMG_ACCESSED:
            random_select_p = random.random()
            if random_select_p > RAND_SELECT_IMAGE_FROM_MUL:
                # 查询图片内部其他的bbox对应的类别
                multi_cls_img_category_list = self._mul_img_id_to_category_id_dict[multi_cls_img_name]
                # 检查该图片其他的类别的标注量, 获取阈值: num_bbox_thr, 保证不取到标注量在TOP20的类别
                num_bbox_thr = \
                    sorted_mul_category_id_to_num_bboxes_list[int(0.8 * len(sorted_mul_category_id_to_num_bboxes_list))][1]
                # 对图片中其他所有标注bbox数量进行检查, 只要有一个不合适就return None回去
                for idx, img_category_id in enumerate(multi_cls_img_category_list):
                    num_bbox_counts = self._mul_category_id_to_num_bboxes[str(img_category_id)]
                    # 默认该类别的bbox数目若在大于最高值的0.8倍, 则整张图片丢弃
                    if num_bbox_counts > num_bbox_thr:
                        return None

        multi_cls_img_path = os.path.join(self.multi_cls_images_path_root, multi_cls_img_name + ".png")
        mlclsimage = cv2.imread(multi_cls_img_path)

        # step5: 图像混合: new_data = (new_bbox, bbox_img, mlclsimage)
        new_data = self._mixup_picture(new_single_cls_bbox,
                                       letterbox_single_image,
                                       multi_cls_img_name,
                                       mlclsimage, IOU_TH_LOWER,
                                       IOU_TH_UPPER, BORDER_PIXEL)

        # step6: 整理多类图片的标注, 以图片为单位, 将ann中的bbox重新整理. 这里必须注意一个问题, 更新必须要对
        # self._multi_img_id_bbox_dict = {}和self._mul_img_id_bbox_ann_infos_dict = {}进行原地更新.
        if new_data is not None:
            self._update_bbox_ann_infos(*new_data, sgl_bbox_ann_dict, multi_cls_img_name + ".png")

        return True

def main():
    parser = argparse.ArgumentParser()
    # 单类图片文件夹root
    parser.add_argument('-single_cls_root', default="/home/pi/Desktop/work/data/competition_data/single_class",
                        help='the folder root path von single cls root')
    # 多类图片文件夹root
    parser.add_argument('-multi_cls_root', default="/home/pi/Desktop/work/data/competition_data/multi_class",
                        help='the folder root path von multi cls root')

    args = parser.parse_args()
    picturemixup = PictureMixupStrategy(args)

    while True:
        picturemixup.main_process()
        if CURRENT_IMAGES_ID > GENERATE_IMG_NUMS:
            print("恭喜您！数据生成完成。")
            break

if __name__ == '__main__':
    main()


def solution_part1(x: str):
    s = 0
    with open(x) as f:
        while True:
            # line by line, don't read whole thing into memory
            line = f.readline().strip()
            if not line:
                break
            left = 0
            right = len(line) - 1
            first = None
            second = None
            while not first or not second:
                if not first and line[left].isdigit():
                    first = line[left]
                if not second and line[right].isdigit():
                    second = line[right]
                if not first:
                    left += 1
                if not second:
                    right -= 1
                # in case there are no digits in the string
                if left > right:
                    first = "0"
                    second = "0"
            s += int(f"{first}{second}")
    return s

def solution_part2(x: str):
    s = 0
    words = {
        "one": 1,
        "two": 2,
        "three": 3,
        "four": 4,
        "five": 5,
        "six": 6,
        "seven": 7,
        "eight": 8,
        "nine": 9
    }
    with open(x) as f:
        s = 0
        while True:
            line = f.readline().strip()
            if not line:
                break
            nums = []
            for i in range(len(line)):
                if line[i].isdigit():
                    nums.append(int(line[i]))
                else:
                    if i - 4 >= 0 and line[i-4:i+1] in words:
                        nums.append(words[line[i-4:i+1]])
                    if i - 3 >= 0 and line[i-3:i+1] in words:
                        nums.append(words[line[i-3:i+1]])
                    if i - 2 >= 0 and line[i-2:i+1] in words:
                        nums.append(words[line[i-2:i+1]])
            s += (nums[0]*10 + nums[-1])
    return s

if __name__ == "__main__":
    res1 = solution_part1("input.txt")
    res2 = solution_part2("input.txt")
    print(res1, res2)


from settings import *
from login import *
from Game import *

class Main:
    def __init__(self):
        self.srcNo = 1
        self.win = display.set_mode((720,720))
        self.login = Login()
        self.game = Game()
        self.quit = False

    def run(self):
        while self.quit == False:
            events= event.get()
            for i in events:
                if i.type == QUIT:self.quit = True

            if self.srcNo == 1:
                if self.login.run(events):
                    self.srcNo = 2
                    self.win = display.set_mode((1080,720))

            if self.srcNo == 2:
                self.game.run(self.login.userTextBox.getText())

            display.flip()
            CLOCK.tick(60)
        quit()

main = Main()
main.run()


result = []

for i in range(15):
    result.append([])

for i in range(5):
    letter = input()
    for idx in range(len(letter)):
        result[idx].append(letter[idx])

for lst in result:
    for char in lst:
        print(char, end='')


import re

names = input().split(",")
for i in range(len(names)):
    names[i] = names[i].strip()

dict_with_names = {}
letters_pattern = r"[a-zA-Z]"
digits_pattern = r"\d"

while True:
    info = input()
    if info == "end of race":
        break
    name = "".join(re.findall(letters_pattern, info))
    distance = "".join(re.findall(digits_pattern, info))
    if name in names:
        dict_with_names[name] = dict_with_names.get(name, 0) + (sum(int(n) for n in distance))

positions = {1: '1st', 2: '2nd', 3: '3rd'}
sorted_dict = sorted(dict_with_names.items(), key=lambda x: -x[1])

for num, (person_name, km) in enumerate(sorted_dict[:3], 1):
    print(f"{positions[num]} place: {person_name}")

2. Python Fundamentals/9. Regular Expressions - More Exercises/01_race.py


import os
import numpy as np

def read_ram_estimates(folder: str):
    """
    Read estimated RAM consumption from Memory.txt.

    Note that the RAM consumption per frequency step can be estimated and
    written to `Memory.txt` by calling ``NumCalc -estimate_ram``. This must
    be done before calling this function.

    Parameters
    ----------
    folder : str
        full path to the source folder containing the `Memory.txt` file from
        which the estimates are read

    Returns
    -------
    estimates : numpy array
        An array of shape ``(N, 3)`` where ``N`` is the number of frequency
        steps. The first column contains the frequency step, the second the
        frequency in Hz, and the third the estimated RAM consumption in GB.
    """

    # check if file exists
    if not os.path.isfile(os.path.join(folder, "Memory.txt")):
        raise ValueError(f"{folder} does not contain a Memory.txt file")

    # read content of file
    with open(os.path.join(folder, "Memory.txt"), "r") as ff:
        content = ff.readlines()

    # parse data to nested list
    estimates = []
    for line in content:
        estimate = []
        for ee in line.strip().split(" "):
            estimate.append(float(ee))

        estimates.append(estimate)

    return np.asarray(estimates)


from typing import List

def solution(phone_book: List[str]):
    phone_book = sorted(phone_book)

    for i in range(1, len(phone_book)):
        a = phone_book[i - 1]
        b = phone_book[i]
        if len(a) < len(b):
            if b.startswith(a):
                return False
    return True

if __name__ == "__main__":
    assert solution(["119", "97674223", "1195524421"]) == False  # noqa: E712
    assert solution(["123", "456", "789"]) == True  # noqa: E712
    assert solution(["12", "123", "1235", "567", "88"]) == False  # noqa: E712
    print("SUCCESS")


import os

myList = []

def add():
  os.system("clear")
  print('\033[33mINVENTORY')
  print('=========')
  item = input("\033[33mItem to add > \033[32m")
  myList.append(item)
  print("Added")
  input("Press Enter to continue...")

def view():
  os.system("clear")
  print('\033[33mINVENTORY')
  print('=========')
  print("You have the following items:")
  for item in myList:
    print(f"{item}")
  input("Press Enter to continue...")

def count():
  os.system("clear")
  print('\033[33mINVENTORY')
  print('=========')
  amount = myList.count(input("What item do you want to know the amount? \033[32m"))
  print(f"\033[33mYou have {amount} of this item.")
  input("Press Enter to continue...")

def remove():
  os.system("clear")
  print('\033[33mINVENTORY')
  print('=========')
  item = input("\033[33mItem to remove > \033[32m")
  myList.remove(item)
  print("Removed")
  input("Press Enter to continue...")

while True:
  try:
    f = open("inventory.txt", 'r')
    myList = eval(f.read())
    f.close()
  except Exception as e:
    print(f"{e}. But don't worry, a new file will be created.")

  os.system("clear")
  print('\033[33mINVENTORY')
  print('=========')
  option = input("1: Add\n2: View\n3: Count Item\n4: Remove Item\n5: Exit\n> \033[32m")
  if option == "1":
    add()
  elif option == "2":
    view()
  elif option == "3":
    count()
  elif option == "4":
    remove()
  elif option == "5":
    print("Exiting Inventory...")
    break
  else:
    print("Invalid Option!")
    input("Press enter to continue...")

  f = open("inventory.txt",'w')
  f.write(str(myList))
  f.close()


def solution(numbers, target):
    answer = 0
    len_numbers = len(numbers)
    # DFS같은데 내려가는 순서가 조금 다른거 같다...
    def operator(idx=0) :
        if idx < len_numbers:
            numbers[idx] *= 1
            operator(idx+1)

            numbers[idx] *= -1
            operator(idx+1)

        elif sum(numbers) == target :
            nonlocal answer
            answer += 1

    operator()

    return answer

######

def dfs (array, numbers, target, size):
    answer = 0
    if len(array) == size:
        if sum(array) == target:
            return 1
        else:
            return 0
    else :
        A = numbers.pop(0)
        for i in [1, -1]:
            array.append(A * i)
            answer += dfs(array, numbers, target, size)
            array.pop()
        numbers.append(A)
        return answer

def solution_dfs(numbers, target):
    answer = 0
    answer += dfs([numbers[0]], numbers[1:], target, len(numbers))
    answer += dfs([-numbers[0]], numbers[1:], target, len(numbers))

    return answer

#####
# 완전탐색
from itertools import product
def solution_brute(numbers, tareget):
    l = [(x, -x) for x in numbers]
    s = list(map(sum, product(*l)))
    return s.count(target)

####
# bfs
import collections
def solution_bfs(numbers, target):
    answer = 0
    stack = collections.deque([(0, 0)])
    while stack :
        current_sum, num_idx = stack.popleft()

        if num_idx  == len(numbers) :
            if current_sum == target:
                answer += 1
            else :
                number = numbers[num_idx]
                stack.append((current_sum+number, num_idx+1))
                stack.append((current_sum-number, num_idx+1))

    return answer

#####
# dfs2

answer = 0
def DFS(idx, numbers, target, value) :
    global answer
    N = len(nubers)
    if (idx == N and target == value):
        answer += 1
        return
    if (idx==n):
        return

    DFS(idx+1, numbers, target, value+numbers[idx])
    DFS(idx+1, numbers, target, value-numbers[idx])

def solution_dfs2(numbers, target):
    global answer
    DFS(0, numbers, target, 0)
    return answer

## 출처 : 재희님 깃허브(https://github.com/JaeHeee)
def solution_1012(numbers, target):
    node = [0]

    for n in numbers:
        child = []
        for i in node:
            child.append(i + n)
            child.append(i - n)
        node = child
    return node.count(target)

if __name__ == '__main__':
    numbers, target = [1, 1, 1, 1, 1], 3

    print(solution_dfs(numbers, target))


# -*- coding: utf-8 -*-
"""
@author by mkzhou
@time 2020/6/13 11:21
进程池中都Queue
"""

from multiprocessing import Manager, Pool
import os, time, random

def reader(q):
    print('reader 启动{}, 父进程为{}'.format(os.getpid(), os.getppid()))
    for i in range(q.size()):
        print('reader从Queue获取消息{}'.format(q.get(True)))
def writer(q):
    print('writer启动{}，父进程为{}'.format(os.getpid(), os.getppid()))
    for i in 'dog':
        q.put(i)

if __name__ == '__main__':
    print('{}start'.format(os.getpid()))
    q = Manager().Queue()
    po = Pool()
    po.apply(writer, (q, ))
    po.apply(reader, (q))
    po.close()
    po.join()
    print('{} end'.format(os.getpid()))


from setuptools import setup, find_packages

# Doing it as suggested here:
# https://packaging.python.org/guides/single-sourcing-package-version/
# (number 3)

version = {}
with open("mcmala/version.py") as fp:
    exec(fp.read(), version)

with open("README.md") as f:
    readme = f.read()

with open("LICENSE") as f:
    license = f.read()

setup(
    name="mcmala",
    version=version["__version__"],
    description=("Monte-Carlo fo Materials Learning Algorithms. "
                 "A Monte-Carlo frontend for MALA."),
    long_description=readme,
    long_description_content_type='text/markdown',
    url="https://github.com/mala-project/mcmala",
    author="MALA developers",
    license=license,
    packages=find_packages(exclude=("test", "docs", "examples", "install")),
    zip_safe=False,
    install_requires=open('requirements.txt').read().splitlines(),
)


#!/bin/python3

import math
import os
import random
import re
import sys

# Complete the catAndMouse function below.
def catAndMouse(x, y, z):
    # a는 고양이 a와 쥐의 거리, b는 고양이 b와 쥐의 거리
    a = abs(x-z)
    b = abs(y-z)
    if a > b:
        return "Cat B"
    elif a == b:
        return "Mouse C"
    else:
        return "Cat A"

if __name__ == '__main__':
    fptr = open(os.environ['OUTPUT_PATH'], 'w')

    q = int(input())

    for q_itr in range(q):
        xyz = input().split()

        x = int(xyz[0])

        y = int(xyz[1])

        z = int(xyz[2])

        result = catAndMouse(x, y, z)

        fptr.write(result + '\n')

    fptr.close()


import queue

def show_fifo():
    fifo = queue.Queue()
    for i in range(5):
        fifo.put(i)

    while not fifo.empty():
        print(fifo.get(), end=" ")
    print()

# show_fifo()

def show_lifo():
    lifo = queue.LifoQueue()
    for i in range(5):
        lifo.put(i)

    while not lifo.empty():
        print(f"{lifo.get()}", end=" ")
    print()

show_lifo()


import csv
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from joblib import load
import warnings
warnings.filterwarnings("ignore")

day = 13

out_st=21
in_st=12

features_links=[]

with open("Principal_Components.csv", 'r') as file:
    reader = csv.reader(file, delimiter=';')
    for row in reader:
        if int(row[1])==out_st and int(row[2])==in_st and int(row[3])==day:
            features_links.append(int(row[0]))

features_array=[]
values=[]
with open("PC_Dataframe.csv", 'r') as file:
    reader = csv.reader(file, delimiter=';')
    next(reader)
    for row in reader:
        if int(row[0]) in features_links:
            features_array.append([float(i) for i in row[1:-1]])
            values.append(float(row[-1]))

fig1, subplots = plt.subplots(nrows=1, ncols=1, sharex=True, sharey=True)

x=[0.5*i for i in range(48)]
fig1.axes[0].plot(x, values,color='black',linewidth=1)
print(values)
y1=[]
with open('knn.pkl', 'rb') as fid:
    regressor = load(fid)
    y1=regressor.predict(features_array)
fig1.axes[0].plot(x, y1.tolist(),color='forestgreen',linewidth=0.8)

y2=[]
with open('SGD.pkl', 'rb') as fid:
    regressor = load(fid)
    y2=regressor.predict(features_array)
fig1.axes[0].plot(x, y2.tolist(),color='crimson',linewidth=0.8)

y3=[]
with open('MLP.pkl', 'rb') as fid:
    regressor = load(fid)
    y3=regressor.predict(features_array)
fig1.axes[0].plot(x, y3.tolist(),color='dodgerblue',linewidth=0.8)

knn_patch        = mpatches.Patch(color='forestgreen',label='KNN')
sgd_patch        = mpatches.Patch(color='crimson',    label='SGD')
mlp_patch        = mpatches.Patch(color='dodgerblue', label='MLP')
true_value_patch = mpatches.Patch(color='black',      label='True value')
plt.legend(handles=[knn_patch, sgd_patch, mlp_patch, true_value_patch])

plt.xlabel('Time')
plt.ylabel('Passengers number')

plt.xticks([i for i in range(0,25,4)], [i for i in range(0,25,4)])
plt.show()


from manim import *
import math

class AdvancedHexagon(Scene):
    def construct(self):
        self.radius = 3

        outer_hexagon = self.add_outer_hexagon()
        (q_hex, q_brace, q_tex, q_tracker, r_tracker, updaters) = self.define_q()
        q_eqn_tex = self.eqn_radius_in_q()
        self.geometrical_radius_in_q(q_hex, q_tracker)
        self.play(q_tracker.animate.set_value(6))
        self.geometrical_radius_in_q(q_hex, q_tracker)
        self.define_basis(q_tracker)
        for mobj, upd in updaters:
            mobj.remove_updater(upd)
        q_hex.remove_updaters()
        self.play(ShrinkToCenter(q_brace), ShrinkToCenter(q_tex), ShrinkToCenter(q_eqn_tex))
        self.calculate_corners_in_basis(outer_hexagon)
        self.play(*[ShrinkToCenter(mobj) for mobj in q_hex.children()])
        self.transform_hexagon_in_basis(outer_hexagon, q_tracker)
        self.show_transformed_normals(outer_hexagon)
        self.show_transformed_contains(outer_hexagon)
        # self.transform_back(outer_hexagon)

    def add_outer_hexagon(self):
        res = Hexagon(ORIGIN, self.radius, dots=True, labels=True)
        self.add(*res.children())
        self.wait()
        return res

    def define_q(self):
        q = ValueTracker(4)
        radius_tracker = QToRadiusTracker(q, self.radius)
        hex = Hexagon(ORIGIN, radius_tracker.get_value())

        self.play(*[Create(mobj) for mobj in hex.children()])

        brace = Brace(hex.lines[1], direction=UP)
        tex = MathTex(r'\frac{1}{q}r').scale(0.75)
        brace.put_at_tip(tex)

        self.play(GrowFromCenter(brace), GrowFromCenter(tex))

        def brace_updater(x):
            return x.become(Brace(hex.lines[1], direction=UP))

        def tex_updater(x):
            cp_x = x.copy()
            brace.put_at_tip(cp_x)
            return x.become(cp_x)

        hex.add_updaters(radius_tracker)
        brace.add_updater(brace_updater)
        tex.add_updater(tex_updater)

        self.play(q.animate.set_value(3))
        self.play(q.animate.set_value(7))
        self.play(q.animate.set_value(5))
        self.wait()

        brace.remove_updater(brace_updater)

        self.play(Transform(brace, Brace(hex.lines[-2], direction=DOWN)))
        self.wait()

        def brace_updater(x):
            return x.become(Brace(hex.lines[-2], direction=DOWN))

        brace.add_updater(brace_updater)

        return (hex, brace, tex, q, radius_tracker, [(brace, brace_updater), (tex, tex_updater)])

    def eqn_radius_in_q(self):
        tex = MathTex(r'r = \frac{1}{q}r \cdot q').shift([0, 1.5, 0])
        self.play(GrowFromCenter(tex))
        self.wait(2)

        self.play(MoveAlongPath(tex, Line([0, 1.5, 0], [0, 3.2, 0])))
        self.play(ScaleInPlace(tex, 0.75))
        self.wait()
        return tex

    def geometrical_radius_in_q(self, c_hex, q_tracker):
        inner_radius = self.radius / q_tracker.get_value()
        inner_height = inner_radius * (math.sqrt(3) / 2)
        q = int(q_tracker.get_value())

        brace = Brace(c_hex.lines[2], direction=LEFT)
        brace_tex = MathTex(r'\frac{\sqrt{3}}{2}\frac{1}{q}r').scale(0.75)
        brace.put_at_tip(brace_tex)
        self.play(GrowFromCenter(brace), GrowFromCenter(brace_tex))
        self.wait(2)

        tmp_line = Line([-inner_radius, inner_height, 0], [-inner_radius, -inner_height, 0])
        tmp_brace = Brace(tmp_line, direction=LEFT)
        tmp_brace_tex = MathTex(r'\frac{\sqrt{3}}{2}\frac{1}{q}r\cdot 2').scale(0.75)
        tmp_brace.put_at_tip(tmp_brace_tex)
        self.play(Transform(brace, tmp_brace), FadeTransform(brace_tex, tmp_brace_tex))
        brace_tex = tmp_brace_tex
        self.wait(2)

        tmp_brace_tex = MathTex(r'\sqrt{3}\frac{1}{q}r').scale(0.75)
        brace.put_at_tip(tmp_brace_tex)
        self.play(FadeTransform(brace_tex, tmp_brace_tex))
        brace_tex = tmp_brace_tex

        y_shift_tracker = ValueTracker(0)
        def brace_updater(x):
            tmp_line = Line([-inner_radius, inner_height + y_shift_tracker.get_value(), 0], [-inner_radius, -inner_height + y_shift_tracker.get_value(), 0])
            return x.become(Brace(tmp_line, direction=LEFT))

        def brace_tex_updater(x):
            cp_x = x.copy()
            brace.put_at_tip(cp_x)
            return x.become(cp_x)

        brace.add_updater(brace_updater)
        brace_tex.add_updater(brace_tex_updater)

        height_hexes = []

        for i in range(int(q/2)):
            self.play(y_shift_tracker.animate.set_value(inner_height*2*i + inner_height))

            dot = Dot([0, inner_height * 2 * (i+1), 0])
            self.play(GrowFromCenter(dot))
            hex = Hexagon([0, inner_height * 2 * (i+1), 0], inner_radius)
            if (i+1) * 2 == q:
                hex.lines = [hex.lines[3], hex.lines[5]]
            self.play(*[Create(mobj) for mobj in hex.children()])
            self.play(ShrinkToCenter(dot))
            height_hexes.append(hex)

        brace.remove_updater(brace_updater)
        brace_tex.remove_updater(brace_tex_updater)
        self.play(ShrinkToCenter(brace), ShrinkToCenter(brace_tex))

        counter_texes = []
        for i in range(q):
            counter_tex = MathTex(str(int(i)+1)).scale(0.5).next_to([-inner_radius, inner_height * i + inner_height / 2, 0], LEFT)
            self.play(GrowFromCenter(counter_tex))
            counter_texes.append(counter_tex)

        show_q_tex = MathTex(f'q={q}').next_to([inner_radius, inner_height, 0], RIGHT)
        self.play(GrowFromCenter(show_q_tex))
        self.wait()

        anims = []
        for hhex in height_hexes:
            anims.extend([ShrinkToCenter(c) for c in hhex.children()])

        anims.extend([ShrinkToCenter(c) for c in counter_texes])
        anims.append(ShrinkToCenter(show_q_tex))
        self.play(*anims)
        self.wait()

    def define_basis(self, q_tracker):
        s = UP
        s_arrow = Arrow(ORIGIN, s, buff=0)
        s_tex = MathTex(r'\vec{s} = \langle 0, 1 \rangle').scale(0.75).next_to(s_arrow.get_end(), LEFT)

        self.play(Create(s_arrow), GrowFromCenter(s_tex))

        t = np.array([math.cos(30 * DEGREES), math.sin(30 * DEGREES), 0])
        t_arrow = Arrow(ORIGIN, t, buff=0)
        t_tex = MathTex(r'\vec{t} = \langle \cos(30^\circ), \sin(30^\circ) \rangle').scale(0.5).next_to(t_arrow.get_end(), RIGHT)

        self.play(Create(t_arrow), GrowFromCenter(t_tex))
        self.wait(2)

        tmp_t_tex = MathTex(r'\vec{t} = \left\langle \frac{\sqrt{3}}{2}, \frac{1}{2} \right\rangle').scale(0.5).next_to(t_arrow.get_end(), RIGHT)
        self.play(FadeTransform(t_tex, tmp_t_tex))
        t_tex = tmp_t_tex

        p = np.array([1.2, 1.5, 0])
        p_dot = Dot(p)
        p_tex = MathTex(r'\mathbf{p}').scale(0.75).next_to(p_dot, RIGHT)

        self.play(Create(p_dot), GrowFromCenter(p_tex))
        self.wait(2)

        self.play(ShrinkToCenter(t_tex), ShrinkToCenter(s_tex), ShrinkToCenter(p_tex))

        p_rebased = switch_basis(p, s, t)
        self.play(
            Transform(s_arrow, Arrow(ORIGIN, s * p_rebased[0], buff=0)),
            Transform(t_arrow, Arrow(ORIGIN, t * p_rebased[1], buff=0))
        )
        self.play(
            MoveAlongPath(s_arrow, Line(ORIGIN + s_arrow.get_center(), t * p_rebased[1] + s_arrow.get_center()))
        )

        self.wait(2)

        px_tracker = ValueTracker(p[0])
        py_tracker = ValueTracker(p[1])

        def p_dot_updater(x):
            p = np.array((px_tracker.get_value(), py_tracker.get_value(), 0))
            return x.become(Dot(p))

        def p_tex_updater(x):
            p = np.array((px_tracker.get_value(), py_tracker.get_value(), 0))
            return x.become(x.copy().next_to(Dot(p), RIGHT))

        def t_arrow_updater(x):
            p = np.array((px_tracker.get_value(), py_tracker.get_value(), 0))
            p_rebased = switch_basis(p, s, t)
            if p_rebased[1] == 0:
                return x.scale(0).set_opacity(0)
            return x.become(Arrow(ORIGIN, t * p_rebased[1], buff=0))

        def s_arrow_updater(x):
            p = np.array((px_tracker.get_value(), py_tracker.get_value(), 0))
            p_rebased = switch_basis(p, s, t)
            if p_rebased[0] == 0:
                return x.scale(0).set_opacity(0)
            return x.become(Arrow(t * p_rebased[1], p, buff=0))

        p_dot.add_updater(p_dot_updater)
        p_tex.add_updater(p_tex_updater)
        t_arrow.add_updater(t_arrow_updater)
        s_arrow.add_updater(s_arrow_updater)

        self.play(px_tracker.animate.set_value(-1.2), py_tracker.animate.set_value(1.7))
        self.wait(0.5)
        self.play(px_tracker.animate.set_value(-1.6), py_tracker.animate.set_value(-2))
        self.wait(0.5)
        self.play(px_tracker.animate.set_value(1.6), py_tracker.animate.set_value(-1.5))
        self.wait(0.5)
        self.play(px_tracker.animate.set_value(1.2), py_tracker.animate.set_value(1.5))
        self.wait()

        inner_radius = self.radius / q_tracker.get_value()
        inner_height = (math.sqrt(3) / 2) * inner_radius
        length = inner_height * 2

        s_term = r'\sqrt{3}\frac{1}{q}r\vec{s}'
        t_term = r'\sqrt{3}\frac{1}{q}r\vec{t}'
        for (i, j) in [
            (1, 0), (2, 0), (3, 0), (3, -1), (4, -1), (4, -2),
            (1, 0), (1, 1), (1, 2), (2, 2)
        ]:
            p = length * i * s + length * j * t

            tmp_p_tex = (
                MathTex(pretty_linear(pretty_linear('0', str(i), s_term), str(j), t_term))
                .scale(0.75).next_to(p_dot, RIGHT)
            )
            tmp_p_tex.add_updater(p_tex_updater)
            self.play(
                px_tracker.animate.set_value(p[0]),
                py_tracker.animate.set_value(p[1]),
                FadeTransform(p_tex, tmp_p_tex)
            )
            p_tex = tmp_p_tex
            self.wait()

        p_dot.remove_updater(p_dot_updater)
        p_tex.remove_updater(p_tex_updater)
        t_arrow.remove_updater(t_arrow_updater)
        s_arrow.remove_updater(s_arrow_updater)

        self.play(
            FadeOut(s_arrow), FadeOut(t_arrow),
            ShrinkToCenter(p_dot), ShrinkToCenter(p_tex)
        )
        self.wait()

    def calculate_corners_in_basis(self, outer_hexagon):
        s = UP
        s_arrow = Arrow(ORIGIN, s, buff=0)
        s_tex = MathTex(r'\vec{s} = \langle 0, 1 \rangle').scale(0.75).next_to(s_arrow.get_end(), LEFT)

        self.play(Create(s_arrow), GrowFromCenter(s_tex))

        t = np.array([math.cos(30 * DEGREES), math.sin(30 * DEGREES), 0])
        t_arrow = Arrow(ORIGIN, t, buff=0)
        t_tex = MathTex(r'\vec{t} = \langle \frac{\sqrt{3}}{2}, \frac{1}{2} \rangle').scale(0.5).next_to(t_arrow.get_end(), RIGHT)

        self.play(Create(t_arrow), GrowFromCenter(t_tex))
        self.wait(2)
        self.play(
            FadeOut(s_arrow), FadeOut(t_arrow),
            Transform(s_tex, s_tex.copy().move_to([-3.5, 1.7, 0])),
            Transform(t_tex, t_tex.copy().move_to([-3.75, 1.0, 0]))
        )
        self.wait()

        l_tex = MathTex(r'L = \sqrt{3}\frac{1}{q}r').move_to([-3.75, -1.5, 0])

        for idx, steps in enumerate([
            [
                r'(r, 0) = a\sqrt{3}\frac{1}{q}r\vec{s} + b\sqrt{3}\frac{1}{q}r\vec{t}',
                [
                    r'r = a\sqrt{3}\frac{1}{q}r\cdot 0 + b \sqrt{3}\frac{1}{q}r\cdot \frac{\sqrt{3}}{2}',
                    r'0 = a\sqrt{3}\frac{1}{q}r\cdot 1 + b \sqrt{3}\frac{1}{q}r\cdot \frac{1}{2}'
                ],
                [
                    r'r = b\frac{3}{2q}r',
                    r'0 = a\frac{\sqrt{3}}{q}r + b \frac{\sqrt{3}}{2q}r'
                ],
                [
                    r'1 = b\frac{3}{2q}',
                    r'0 = a + b \frac{\sqrt{3}}{2q}r \cdot \frac{q}{\sqrt{3}r}'
                ],
                [
                    r'b = \frac{2q}{3}',
                    r'a = -b \frac{1}{2}'
                ],
                [
                    r'b = \frac{2q}{3}',
                    r'a = -\frac{q}{3}'
                ],
                r'-\frac{q}{3} \sqrt{3}\frac{1}{q}r\vec{s} + \frac{2q}{3}\sqrt{3}\frac{1}{q}r\vec{t}',
                r'-\frac{q}{3} L\vec{s} + \frac{2q}{3} L\vec{t}'
            ],
            [
                r'\left(\frac{1}{2}r, \frac{\sqrt{3}}{2}r\right) = a\sqrt{3}\frac{1}{q}r\vec{s} + b\sqrt{3}\frac{1}{q}r\vec{t}',
                [
                    r'\frac{1}{2}r = a \sqrt{3}\frac{1}{q}r \cdot 0 + b\sqrt{3}\frac{1}{q}r \cdot \frac{\sqrt{3}}{2}',
                    r'\frac{\sqrt{3}}{2}r = a\sqrt{3}\frac{1}{q}r\cdot 1 + b\sqrt{3}\frac{1}{q}r\cdot \frac{1}{2}'
                ],
                [
                    r'\frac{1}{2}r = \frac{3b}{2q}r',
                    r'\frac{\sqrt{3}}{2}r = \frac{\sqrt{3}a}{q}r + \frac{\sqrt{3}b}{2q}r'
                ],
                [
                    r'1 = \frac{3b}{q}',
                    r'1 = \frac{2a}{q} + \frac{b}{q}'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'q = 2a + b'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'2a = q - \frac{q}{3}'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'a = \frac{q}{3}'
                ],
                r'\frac{q}{3}L\vec{s} + \frac{q}{3}L\vec{t}'
            ],
            [
                r'\left(-\frac{1}{2}r, \frac{\sqrt{3}}{2}r) = a\sqrt{3}\frac{1}{q}r\vec{s} + b\sqrt{3}\frac{1}{q}r\vec{t}',
                [
                    r'-\frac{1}{2}r = a\sqrt{3}\frac{1}{q}r\cdot 0 + b\sqrt{3}\frac{1}{q}r \cdot \frac{\sqrt{3}}{2}',
                    r'\frac{\sqrt{3}}{2}r = a\sqrt{3}\frac{1}{q}r\cdot 1 + b\sqrt{3}\frac{1}{q}r\cdot \frac{1}{2}'
                ],
                [
                    r'-\frac{1}{2}r = \frac{3b}{2q}r',
                    r'\frac{\sqrt{3}}{2}r = \frac{\sqrt{3}a}{q}r + \frac{\sqrt{3}b}{2q}r'
                ],
                [
                    r'1 = -\frac{3b}{q}',
                    r'1 = \frac{2a}{q} + \frac{b}{q}'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'a = \frac{q}{2} - \frac{b}{2}'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'a = \frac{q}{2} + \frac{q}{6}'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'a = \frac{2q}{3}'
                ],
                r'\frac{2q}{3}L\vec{s} - \frac{q}{3}L\vec{t}'
            ],
            [
                r'(-r, 0) = a\sqrt{3}\frac{1}{q}r\vec{s} + b\sqrt{3}\frac{1}{q}r\vec{t}',
                [
                    r'-r = a\sqrt{3}\frac{1}{q}r\cdot 0 + b\sqrt{3}\frac{1}{q}r \cdot \frac{\sqrt{3}}{2}',
                    r'0 = a\sqrt{3}\frac{1}{q}r\cdot 1 + b\sqrt{3}\frac{1}{q}r\cdot \frac{1}{2}'
                ],
                [
                    r'-r = \frac{3b}{2q}r',
                    r'0 = \frac{\sqrt{3}a}{q}r + \frac{\sqrt{3}b}{2q}r'
                ],
                [
                    r'1 = -\frac{3b}{2q}',
                    r'0 = a + \frac{b}{2}'
                ],
                [
                    r'b = -\frac{2q}{3}',
                    r'a = -\frac{b}{2}'
                ],
                [
                    r'b = -\frac{2q}{3}',
                    r'a = \frac{q}{3}'
                ],
                r'\frac{q}{3}L\vec{s} - \frac{2q}{3}L\vec{t}'
            ],
            [
                r'\left(-\frac{1}{2}r, -\frac{\sqrt{3}}{2}r\right) = a\sqrt{3}\frac{1}{q}r\vec{s} + b\sqrt{3}\frac{1}{q}r\vec{t}',
                [
                    r'-\frac{1}{2}r = a\sqrt{3}\frac{1}{q}r\cdot 0 + b\sqrt{3}\frac{1}{q}r \cdot \frac{\sqrt{3}}{2}',
                    r'-\frac{\sqrt{3}}{2}r = a\sqrt{3}\frac{1}{q}r\cdot 1 + b\sqrt{3}\frac{1}{q}r\cdot \frac{1}{2}'
                ],
                [
                    r'-\frac{1}{2}r = \frac{3b}{2q}r',
                    r'-\frac{\sqrt{3}}{2}r = \frac{\sqrt{3}a}{q}r + \frac{\sqrt{3}b}{2q}r'
                ],
                [
                    r'1 = -\frac{3b}{q}',
                    r'1 = -\frac{2a}{q} - \frac{b}{q}'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'q = -2a - b'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'2a = -b - q'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'2a = \frac{q}{3} - q'
                ],
                [
                    r'b = -\frac{q}{3}',
                    r'a = -\frac{q}{3}'
                ],
                r'-\frac{q}{3}L\vec{s} - \frac{q}{3}L\vec{t}'
            ],
            [
                r'\left(\frac{1}{2}r, -\frac{\sqrt{3}}{2}r\right) = a\sqrt{3}\frac{1}{q}r\vec{s} + b\sqrt{3}\frac{1}{q}r\vec{t}',
                [
                    r'\frac{1}{2}r = a \sqrt{3}\frac{1}{q}r \cdot 0 + b\sqrt{3}\frac{1}{q}r \cdot \frac{\sqrt{3}}{2}',
                    r'-\frac{\sqrt{3}}{2}r = a\sqrt{3}\frac{1}{q}r\cdot 1 + b\sqrt{3}\frac{1}{q}r\cdot \frac{1}{2}'
                ],
                [
                    r'\frac{1}{2}r = \frac{3b}{2q}r',
                    r'-\frac{\sqrt{3}}{2}r = \frac{\sqrt{3}a}{q}r + \frac{\sqrt{3}b}{2q}r'
                ],
                [
                    r'1 = \frac{3b}{q}',
                    r'1 = -\frac{2a}{q} - \frac{b}{q}'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'q = -2a - b'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'2a = -b - q'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'2a = -\frac{q}{3} - q'
                ],
                [
                    r'b = \frac{q}{3}',
                    r'a = -\frac{2q}{3}'
                ],
                r'-\frac{2q}{3}L\vec{s} + \frac{q}{3}L\vec{t}'
            ]
        ]):
            p_tex = outer_hexagon.texs[idx]
            p_tex_2 = None
            self.play(Transform(p_tex, p_tex.copy().move_to([0, 1.5, 0])))
            self.wait()
            for step in steps:
                if isinstance(step, str):
                    p_tex_tmp = MathTex(step).scale(0.5).move_to([0, 1.5, 0])
                    if p_tex_2 is None:
                        self.play(FadeTransform(p_tex, p_tex_tmp))
                    else:
                        self.play(FadeTransform(p_tex, p_tex_tmp), ShrinkToCenter(p_tex_2))
                        p_tex_2 = None
                    p_tex = p_tex_tmp
                else:
                    p_tex_tmp = MathTex(step[0]).scale(0.5).move_to([0, 1.8, 0])
                    p_tex_2_tmp = MathTex(step[1]).scale(0.5).move_to([0, 1.2, 0])

                    if p_tex_2 is None:
                        self.play(FadeTransform(p_tex, p_tex_tmp), GrowFromCenter(p_tex_2_tmp))
                    else:
                        self.play(FadeTransform(p_tex, p_tex_tmp), FadeTransform(p_tex_2, p_tex_2_tmp))

                    p_tex = p_tex_tmp
                    p_tex_2 = p_tex_2_tmp

                self.wait(4 if idx == 0 else 1)

            if p_tex_2 is not None:
                self.play(ShrinkToCenter(p_tex_2))
                p_tex_2 = None

            p_tex_tmp = p_tex.copy().next_to(outer_hexagon.dots[idx], [RIGHT, RIGHT, LEFT, LEFT, DOWN, DOWN][idx])
            self.play(FadeTransform(p_tex, p_tex_tmp))
            outer_hexagon.texs[idx] = p_tex_tmp
            if idx == 0:
                self.play(GrowFromCenter(l_tex))

        self.play(ShrinkToCenter(l_tex), ShrinkToCenter(s_tex), ShrinkToCenter(t_tex))

    def transform_hexagon_in_basis(self, outer_hexagon, q_tracker):
        vertex_starts = [
            np.array([self.radius * math.cos(angle * DEGREES), self.radius * math.sin(angle * DEGREES)])
            for angle in range(0, 360, 60)
        ]
        basis = np.array([
            [0, math.cos(30 * DEGREES)],
            [1, math.sin(30 * DEGREES)]
        ])
        inv_basis = np.linalg.inv(basis)
        q = q_tracker.get_value()
        inner_radius = self.radius / q
        L = math.sqrt(3) * inner_radius
        # L * q = math.sqrt(3) * (self.radius / q) * q = math.sqrt(3) * self.radius
        vertex_ends = [
            ((inv_basis @ vertex) / (L * q)) * 4
            for vertex in vertex_starts
        ]

        tex_dirs_initial = [RIGHT, RIGHT, LEFT, LEFT, DOWN, DOWN]
        tex_dirs_final = [UP, RIGHT, RIGHT, DOWN, LEFT, LEFT]

        reference_dot = Dot(ORIGIN)
        tex_starts = [
            tex.copy().next_to(reference_dot, dir).shift(project_2_to_3(vert)).get_center()
            for (tex, dir, vert) in zip(outer_hexagon.texs, tex_dirs_initial, vertex_starts)
        ]
        tex_ends = [
            tex.copy().next_to(reference_dot, dir).shift(project_2_to_3(vert)).get_center()
            for (tex, dir, vert) in zip(outer_hexagon.texs, tex_dirs_final, vertex_ends)
        ]

        alpha = ValueTracker(0)

        def create_updaters(idx):
            line_start_initial = vertex_starts[idx]
            line_start_finish = vertex_ends[idx]
            line_start_delta = line_start_finish - line_start_initial

            line_end_initial = vertex_starts[(idx + 1) % 6]
            line_end_finish = vertex_ends[(idx + 1) % 6]
            line_end_delta = line_end_finish - line_end_initial

            tex_initial = tex_starts[idx]
            tex_finish = tex_ends[idx]
            tex_delta = tex_finish - tex_initial

            def dot_updater(x):
                return x.become(Dot(project_2_to_3(line_start_initial + line_start_delta * alpha.get_value())))

            def line_updater(x):
                return x.become(
                    Line(
                        project_2_to_3(line_start_initial + line_start_delta * alpha.get_value()),
                        project_2_to_3(line_end_initial + line_end_delta * alpha.get_value())
                    )
                )

            def tex_updater(x):
                return x.become(x.copy().move_to(tex_initial + tex_delta * alpha.get_value()))

            return [
                (outer_hexagon.dots[idx], dot_updater),
                (outer_hexagon.lines[idx], line_updater),
                (outer_hexagon.texs[idx], tex_updater)
            ]

        alpha_updaters = []
        for idx in range(6):
            new_updaters = create_updaters(idx)
            for (mobj, updater) in new_updaters:
                mobj.add_updater(updater)
            alpha_updaters.extend(new_updaters)

        self.play(alpha.animate.set_value(1))

        for (mobj, updater) in alpha_updaters:
            mobj.remove_updater(updater)

        anims = []
        new_texs = []
        for (tex, dot, dir, new_label) in zip(outer_hexagon.texs, outer_hexagon.dots, tex_dirs_final, [
            r'\left(-\frac{1}{3}, \frac{2}{3}\right)qL',
            r'\left(\frac{1}{3}, \frac{1}{3}\right)qL',
            r'\left(\frac{2}{3}, -\frac{1}{3}\right)qL',
            r'\left(\frac{1}{3}, -\frac{2}{3}\right)qL',
            r'\left(-\frac{1}{3}, -\frac{1}{3}\right)qL',
            r'\left(-\frac{2}{3}, \frac{1}{3}\right)qL'
        ]):
            tmp_tex = MathTex(new_label).scale(0.5).next_to(dot, dir)
            new_texs.append(tmp_tex)
            anims.append(FadeTransform(tex, tmp_tex))

        self.play(*anims)
        outer_hexagon.texs = new_texs

        s_arrow = Arrow(ORIGIN, RIGHT, buff=0)
        s_tex = MathTex(r'\vec{s}').next_to(s_arrow, RIGHT)
        t_arrow = Arrow(ORIGIN, UP, buff=0)
        t_tex = MathTex(r'\vec{t}').next_to(t_arrow, UP)
        self.play(
            FadeIn(s_arrow), GrowFromCenter(s_tex),
            FadeIn(t_arrow), GrowFromCenter(t_tex)
        )
        self.wait(4)
        self.play(
            FadeOut(s_arrow), ShrinkToCenter(s_tex),
            FadeOut(t_arrow), ShrinkToCenter(t_tex)
        )
        self.wait()

    def show_transformed_normals(self, outer_hexagon):
        for (idx, (texs, dot_texs)) in enumerate([
            (
                [
                    r'\langle -\frac{1}{3}qL, -\frac{2}{3}qL \rangle',
                    r'\langle -1, -2 \rangle'
                ],
                [
                    r'\frac{1}{2}\left(\left(-\frac{1}{3},\frac{2}{3}\right)qL + \left(\frac{1}{3}, \frac{1}{3}\right)qL\right)',
                    r'\frac{1}{6}qL \left(0, 1\right)'
                ]
            ),
            (
                [
                    r'\langle -\frac{2}{3}qL, -\frac{1}{3}qL \rangle',
                    r'\langle -2, -1 \rangle'
                ],
                [
                    r'\frac{1}{2}\left(\left(\frac{1}{3}, \frac{1}{3}\right)qL + \left(\frac{2}{3}, -\frac{1}{3}\right)qL\right)',
                    r'\frac{1}{6}qL \left(1, 0\right)'
                ]
            ),
            (
                [
                    r'\langle -\frac{1}{3}qL, \frac{1}{3}qL \rangle',
                    r'\langle -1, 1 \rangle',
                ],
                [
                    r'\frac{1}{2}\left(\left(\frac{2}{3}, -\frac{1}{3}\right)qL + \left(\frac{1}{3}, -\frac{2}{3}\right)qL\right)',
                    r'\frac{1}{6}qL \left(1, -1\right)'
                ]
            ),
            (
                [
                    r'\langle \frac{1}{3}qL, \frac{2}{3}qL \rangle',
                    r'\langle 1, 2 \rangle'
                ],
                [
                    r'\frac{1}{2}\left(\left(\frac{1}{3}, -\frac{2}{3}\right)qL + \left(-\frac{1}{3}, -\frac{1}{3}\right)qL\right)',
                    r'\frac{1}{6}qL \left(0, -1\right)'
                ]
            ),
            (
                [
                    r'\langle \frac{2}{3}qL, \frac{1}{3}qL \rangle',
                    r'\langle 2, 1 \rangle'
                ],
                [
                    r'\frac{1}{2}\left(\left(-\frac{1}{3}, -\frac{1}{3}\right)qL + \left(-\frac{2}{3}, \frac{1}{3}\right)qL\right)',
                    r'\frac{1}{6}qL \left(-1, 0\right)'
                ]
            ),
            (
                [
                    r'\langle \frac{1}{3}, -\frac{1}{3} \rangle',
                    r'\langle 1, -1 \rangle'
                ],
                [
                    r'\frac{1}{2}\left(\left(-\frac{2}{3}, \frac{1}{3}\right)qL + \left(-\frac{1}{3}, \frac{2}{3}\right)qL\right)',
                    r'\frac{1}{6}qL \left(-1, 1\right)'
                ]
            ),
        ]):
            start = outer_hexagon.dots[idx].get_center()
            end = outer_hexagon.dots[(idx + 1) % 6].get_center()

            halfway = (start + end) / 2
            unit_v = end - start
            unit_v /= np.linalg.norm(unit_v)

            norm_v = np.cross(np.cross(unit_v, -halfway), unit_v)
            norm_v /= np.linalg.norm(norm_v)

            arrow = Arrow(halfway, halfway + norm_v, buff=0)
            tex = MathTex(texs[0]).scale(0.5).next_to(arrow, norm_v)

            dot = Dot(halfway)
            dot_tex = MathTex(dot_texs[0]).scale(0.5).next_to(dot, -norm_v)
            self.play(FadeIn(arrow), GrowFromCenter(tex), GrowFromCenter(dot), GrowFromCenter(dot_tex))
            for inner_idx in range(1, max(len(texs), len(dot_texs))):
                anims = []
                if inner_idx < len(texs):
                    tmp_tex = MathTex(texs[inner_idx]).scale(0.5).next_to(arrow, norm_v)
                    anims.append(FadeTransform(tex, tmp_tex))
                    tex = tmp_tex
                if inner_idx < len(dot_texs):
                    tmp_tex = MathTex(dot_texs[inner_idx]).scale(0.5).next_to(dot, -norm_v)
                    anims.append(FadeTransform(dot_tex, tmp_tex))
                    dot_tex = tmp_tex
                self.wait()
                self.play(*anims)

            self.wait()
            self.play(ShrinkToCenter(tex), FadeOut(arrow), ShrinkToCenter(dot), ShrinkToCenter(dot_tex))

    def show_transformed_contains(self, outer_hexagon):
        line_centers = []
        normal_vs = []
        for idx in range(6):
            start = outer_hexagon.dots[idx].get_center()
            end = outer_hexagon.dots[(idx + 1) % 6].get_center()
            halfway = (start + end) / 2
            unit_v = end - start
            unit_v /= np.linalg.norm(unit_v)
            norm_v = np.cross(np.cross(unit_v, -halfway), unit_v)
            norm_v /= np.linalg.norm(norm_v)
            line_centers.append(halfway)
            normal_vs.append(norm_v)

        px_tracker = ValueTracker(0)
        py_tracker = ValueTracker(0)
        arrows = []
        arrow_updaters = []

        def make_arrow(base, dir):
            proj = np.dot(-base, dir) * dir
            arrow = Arrow(base, base + proj, buff=0).set_color(GREEN)
            def arrow_updater(x):
                p = np.array((px_tracker.get_value(), py_tracker.get_value(), 0))
                length = np.dot(p - base, dir)
                proj = length * dir
                return x.become(Arrow(base, base + proj, buff=0).set_color(GREEN if length >= 0 else RED))
            return [arrow, arrow_updater]

        for base, dir in zip(line_centers, normal_vs):
            a, upd = make_arrow(base, dir)
            arrows.append(a)
            arrow_updaters.append(upd)

        p_dot = Dot(ORIGIN)
        self.play(GrowFromCenter(p_dot))
        self.play(*[FadeIn(a) for a in arrows])
        self.wait()

        def p_dot_updater(x):
            return x.become(Dot([px_tracker.get_value(), py_tracker.get_value(), 0]))

        p_dot.add_updater(p_dot_updater)
        for a, upd in zip(arrows, arrow_updaters):
            a.add_updater(upd)

        for (s, t, w) in [[-1, 0, 1], [0, 1, 1], [0, 4, 1], [1, -3.5, 1], [3.5, 1.5, 1], [0, 0, 0], [-2, 0, 1], [-2.5, 0, 2], [2, 0, 1], [2.5, 0, 2], [0, 0, 1]]:
            self.play(px_tracker.animate.set_value(s), py_tracker.animate.set_value(t))
            if w > 0:
                self.wait(w)

        p_dot.remove_updater(p_dot_updater)
        for a, upd in zip(arrows, arrow_updaters):
            a.remove_updater(upd)

        self.play(ShrinkToCenter(p_dot), *[FadeOut(a) for a in arrows])

    def transform_back(self, outer_hexagon):
        tmp_hexagon = Hexagon(ORIGIN, self.radius, dots=True, labels=True)

        self.play(
            *[Transform(og_line, new_line) for (og_line, new_line) in zip(outer_hexagon.lines, tmp_hexagon.lines)],
            *[Transform(og_dot, new_dot) for (og_dot, new_dot) in zip(outer_hexagon.dots, tmp_hexagon.dots)],
            *[Transform(og_tex, new_tex) for (og_tex, new_tex) in zip(outer_hexagon.texs, tmp_hexagon.texs)]
        )

class Hexagon:
    def __init__(self, center, radius, dots=False, labels=False, center_tex=None, radius_tex='r') -> None:
        if center_tex is None:
            center_tex = [pretty_float(i) for i in center]

        self.radius = radius
        self.center = center

        vertices = tuple(
            (center[0] + radius * math.cos(angle * DEGREES), center[1] + radius * math.sin(angle * DEGREES), center[2])
            for angle in range(0, 360, 60)
        )

        if dots:
            self.dots = [Dot(vert) for vert in vertices]
        else:
            self.dots = []

        self.lines = [Line(vertices[i], vertices[(i+1) % len(vertices)]) for i in range(len(vertices))]

        self.texs = []
        if labels:
            reference_dot = Dot(ORIGIN)
            self.texs = [
                MathTex(
                    pretty_coords(
                        pretty_linear(center_tex[0], pretty_cos(angle), radius_tex),
                        pretty_linear(center_tex[1], pretty_sin(angle), radius_tex)
                    )
                ).scale(0.75).next_to(vert, direction=direction).shift(reference_dot.get_critical_point(direction))
                for (angle, vert, direction) in zip(range(0, 360, 60), vertices, [RIGHT, RIGHT, LEFT, LEFT, DOWN, DOWN])
            ]

        self.tracker_pairs = []

    def children(self):
        return [*self.lines, *self.dots, *self.texs]

    def add_updaters(self, r_tracker, x_tracker=None, y_tracker=None):
        if x_tracker is None:
            x_tracker = ValueTracker(self.center[0])
        if y_tracker is None:
            y_tracker = ValueTracker(self.center[1])

        for angle, line in zip(range(0, 360, 60), self.lines):
            updater = self.create_line_updater(angle, r_tracker, x_tracker, y_tracker)
            self.tracker_pairs.append((line, updater))
            line.add_updater(updater)

    def remove_updaters(self):
        for (mobj, upd) in self.tracker_pairs:
            mobj.remove_updater(upd)

        self.tracker_pairs = []

    def create_line_updater(self, angle, r_tracker, x_tracker, y_tracker):
        def updater(x):
            radius = r_tracker.get_value()
            center_x = x_tracker.get_value()
            center_y = y_tracker.get_value()
            center_z = 0
            return x.become(Line(
                (center_x + radius * math.cos(angle * DEGREES), center_y + radius * math.sin(angle * DEGREES), center_z),
                (center_x + radius * math.cos((angle+60) * DEGREES), center_y + radius * math.sin((angle+60) * DEGREES), center_z)
            ))
        return updater

def pretty_sin(angle):
    sqrt_3_over_2 = r'\frac{\sqrt{3}}{2}'
    return {
        0: '0',
        60: sqrt_3_over_2,
        120: sqrt_3_over_2,
        180: '0',
        240: f'-{sqrt_3_over_2}',
        300: f'-{sqrt_3_over_2}'
    }[angle]

def pretty_cos(angle):
    one_half = r'\frac{1}{2}'
    return {
        0: '1',
        60: one_half,
        120: f'-{one_half}',
        180: '-1',
        240: f'-{one_half}',
        300: one_half
    }[angle]

def pretty_linear(a, b, x):
    if a == '0' and b == '0':
        return '0'

    if a == '0':
        if b == '1':
            return x
        if b == '-1':
            return f'-{x}'
        return f'{b}{x}'

    if b == '0':
        return str(a)

    if b == '1':
        return f'{a}+{x}'

    if b == '-1':
        return f'{a}-{x}'

    if b[0] == '-':
        return f'{a}{b}{x}'

    return f'{a} + {b}{x}'

def pretty_coords(x, y):
    return f'\\left({x}, {y}\\right)'

def pretty_float(f):
    if f == int(f):
        return str(int(f))
    return str(f)

class QToRadiusTracker:
    def __init__(self, q_tracker, radius):
        self.q_tracker = q_tracker
        self.radius = radius

    def get_value(self):
        return self.radius / self.q_tracker.get_value()

# switch from p=(x, y) to (a, b) in the basis [s, t]
def switch_basis(p, s, t):
    # (x, y) = a(s1, s2) + b(t1, t2)
    #
    # |x| = |s1, t1| |a|
    # |y|   |s2, t2| |b|
    #
    # X = BA
    # B^-1 X = A

    return np.linalg.inv(np.array([s[:2], t[:2]]).T) @ np.array(p[:2])

def project_2_to_3(twod):
    return np.array((twod[0], twod[1], 0))


# -*- coding: utf-8 -*-
import os
import random
from selenium import webdriver
from selenium.webdriver import DesiredCapabilities, Remote
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import config
import sys

class BasicCase(object):

    def __init__(self, no_sandbox, headless, executor, virtual_display):
        options = webdriver.ChromeOptions()
        options.add_experimental_option('prefs', {'intl.accept_languages': 'ru,ru_RU'})
        if headless:
            options.add_argument('headless')
        if no_sandbox:
            options.add_argument("-–no-sandbox")
            options.add_argument("--disable-dev-shm-usage")
        if virtual_display:
            from pyvirtualdisplay import Display
            display = Display(visible=0, size=(800, 600))
            display.start()
        if executor:
            self.driver = webdriver.Remote(
                command_executor=executor,
                desired_capabilities=DesiredCapabilities.CHROME,
                options=options,
            )
            return
        self.driver = webdriver.Chrome(config.DRIVER, chrome_options=options)


def CreateBoard(arguments, player, size):
    board = [[0 for c in range(size)] for r in range(size)]
    arg = arguments.split("|")
    for x in range(len(arg)):
        arg[x] = arg[x].replace("[(", "")
        arg[x] = arg[x].replace(")", "")
        arg[x] = arg[x].replace("]", "")
        values = arg[x].split(",")
        piece = int(values[2])
        if piece == 1:
            piece = -1
        elif piece == 2:
            piece = 1
        board[int(values[0])][int(values[1])] = piece
    return board


# coding: utf-8

# author: Fei Gao <leetcode.com@feigao.xyz>

# Problem: game of life
#
# According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."
#
# Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):
#
# Any live cell with fewer than two live neighbors dies, as if caused by under-population.
# Any live cell with two or three live neighbors lives on to the next generation.
# Any live cell with more than three live neighbors dies, as if by over-population..
# Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
#
# Write a function to compute the next state (after one update) of the board given its current state.
#
# Follow up:
#
# Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
# In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?
#
# Credits:Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
#
# Subscribe to see which companies asked this question
#
# Show Tags
#
# Array
#
# Show Similar Problems
#
#  (M) Set Matrix Zeroes

class Solution(object):
    def gameOfLife(self, board):
        """
        :type board: List[List[int]]
        :rtype: void Do not return anything, modify board in-place instead.
        """
        import itertools
        if not board or not board[0]:
            return
        rows = len(board)
        cols = len(board[0])

        def neighbors(r, c):
            nr, nc = [r], [c]
            if r > 0: nr.append(r-1)
            if r < rows - 1: nr.append(r+1)
            if c > 0: nc.append(c-1)
            if c < cols - 1: nc.append(c+1)
            return list(itertools.product(nr, nc))[1:]

        def live(nb, se):
            if se % 100 == 1:
                if 2 <= nb % 100 <= 3:
                    return 1
                else:
                    return 0
            elif se % 100 == 0:
                if nb % 100 == 3:
                    return 1
                else:
                    return 0

        for r, c in itertools.product(range(rows), range(cols)):
            board[r][c] += 100 * live((sum(board[x][y] for x, y in neighbors(r, c))) % 100, board[r][c])

        for r, c in itertools.product(range(rows), range(cols)):
            board[r][c] //= 100

def main():
    solver = Solution()
    tests = [
        (([[1, 1, 1, 1, 1, 1],
           [0, 0, 1, 0, 0, 1]],), 'result'),
        (([[1, 1], [1, 0]],), None)
    ]
    for params, expect in tests:
        print('-' * 5 + 'TEST' + '-' * 5)
        print('Input:  ' + str(params))
        print('Expect: ' + str(expect))

        solver.gameOfLife(*params)
        print('Result: ' + str(params))
    pass

if __name__ == '__main__':
    main()
    pass


"""
Thiago S.  12/02/21
Adaptado do código do Daniel C.: <https://github.com/camposdp/emg-monitor>
Objetivo: receber os dados coletados com o esp32 (freq. de 1000 Hz)
Pacote com buffer (200 linhas e 4 colunas). Cada coluna representa um sensor
"""

from numpy import *
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
from random import randint
import socket
import json
import time
import pandas as pd
import numpy as np
from datetime import datetime

######################################
#Initial parameters

#HOST = socket.gethostbyname(socket.gethostname())
HOST = '192.168.4.1'  # Manual host enter
PORT = 80  # Port

vountario = 'Thiago S. - CPGEI'
Nome_Arquivo = "C:\\Users\\tsimo\\Desktop\\Codigo_Esp32_Dt\\coletaww.txt"

### START QtApp #####
app = QtGui.QApplication([])            # you MUST do this once (initialize things)
####################
pg.setConfigOption('background', 'w') #to config the background color
win = pg.GraphicsWindow(title="EMG Signals") # creates a window  pen='b'

p1 = win.addPlot(title="Signals from EMG system")  # creates empty space for the plot in the window
#p1.showGrid(x=True,y=True)
p1.addLegend()
p1.setYRange(0,2000)
curve1 = p1.plot(pen='b', name='Canal 1')                        # create an empty "plot" (a curve to plot)
curve2 = p1.plot(pen='r', name='Canal 2')
curve3 = p1.plot(pen='g', name='Canal 3')                        # create an empty "plot" (a curve to plot)
curve4 = p1.plot(pen='y', name='Canal 4')

windowWidth = 200                       # width of the window displaying the curve
Xm1 = linspace(0,0,windowWidth)          # create array that will contain the relevant time series
ptr1 = -windowWidth                      # set first x position

Xm2 = linspace(0,0,windowWidth)          # create array that will contain the relevant time series
ptr2 = -windowWidth                      # set first x position

Xm3 = linspace(0,0,windowWidth)          # create array that will contain the relevant time series
ptr3 = -windowWidth                      # set first x position

Xm4 = linspace(0,0,windowWidth)          # create array that will contain the relevant time series
ptr4 = -windowWidth                      # set first x position

# Realtime data plot. Each time this function is called, the data display is updated
def update(data1, data2, data3, data4):
    global curve1, curve2, curve3, curve4, ptr1, ptr2, ptr3, ptr4, Xm1, Xm2, Xm3, Xm4
    Xm1[:-1] = Xm1[1:]
    Xm2[:-1] = Xm2[1:]                      # shift data in the temporal mean 1 sample left
    Xm3[:-1] = Xm3[1:]
    Xm4[:-1] = Xm4[1:]
    value1 = data1
    value2 = data2               # read line (single value) from the serial port
    value3 = data3
    value4 = data4               # read line (single value) from the serial port
    Xm1[-1] = float(value1)                 # vector containing the instantaneous values
    Xm2[-1] = float(value2)
    Xm3[-1] = float(value3)                 # vector containing the instantaneous values
    Xm4[-1] = float(value4)
    ptr1 += 1
    ptr2 += 1                              # update x position for displaying the curve
    ptr3 += 1
    ptr4 += 1                              # update x position for displaying the curve
    curve1.setData(Xm1)                     # set the curve with this data
    curve1.setPos(ptr1,0)                   # set x position in the graph to 0
    curve2.setData(Xm2)                     # set the curve with this data
    curve2.setPos(ptr2,0)                   # set x position in the graph to 0
    curve3.setData(Xm3)                     # set the curve with this data
    curve3.setPos(ptr3,0)                   # set x position in the graph to 0
    curve4.setData(Xm4)                     # set the curve with this data
    curve4.setPos(ptr4,0)                   # set x position in the graph to 0
    QtGui.QApplication.processEvents()    # you MUST process the plot now

#Main function
def connected(c):

    global msg

    while True:

        msg = c.recv(16384) #receive data...

        if msg is not None:
            t = tempo = time.time()
            #now = datetime.now() #print(type(msg)) #bytes
            a = json.loads(msg) #print(type(a)) #dict
            df = pd.DataFrame(a)  #transforma o dicionário em DataFrame - print(df.head())
            #print(df.tail())
            #print("Antes:", time.time())
            update(str(t), df['data2'][5], df['data3'][5], df['data4'][5]) #Plota data from EMG System
            #print("Depois:", time.time())
            arquivo = open(Nome_Arquivo, "a", newline="")
            #arquivo.write(str(t)+str('-')+str(now.hour)+str(':')+str(now.minute)+str(':')+str(now.second)+str(':')+str(now.microsecond)+str(df.values))
            arquivo.write(str(t)+str(df.values))
            arquivo.write('\n')
            arquivo.close()

######################################
# Main Code
######################################

#Create Socket
sock = socket.socket()
sock.connect((HOST, PORT)) #Conecta-se ao servidor (Esp32)
#tcp.bind(orig)
#tcp.listen(1)

while True:
    try:
        connected(sock)

    except Exception as e: pass #print(e)

sock.close()
### END QtApp ####
pg.QtGui.QApplication.exec_() # you MUST put this at the end
##################


import networkx as nx
import community as com

def calculate_Modulartiy(com_c):

    m = len(G.edges())                         #m 为图中的总边数
    lc = 0                                     #lc为社区c内部边的条数
    Oc = 0
    Dc = 0
    for each_edge in range(m):
        if(G.edges()[each_edge][0] in com_c  and G.edges()[each_edge][1] in com_c ):
            lc +=1
        else:
            if((G.edges()[each_edge][0] in com_c  and G.edges()[each_edge][1] not in com_c)or (G.edges()[each_edge][0] not in com_c  and G.edges()[each_edge][1] in com_c)):
                Oc +=1
    Dc = 2*lc + Oc

    return(lc/m - (Dc/(2*m))*(Dc/(2*m)))

G = nx.read_gml('E:\社区 数据\karate\karate.gml')
partition = com.best_partition(G)
print(partition)
print(partition.values())
print(partition.keys())
com_dict = {0:[],1:[],2:[],3:[]}

for i in partition.keys():
    if partition[i] == 0:
        com_dict[0].append(i)
    elif partition[i] == 1:
        com_dict[1].append(i)
    elif partition[i] == 2:
        com_dict[2].append(i)
    else:
        com_dict[3].append(i)

print(com_dict)
Q = calculate_Modulartiy(com_dict[0])+calculate_Modulartiy(com_dict[1])+calculate_Modulartiy(com_dict[2])+calculate_Modulartiy(com_dict[3])
print('Q = %f' %Q)


binary_datasets = ['glass1',
                   'wisconsin',
                   'pima',
                   'yeast1',
                   'haberman',
                   'vehicle0',
                   'ecoli1',
                   'new-thyroid1',
                   'yeast3',
                   'yeast-0-3-5-9_vs_7-8',
                   'cleveland-0_vs_4',
                   'winequality-red-4']

multi_datasets = ['balance', 'hayes-roth']

if __name__ == '__main__':
    print("Just keep on movin!")


"""tools to print object shape or type

"""

# from utils.print_tool import print_config
def print_config(config, file=None):
    print('='*10, ' important config: ', '='*10, file=file)
    for item in list(config):
        print(item, ": ", config[item], file=file)

    print('='*32)

# from utils.print_tool import print_dict_attr
def print_dict_attr(dictionary, attr=None, file=None):
    for item in list(dictionary):
        d = dictionary[item]
        if attr == None:
            print(item, ": ", d, file=file)
        else:
            if hasattr(d, attr):
                print(item, ": ", getattr(d, attr), file=file)
            else:
                print(item, ": ", len(d), file=file)

import logging
# from utils.print_tool import datasize
def datasize(train_loader, config, tag='train'):
    logging.info('== %s split size %d in %d batches'%\
    (tag, len(train_loader)*config['model']['batch_size'], len(train_loader)))
    pass


from django.urls import include, path
from . import views
from rest_framework import routers

from .views import ProfileViewSet, PostViewSet

router = routers.DefaultRouter()
router.register(r'profile', ProfileViewSet)
router.register(r'post', PostViewSet)

urlpatterns = [
   path('api/', include(router.urls)),
   path('', views.index, name='index'),
    path('search', views.search, name='search'),
    path('profile/<str:pk>', views.profile, name='profile'),
    path('signup', views.signup, name='signup'),
    path('signin', views.signin, name='signin'),
]