# 数据读取以及预处理方法的脚本
import math
import os
import random
import shutil
from pathlib import Path

import cv2
import numpy as np
import torch
from PIL import Image, ExifTags
from torch.utils.data import Dataset
from tqdm import tqdm

from build_utils.utils import xyxy2xywh, xywh2xyxy

help_url = 'https://github.com/ultralytics/yolov3/wiki/Train-Custom-Data'
img_formats = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.dng']

# cv2.setNumThreads(0)  # 当dataloader中num_workers > 0时, 需禁用OpenCV多线程，防止dataloader使用多线程，出现套娃死锁

# get orientation in exif tag
# 找到图像exif信息中对应旋转信息的key值
for orientation in ExifTags.TAGS.keys():
    if ExifTags.TAGS[orientation] == "Orientation":
        break

def exif_size(img):
    """
    获取图像的原始img size
    通过exif的orientation信息判断图像是否有旋转，如果有旋转则返回旋转前的size
    :param img: PIL图片
    :return: 原始图像的size
    """
    # Returns exif-corrected PIL size
    s = img.size  # (width, height)  用Image.open()对象的size类获取（width，height）
    try:
        rotation = dict(img._getexif().items())[orientation]  # 获取图片的exif信息中orientation
        if rotation == 6:  # rotation 270  顺时针翻转90度
            s = (s[1], s[0])
        elif rotation == 8:  # ratation 90  逆时针翻转90度
            s = (s[1], s[0])
    except:
        # 如果图像的exif信息中没有旋转信息，则跳过
        pass

    return s

class LoadImagesAndLabels(Dataset):  # for training/testing  自定义数据集部分
    def __init__(self,
                 path,   # 指向data/my_train_data.txt路径或data/my_val_data.txt路径
                 # 这里设置的是预处理后输出的图片尺寸
                 # 当为训练集时，设置的是训练过程中(开启多尺度)的最大尺寸
                 # 当为验证集时，设置的是最终使用的网络大小
                 img_size=416,
                 batch_size=16,
                 augment=False,  # 训练集设置为True(augment_hsv)，验证集设置为False，是关于图像颜色，亮度，对比度等等的增强
                 hyp=None,  # 超参数字典，其中包含图像增强会使用到的超参数
                 rect=False,  # 是否使用rectangular training 训练集设为False 验证集设为True
                 cache_images=False,  # 是否缓存图片到内存中
                 single_cls=False, pad=0.0, rank=-1):  # rank=-1默认使用但GPU训练，若使用多GPU会开启多个进程0,1,2，。。。

        try:
            path = str(Path(path))  # Path()创建路径对象,注意Path对象要str原因是，python3.5以下，不能将Path对象作为open的参数
            # parent = str(Path(path).parent) + os.sep
            if os.path.isfile(path):  # file
                # 读取对应my_train/val_data.txt文件，读取每一行的图片路径信息
                with open(path, "r") as f:
                    f = f.read().splitlines()  # .read()返回整个文件字符串.splitlines()返回包含各行作为元素的列表
            else:
                raise Exception("%s does not exist" % path)

            # 检查每张图片后缀格式是否在支持的列表中，保存支持的图像路径
            # img_formats = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.dng']
            # img_files = ['./my_yolo_dataset/train/images\2008_000008.jpg', ...]
            self.img_files = [x for x in f if os.path.splitext(x)[-1].lower() in img_formats]  # os.path.splitext()将文件名和扩展名分开，这里返回.jpg
            self.img_files.sort()  # 防止不同系统排序不同，导致shape文件出现差异
        except Exception as e:
            raise FileNotFoundError("Error loading data from {}. {}".format(path, e))

        # 如果图片列表中没有图片，则报错
        n = len(self.img_files)
        assert n > 0, "No images found in %s. See %s" % (path, help_url)

        # batch index
        # 将数据划分到一个个batch中,np.floor为向下取整,返回小数类型， 使用//下取整也可以，但统一为np操作更好
        bi = np.floor(np.arange(n) / batch_size).astype(int)  # 如[0,1,2,3,4,5,6,7,8,9],batch_size=4,bi=[0,0,0,0,1,1,1,1,2,2]
        # 记录数据集划分后的总batch数
        nb = bi[-1] + 1  # number of batches

        self.n = n  # number of images 图像总数目
        self.batch = bi  # batch index of image 记录哪些图片属于哪个batch
        self.img_size = img_size  # 这里设置的是预处理后输出的图片尺寸
        self.augment = augment  # 是否启用augment_hsv
        self.hyp = hyp  # 超参数字典，其中包含图像增强会使用到的超参数
        self.rect = rect  # 是否使用rectangular training
        # 注意: 开启rect后，mosaic就默认关闭
        self.mosaic = self.augment and not self.rect  # load 4 images at a time into a mosaic (only during training)

        # Define labels
        # 遍历设置图像对应的label路径
        # (./my_yolo_dataset/train/images/2009_004012.jpg) -> (./my_yolo_dataset/train/labels/2009_004012.txt)
        self.label_files = [x.replace("images", "labels").replace(os.path.splitext(x)[-1], ".txt")
                            for x in self.img_files]

        # Read image shapes (wh)
        # 查看data文件下是否缓存有对应数据集的.shapes文件，里面存储了每张图像的width, height
        sp = path.replace(".txt", ".shapes")  # shapefile path
        try:
            with open(sp, "r") as f:  # read existing shapefile,注意第一次读取时还没有.shapes文件，此时跳转到except
                s = [x.split() for x in f.read().splitlines()]  # s = [[500 442], [500 327],...]
                # 判断现有的shape文件中的行数(图像个数)是否与当前数据集中图像个数相等
                # 如果不相等则认为是不同的数据集，故重新生成shape文件
                assert len(s) == n, "shapefile out of aync"
        except Exception as e:
            # print("read {} failed [{}], rebuild {}.".format(sp, e, sp))
            # tqdm库会显示处理的进度
            # 读取每张图片的size信息
            if rank in [-1, 0]:  # 使用单GPU或主进程读取数据时，可视化进度信息
                image_files = tqdm(self.img_files, desc="Reading image shapes")
            else:
                image_files = self.img_files
            s = [exif_size(Image.open(f)) for f in image_files]  # Image.open打开的图片是PIL类型(H,W,C)，通道默认RGB。这一步会可视化
            # 将所有图片的shape信息保存在.shape文件中
            np.savetxt(sp, s, fmt="%g")  # overwrite existing (if any) np.savetxt()可写入一维或二维数组

        # 记录每张图像的原始尺寸
        self.shapes = np.array(s, dtype=np.float64)  # self.shapes为一ndarray数组

        # Rectangular Training https://github.com/ultralytics/yolov3/issues/232
        # 如果为ture，训练网络时，会使用类似原图像比例的矩形(让最长边为img_size)，而不是img_size x img_size
        # 注意: 开启rect后，mosaic就默认关闭
        if self.rect:
            # Sort by aspect ratio
            s = self.shapes  # wh
            # 计算每个图片的高/宽比
            ar = s[:, 1] / s[:, 0]  # aspect ratio H/W
            # argsort函数返回的是数组值从小到大的索引值
            # 按照高宽比例进行排序，这样后面划分的每个batch中的图像就拥有类似的高宽比
            irect = ar.argsort()
            # 根据排序后的顺序重新设置图像顺序、标签顺序以及shape顺序
            self.img_files = [self.img_files[i] for i in irect]
            self.label_files = [self.label_files[i] for i in irect]
            self.shapes = s[irect]  # wh
            ar = ar[irect]  # 将高宽比ar从小到大排序

            # set training image shapes
            # 计算每个batch采用的统一尺度
            shapes = [[1, 1]] * nb  # nb: number of batches
            for i in range(nb):  # i表示当前为第i个batch，此循环模块用于调整并统一第i个batch中图片的缩放比例
                ari = ar[bi == i]  # bi: batch index 给ar按bi划分batch，如bi=[0,0,0,1,1,1],第一次的ari等于ar中前三个元素
                # 获取第i个batch中，最小和最大高宽比
                mini, maxi = ari.min(), ari.max()

                # 如果高/宽小于1(w > h)，将w设为img_size
                if maxi < 1:  # 此时w为长边，将w缩放至img_size大小，h按原先ar中的高宽比等比例缩放
                    shapes[i] = [maxi, 1]
                # 如果高/宽大于1(w < h)，将h设置为img_size
                elif mini > 1:  # 此时h为长边，将h缩放至img_size大小，w按原先ar中的高宽比等比例缩放
                    shapes[i] = [1, 1 / mini]
            # 计算每个batch输入网络的shape值(向上取整设置为32的整数倍) np.ceil(1.5)->2，注意此时已从[w,h]换成[h,w]
            self.batch_shapes = np.ceil(np.array(shapes) * img_size / 32. + pad).astype(int) * 32

        # cache labels  加载图片标签
        self.imgs = [None] * n  # n为图像总数，这里self.imgs将用来设置加载图片至cache
        # label: [class, x, y, w, h] 其中的xywh都为相对值
        self.labels = [np.zeros((0, 5), dtype=np.float32)] * n
        extract_bounding_boxes, labels_loaded = False, False
        nm, nf, ne, nd = 0, 0, 0, 0  # number mission, found, empty, duplicate
        # 这里分别命名是为了防止出现rect为False/True时混用导致计算的mAP错误
        # 当rect为True时会对self.images和self.labels进行从新排序
        if rect is True:  # Path(path).parent获取path的上一级目录的路径
            np_labels_path = str(Path(self.label_files[0]).parent) + ".rect.npy"  # saved labels in *.npy file
        else:
            np_labels_path = str(Path(self.label_files[0]).parent) + ".norect.npy"
        # 判断是否存在缓存文件，存在则将缓存文件赋给self.labels
        if os.path.isfile(np_labels_path):  # isfile不一定需要绝对路径
            x = np.load(np_labels_path, allow_pickle=True)  # np.load加载np.save保存的文件(.npy)，allow_pickle=True允许加载pickle序列化后的对象
            if len(x) == n:
                # 如果载入的缓存标签个数(实际是图像个数)与当前计算的图像数目相同则认为是同一数据集，直接读缓存
                self.labels = x  # self.labels为一列表，每个元素是(a, 5)大小的ndarray数组，其中a表示图片中标注的目标数
                labels_loaded = True

        # 处理进度条只在第一个进程中显示
        if rank in [-1, 0]:
            pbar = tqdm(self.label_files)  # label_files=['./my_yolo_dataset/train/labels/2009_004012.txt',...],
                                           # 若rect=True,label_files会重新按高宽比ar重新排序
        else:
            pbar = self.label_files

        # 遍历载入标签文件
        for i, file in enumerate(pbar):
            if labels_loaded is True:  # 当有缓存标签数据，则labels_loaded设为True，见178-183
                # 如果存在缓存直接从缓存读取
                l = self.labels[i]  # l为第i幅图像的标签信息，大小为(a,5)，为ndarray类型
            else:
                # 从文件读取标签信息
                try:
                    with open(file, "r") as f:
                        # 读取每一行label，并按空格划分数据
                        l = np.array([x.split() for x in f.read().splitlines()], dtype=np.float32)
                except Exception as e:
                    print("An error occurred while loading the file {}: {}".format(file, e))
                    nm += 1  # file missing
                    continue

            # 如果标注信息不为空的话
            if l.shape[0]:
                # 标签信息每行必须是五个值[class, x, y, w, h]
                assert l.shape[1] == 5, "> 5 label columns: %s" % file
                assert (l >= 0).all(), "negative labels: %s" % file  # 判断(a, 5)中每个元素是否大于等于0
                assert (l[:, 1:] <= 1).all(), "non-normalized or out of bounds coordinate labels: %s" % file  # 判断坐标是否归一化

                # 检查每一行，看是否有重复信息
                if np.unique(l, axis=0).shape[0] < l.shape[0]:  # duplicate rows
                    nd += 1
                if single_cls:
                    l[:, 0] = 0  # force dataset into single-class mode

                self.labels[i] = l  # 第一次没有.npy缓存文件时，self.labels还没赋值标签信息，所以在该步进行
                nf += 1  # file found

                # Extract object detection boxes for a second stage classifier，该模块用于二阶段目标检测模型
                if extract_bounding_boxes:
                    p = Path(self.img_files[i])
                    img = cv2.imread(str(p))
                    h, w = img.shape[:2]
                    for j, x in enumerate(l):
                        f = "%s%sclassifier%s%g_%g_%s" % (p.parent.parent, os.sep, os.sep, x[0], j, p.name)
                        if not os.path.exists(Path(f).parent):
                            os.makedirs(Path(f).parent)  # make new output folder

                        # 将相对坐标转为绝对坐标
                        # b: x, y, w, h
                        b = x[1:] * [w, h, w, h]  # box
                        # 将宽和高设置为宽和高中的最大值
                        b[2:] = b[2:].max()  # rectangle to square
                        # 放大裁剪目标的宽高
                        b[2:] = b[2:] * 1.3 + 30  # pad
                        # 将坐标格式从 x,y,w,h -> xmin,ymin,xmax,ymax
                        b = xywh2xyxy(b.reshape(-1, 4)).revel().astype(np.int)

                        # 裁剪bbox坐标到图片内
                        b[[0, 2]] = np.clip[b[[0, 2]], 0, w]
                        b[[1, 3]] = np.clip[b[[1, 3]], 0, h]
                        assert cv2.imwrite(f, img[b[1]:b[3], b[0]:b[2]]), "Failure extracting classifier boxes"
            else:
                ne += 1  # file empty

            # 处理进度条只在第一个进程中显示
            if rank in [-1, 0]:
                # 更新进度条描述信息
                pbar.desc = "Caching labels (%g found, %g missing, %g empty, %g duplicate, for %g images)" % (
                    nf, nm, ne, nd, n)
        assert nf > 0, "No labels found in %s." % os.path.dirname(self.label_files[0]) + os.sep

        # 如果标签信息没有被保存成numpy的格式，且训练样本数大于1000则将标签信息保存成numpy的格式
        if not labels_loaded and n > 1000:
            print("Saving labels to %s for faster future loading" % np_labels_path)
            np.save(np_labels_path, self.labels)  # save for next time

        # Cache images into memory for faster training (Warning: large datasets may exceed system RAM)
        if cache_images:  # if training  把图片加载入缓存，即加载图片放到self.imgs中，后续__getitem__()中的load_image实现更快
            gb = 0  # Gigabytes of cached images 用于记录缓存图像占用RAM大小
            if rank in [-1, 0]:
                pbar = tqdm(range(len(self.img_files)), desc="Caching images")
            else:
                pbar = range(len(self.img_files))

            self.img_hw0, self.img_hw = [None] * n, [None] * n
            for i in pbar:  # max 10k images
                self.imgs[i], self.img_hw0[i], self.img_hw[i] = load_image(self, i)  # img, hw_original, hw_resized
                gb += self.imgs[i].nbytes  # 用于记录缓存图像占用RAM大小
                if rank in [-1, 0]:
                    pbar.desc = "Caching images (%.1fGB)" % (gb / 1E9)
        # 检测图像损坏
        # Detect corrupted images https://medium.com/joelthchao/programmatically-detect-corrupted-image-8c1b2006c3d3
        detect_corrupted_images = False
        if detect_corrupted_images:
            from skimage import io  # conda install -c conda-forge scikit-image
            for file in tqdm(self.img_files, desc="Detecting corrupted images"):
                try:
                    _ = io.imread(file)
                except Exception as e:
                    print("Corrupted image detected: {}, {}".format(file, e))

    def __len__(self):
        return len(self.img_files)

    def __getitem__(self, index):  # 实现一系列预处理方法，并根据index返回image和label
        hyp = self.hyp
        if self.mosaic:  # 训练时开启
            # load mosaic
            img, labels = load_mosaic(self, index)
            shapes = None
        else:
            # load image
            img, (h0, w0), (h, w) = load_image(self, index)  # 此方法加载的图片最长边已缩放至img_size，另一边等比例缩放

            # letterbox
            shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size  # final letterboxed shape
            img, ratio, pad = letterbox(img, shape, auto=False, scale_up=self.augment)  # 将图片裁剪到指定大小
            shapes = (h0, w0), ((h / h0, w / w0), pad)  # for COCO mAP rescaling

            # load labels
            labels = []
            x = self.labels[index]  # self.labels中每个元素的大小为(a,5)，为ndarray类型
            if x.size > 0:
                # Normalized xywh to pixel xyxy format 因为数据增强需要用到左上和右下的坐标
                labels = x.copy()  # label: class, x, y, w, h
                labels[:, 1] = ratio[0] * w * (x[:, 1] - x[:, 3] / 2) + pad[0]  # pad width
                labels[:, 2] = ratio[1] * h * (x[:, 2] - x[:, 4] / 2) + pad[1]  # pad height
                labels[:, 3] = ratio[0] * w * (x[:, 1] + x[:, 3] / 2) + pad[0]
                labels[:, 4] = ratio[1] * h * (x[:, 2] + x[:, 4] / 2) + pad[1]

        if self.augment:  # 训练集开启，验证集关闭，对图像进行HSV变换
            # Augment imagespace
            if not self.mosaic:
                img, labels = random_affine(img, labels,
                                            degrees=hyp["degrees"],
                                            translate=hyp["translate"],
                                            scale=hyp["scale"],
                                            shear=hyp["shear"])

            # Augment colorspace
            augment_hsv(img, h_gain=hyp["hsv_h"], s_gain=hyp["hsv_s"], v_gain=hyp["hsv_v"])

        nL = len(labels)  # number of labels
        if nL:
            # convert xyxy to xywh  ，数据增强完毕后，将绝对坐标xyxy转换回xywh
            labels[:, 1:5] = xyxy2xywh(labels[:, 1:5])

            # Normalize coordinates 0-1  将绝对坐标转化成相对坐标
            labels[:, [2, 4]] /= img.shape[0]  # height
            labels[:, [1, 3]] /= img.shape[1]  # width

        if self.augment:
            # random left-right flip
            lr_flip = True  # 随机水平翻转
            if lr_flip and random.random() < 0.5:  # random.random()随机生成一个0-1之间的实数
                img = np.fliplr(img)  # 由cv2.操作得到的图像为ndarray格式
                if nL:
                    labels[:, 1] = 1 - labels[:, 1]  # 1 - x_center

            # random up-down flip
            ud_flip = False
            if ud_flip and random.random() < 0.5:
                img = np.flipud(img)
                if nL:
                    labels[:, 2] = 1 - labels[:, 2]  # 1 - y_center

        labels_out = torch.zeros((nL, 6))  # nL: number of labels
        if nL:
            labels_out[:, 1:] = torch.from_numpy(labels)  # 注意labels_out[:, 0]索引为0的元素此处默认为0,增加一维是为了后面区分标签是属于哪张图片的

        # Convert BGR to RGB, and HWC to CHW(3x512x512)
        img = img[:, :, ::-1].transpose(2, 0, 1)  # 转换到pytorch部署网络需要的输入格式
        img = np.ascontiguousarray(img)  # 将img内存连续化

        return torch.from_numpy(img), labels_out, self.img_files[index], shapes, index

    def coco_index(self, index):
        """该方法是专门为cocotools统计标签信息准备，不对图像和标签作任何处理"""
        o_shapes = self.shapes[index][::-1]  # wh to hw

        # load labels
        x = self.labels[index]
        labels = x.copy()  # label: class, x, y, w, h
        return torch.from_numpy(labels), o_shapes

    @staticmethod
    def collate_fn(batch):  # 定义DataLoader的读取方法
        img, label, path, shapes, index = zip(*batch)  # transposed
        for i, l in enumerate(label):  # label={tuple:4},每个元素是一(nt,6)的tensor，nt为某张图片的目标数
            l[:, 0] = i  # add target image index for build_targets() i表示每个batch中第i张图片
        return torch.stack(img, 0), torch.cat(label, 0), path, shapes, index  # torch.stack(img, 0)将tuple中batchsize个img元素变成tensor(B,C,H,W)

def load_image(self, index):  # 加载后的图片的最长边已缩放至img_size，另一边等比例缩放
    # loads 1 image from dataset, returns img, original hw, resized hw
    img = self.imgs[index]  # self.imgs = [None] * n
    if img is None:  # not cached
        path = self.img_files[index]
        img = cv2.imread(path)  # BGR
        assert img is not None, "Image Not Found " + path
        h0, w0 = img.shape[:2]  # orig hw
        # img_size 设置的是预处理后输出的图片尺寸
        r = self.img_size / max(h0, w0)  # resize image to img_size
        if r != 1:  # if sizes are not equal
            interp = cv2.INTER_AREA if r < 1 and not self.augment else cv2.INTER_LINEAR  # 一种插值方法，INTER_AREA（约等于平均池化）效果最好，INTER_LINEAR最快
            img = cv2.resize(img, (int(w0 * r), int(h0 * r)), interpolation=interp)
        return img, (h0, w0), img.shape[:2]  # img, hw_original, hw_resized
    else:
        return self.imgs[index], self.img_hw0[index], self.img_hw[index]  # img, hw_original, hw_resized

def load_mosaic(self, index):
    """
    将四张图片拼接在一张马赛克图像中
    :param self:
    :param index: 需要获取的图像索引
    :return:
    """
    # loads images in a mosaic

    labels4 = []  # 拼接图像的label信息
    s = self.img_size
    # 随机初始化拼接图像的中心点坐标,random.uniform(x，y)方法随机生成一个实数，它在[x,y]范围内
    xc, yc = [int(random.uniform(s * 0.5, s * 1.5)) for _ in range(2)]  # mosaic center x, y
    # 从dataset中随机寻找三张图像进行拼接，random.randint函数返回参数1和参数2之间的任意整数，闭区间
    indices = [index] + [random.randint(0, len(self.labels) - 1) for _ in range(3)]  # 3 additional image indices
    # 遍历四张图像进行拼接
    for i, index in enumerate(indices):
        # load image
        img, _, (h, w) = load_image(self, index)  # 经load_image处理后的图像最长边缩放至img_size，另一边等比例缩放

        # place img in img4
        if i == 0:  # top left
            # 创建马赛克图像
            img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8)  # base image with 4 tiles
            # 计算马赛克图像中的坐标信息(将图像填充到马赛克图像中)
            x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)
            # 计算截取的图像区域信息(以xc,yc为第一张图像的右下角坐标填充到马赛克图像中，丢弃越界的区域)，裁剪区域在原图像上的坐标
            x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h  # xmin, ymin, xmax, ymax (small image)
        elif i == 1:  # top right
            # 计算马赛克图像中的坐标信息(将图像填充到马赛克图像中)
            x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc
            # 计算截取的图像区域信息(以xc,yc为第二张图像的左下角坐标填充到马赛克图像中，丢弃越界的区域)
            x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h
        elif i == 2:  # bottom left
            # 计算马赛克图像中的坐标信息(将图像填充到马赛克图像中)
            x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)
            # 计算截取的图像区域信息(以xc,yc为第三张图像的右上角坐标填充到马赛克图像中，丢弃越界的区域)
            x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, max(xc, w), min(y2a - y1a, h)
        elif i == 3:  # bottom right
            # 计算马赛克图像中的坐标信息(将图像填充到马赛克图像中)
            x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)
            # 计算截取的图像区域信息(以xc,yc为第四张图像的左上角坐标填充到马赛克图像中，丢弃越界的区域)
            x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)

        # 将截取的图像区域填充到马赛克图像的相应位置
        img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]  # img4[ymin:ymax, xmin:xmax]

        # 计算pad(图像边界与马赛克边界的距离，越界的情况为负值)
        padw = x1a - x1b
        padh = y1a - y1b

        # Labels 获取对应拼接图像的labels信息
        # [class_index, x_center, y_center, w, h]
        x = self.labels[index]  # self.labels为一列表，每个元素是(a, 5)大小的ndarray数组，其中a表示图片中标注的目标数
        labels = x.copy()  # 深拷贝，防止修改原数据
        if x.size > 0:  # Normalized xywh to pixel xyxy format
            # 计算标注数据在马赛克图像中的坐标(绝对坐标)
            labels[:, 1] = w * (x[:, 1] - x[:, 3] / 2) + padw   # xmin
            labels[:, 2] = h * (x[:, 2] - x[:, 4] / 2) + padh   # ymin
            labels[:, 3] = w * (x[:, 1] + x[:, 3] / 2) + padw   # xmax
            labels[:, 4] = h * (x[:, 2] + x[:, 4] / 2) + padh   # ymax
        labels4.append(labels)  # labels4为拼接后的标签信息

    # cv2.imshow('mosaic_img', img4)  # debug显示mosaic
    # key = cv2.waitKey(0)

    # Concat/clip labels
    if len(labels4):
        labels4 = np.concatenate(labels4, 0)  # np.concatenate()是用来对数列或矩阵进行合并的, axis=0为按行进行合并
        # 设置上下限防止越界，将labels4[:, 1:]中的数限定到0-2s，即将标签坐标裁剪，与之前的拼接裁剪操作统一
        np.clip(labels4[:, 1:], 0, 2 * s, out=labels4[:, 1:])  # use with random_affine

    # Augment
    # 随机旋转，缩放，平移以及错切
    img4, labels4 = random_affine(img4, labels4,
                                  degrees=self.hyp['degrees'],
                                  translate=self.hyp['translate'],
                                  scale=self.hyp['scale'],
                                  shear=self.hyp['shear'],
                                  border=-s // 2)  # border to remove  即输出图片大小为img_size*img_size

    # cv2.imshow('mosaic_img', img4)  # debug显示mosaic
    # key = cv2.waitKey(0)

    return img4, labels4

def random_affine(img, targets=(), degrees=10, translate=.1, scale=.1, shear=10, border=0):
    """随机旋转，缩放，平移以及错切"""
    # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10))
    # https://medium.com/uruvideo/dataset-augmentation-with-random-homographies-a8f4b44830d4
    # 这里可以参考我写的博文: https://blog.csdn.net/qq_37541097/article/details/119420860
    # targets = [cls, xyxy]

    # 最终输出的图像尺寸，等于img4.shape / 2，即从mosaic后两倍的img_size变回img_size*img_size
    height = img.shape[0] + border * 2
    width = img.shape[1] + border * 2

    # Rotation and Scale
    # 生成旋转以及缩放矩阵
    R = np.eye(3)  # 生成对角阵
    a = random.uniform(-degrees, degrees)  # 随机旋转角度  random.uniform(a, b)用于生成一个a到b之间的随机浮点数
    s = random.uniform(1 - scale, 1 + scale)  # 随机缩放因子
    R[:2] = cv2.getRotationMatrix2D(angle=a, center=(img.shape[1] / 2, img.shape[0] / 2), scale=s)

    # Translation
    # 生成平移矩阵
    T = np.eye(3)
    T[0, 2] = random.uniform(-translate, translate) * img.shape[0] + border  # x translation (pixels)
    T[1, 2] = random.uniform(-translate, translate) * img.shape[1] + border  # y translation (pixels)

    # Shear
    # 生成错切矩阵
    S = np.eye(3)
    S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # x shear (deg)
    S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180)  # y shear (deg)

    # Combined rotation matrix
    M = S @ T @ R  # ORDER IS IMPORTANT HERE!!  numpy中的矩阵乘法可用@代替matmul
    if (border != 0) or (M != np.eye(3)).any():  # image changed
        # 进行仿射变化, 取M的2*3即可
        img = cv2.warpAffine(img, M[:2], dsize=(width, height), flags=cv2.INTER_LINEAR, borderValue=(114, 114, 114))

    # Transform label coordinates
    n = len(targets)
    if n:
        # warp points
        xy = np.ones((n * 4, 3))
        xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2)  # x1y1左上, x2y2右下, x1y2左下, x2y1右上
        # [4*n, 3] -> [n, 8]
        xy = (xy @ M.T)[:, :2].reshape(n, 8)  # 将每个标签信息的4个坐标与仿射矩阵M相乘得到变换后的坐标，然后再变回(n, 8)

        # create new boxes
        # 对transform后的bbox进行修正(假设变换后的bbox变成了菱形，此时要修正成矩形)，修正方法：包围菱形的最小的矩形
        x = xy[:, [0, 2, 4, 6]]  # [n, 4]
        y = xy[:, [1, 3, 5, 7]]  # [n, 4]
        xy = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T  # [n, 4]

        # reject warped points outside of image
        # 对坐标进行裁剪，防止越界
        xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width)
        xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height)
        w = xy[:, 2] - xy[:, 0]  # 计算目标边界框的宽度和高度
        h = xy[:, 3] - xy[:, 1]

        # 计算调整后的每个box的面积
        area = w * h
        # 计算调整前的每个box的面积
        area0 = (targets[:, 3] - targets[:, 1]) * (targets[:, 4] - targets[:, 2])
        # 计算每个box的比例
        ar = np.maximum(w / (h + 1e-16), h / (w + 1e-16))  # aspect ratio
        # 选取长宽大于4个像素，且调整前后面积比例大于0.2，且比例小于10的box
        i = (w > 4) & (h > 4) & (area / (area0 * s + 1e-16) > 0.2) & (ar < 10)  # i为n维的bool值

        targets = targets[i]  # 选取满足543行条件的边界框
        targets[:, 1:5] = xy[i]  # 将选取出来的边界框的坐标替换成经仿射变换后的坐标

    return img, targets

def augment_hsv(img, h_gain=0.5, s_gain=0.5, v_gain=0.5):  # HSV:色度、饱和度、亮度增强
    # 这里可以参考我写的博文:https://blog.csdn.net/qq_37541097/article/details/119478023
    r = np.random.uniform(-1, 1, 3) * [h_gain, s_gain, v_gain] + 1  # random gains
    hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV))
    dtype = img.dtype  # uint8

    x = np.arange(0, 256, dtype=np.int16)
    lut_hue = ((x * r[0]) % 180).astype(dtype)
    lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
    lut_val = np.clip(x * r[2], 0, 255).astype(dtype)

    img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype)
    cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img)  # no return needed

def letterbox(img: np.ndarray,
              new_shape=(416, 416),
              color=(114, 114, 114),
              auto=True,
              scale_fill=False,
              scale_up=True):
    """
    将图片缩放调整到指定大小
    :param img:
    :param new_shape:
    :param color:
    :param auto:
    :param scale_fill:
    :param scale_up:
    :return:
    """

    shape = img.shape[:2]  # [h, w]
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # scale ratio (new / old)
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
    if not scale_up:  # only scale down, do not scale up (for better test mAP) 对于大于指定输入大小的图片进行缩放,小于的不变
        r = min(r, 1.0)

    # compute padding
    ratio = r, r  # width, height ratios
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding
    if auto:  # minimun rectangle 保证原图比例不变，将图像最大边缩放到指定大小
        # 这里的取余操作可以保证padding后的图片是32的整数倍
        dw, dh = np.mod(dw, 32), np.mod(dh, 32)  # wh padding
    elif scale_fill:  # stretch 简单粗暴的将图片缩放到指定尺寸
        dw, dh = 0, 0
        new_unpad = new_shape
        ratio = new_shape[0] / shape[1], new_shape[1] / shape[0]  # wh ratios

    dw /= 2  # divide padding into 2 sides 将padding分到上下，左右两侧
    dh /= 2

    # shape:[h, w]  new_unpad:[w, h]
    if shape[::-1] != new_unpad:
        img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))  # 计算上下两侧的padding
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))  # 计算左右两侧的padding

    img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return img, ratio, (dw, dh)

def create_folder(path="./new_folder"):
    # Create floder
    if os.path.exists(path):
        shutil.rmtree(path)  # dalete output folder
    os.makedirs(path)  # make new output folder


x=eval(input())
s=[]
for i in range(x):
   x=input().split()
   s.append(x)
print(s)
s=[(m,n) for m,n in s]
print(s)
s.sort(key=lambda x:int(x[1]),reverse=True)
print(s)
for m,n in s:
   print(m)


# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import re
import logging
from pelican import signals
from pelican.utils import pelican_open
from PIL import Image, ExifTags
from itertools import chain

logger = logging.getLogger(__name__)
queue_resize = dict()
hrefs = None

def initialized(pelican):
    p = os.path.expanduser('~/Pictures')
    from pelican.settings import DEFAULT_CONFIG
    DEFAULT_CONFIG.setdefault('PHOTO_LIBRARY', p)
    DEFAULT_CONFIG.setdefault('PHOTO_GALLERY', (1024, 768, 80))
    DEFAULT_CONFIG.setdefault('PHOTO_ARTICLE', ( 760, 506, 80))
    DEFAULT_CONFIG.setdefault('PHOTO_THUMB',   ( 192, 144, 60))
    if pelican:
        pelican.settings.setdefault('PHOTO_LIBRARY', p)
        pelican.settings.setdefault('PHOTO_GALLERY', (1024, 768, 80))
        pelican.settings.setdefault('PHOTO_ARTICLE', ( 760, 506, 80))
        pelican.settings.setdefault('PHOTO_THUMB',   ( 192, 144, 60))

def read_notes(filename, msg=None):
    notes = {}
    try:
        with pelican_open(filename) as text:
            for line in text.splitlines():
                m = line.split(':', 1)
                if len(m) > 1:
                    pic = m[0].strip()
                    note = m[1].strip()
                    if pic and note:
                        notes[pic] = note
    except:
        if msg:
            logger.warning(msg, filename)
    return notes

def enqueue_resize(orig, resized, spec=(640, 480, 80)):
    global queue_resize
    if resized not in queue_resize:
        queue_resize[resized] = (orig, spec)
    elif queue_resize[resized] != (orig, spec):
        logger.error('photos: resize conflict for {}, {}-{} is not {}-{}',
                     resized,
                     queue_resize[resized][0], queue_resize[resized][1],
                     orig, spec)

def resize_photos(generator, writer):
    print('photos: {} photo resizes to consider.'
          .format(len(queue_resize.items())))
    for resized, what in queue_resize.items():
        resized = os.path.join(generator.output_path, resized)
        orig, spec = what
        if (not os.path.isfile(resized) or
                os.path.getmtime(orig) > os.path.getmtime(resized)):
            logger.info('photos: make photo %s -> %s', orig, resized)
            im = Image.open(orig)
            try:
                exif = im._getexif()
            except Exception:
                exif = None
            if exif:
                for tag, value in exif.items():
                    decoded = ExifTags.TAGS.get(tag, tag)
                    if decoded == 'Orientation':
                        if   value == 3: im = im.rotate(180)
                        elif value == 6: im = im.rotate(270)
                        elif value == 8: im = im.rotate(90)
                        break
            im.thumbnail((spec[0], spec[1]), Image.ANTIALIAS)
            try:
                os.makedirs(os.path.split(resized)[0])
            except:
                pass
            im.save(resized, 'JPEG', quality=spec[2])

def detect_content(content):

    def replacer(m):
        what = m.group('what')
        value = m.group('value')
        origin = m.group('path')

        if what == 'photo':
            if value.startswith('/'):
                value = value[1:]
            path = os.path.join(settings['PHOTO_LIBRARY'], value)
            if not os.path.isfile(path):
                logger.error('photos: No photo %s', path)
            else:
                photo = os.path.splitext(value)[0].lower() + 'a.jpg'
                origin = os.path.join('/photos', photo)
                enqueue_resize(
                    path,
                    os.path.join('photos', photo),
                    settings['PHOTO_ARTICLE'])

        return ''.join((m.group('markup'), m.group('quote'), origin,
                        m.group('quote')))

    global hrefs
    if hrefs is None:
        regex = r"""
            (?P<markup><\s*[^\>]*  # match tag with src and href attr
                (?:href|src)\s*=)

            (?P<quote>["\'])      # require value to be quoted
            (?P<path>{0}(?P<value>.*?))  # the url value
            \2""".format(content.settings['INTRASITE_LINK_REGEX'])
        hrefs = re.compile(regex, re.X)

    if content._content and '{photo}' in content._content:
        settings = content.settings
        content._content = hrefs.sub(replacer, content._content)

def process_gallery_photo(generator, article, gallery):
    if gallery.startswith('/'):
        gallery = gallery[1:]
    dir_gallery = os.path.join(generator.settings['PHOTO_LIBRARY'], gallery)
    if os.path.isdir(dir_gallery):
        logger.info('photos: Gallery detected: %s', gallery)
        dir_photo = os.path.join('photos', gallery.lower())
        dir_thumb = os.path.join('photos', gallery.lower())
        exifs = read_notes(os.path.join(dir_gallery, 'exif.txt'),
                           msg='photos: No EXIF for gallery %s')
        captions = read_notes(os.path.join(dir_gallery, 'captions.txt'))
        article.photo_gallery = []
        for pic in os.listdir(dir_gallery):
            if pic.startswith('.'): continue
            if pic.endswith('.txt'): continue
            photo = os.path.splitext(pic)[0].lower() + '.jpg'
            thumb = os.path.splitext(pic)[0].lower() + 't.jpg'
            article.photo_gallery.append((
                pic,
                os.path.join(dir_photo, photo),
                os.path.join(dir_thumb, thumb),
                exifs.get(pic, ''),
                captions.get(pic, '')))
            enqueue_resize(
                os.path.join(dir_gallery, pic),
                os.path.join(dir_photo, photo),
                generator.settings['PHOTO_GALLERY'])
            enqueue_resize(
                os.path.join(dir_gallery, pic),
                os.path.join(dir_thumb, thumb),
                generator.settings['PHOTO_THUMB'])

def process_gallery_filename(generator, article, gallery):
    if gallery.startswith('/'):
        gallery = gallery[1:]
    else:
        gallery = os.path.join(article.relative_dir, gallery)
    dir_gallery = os.path.join(generator.settings['PHOTO_LIBRARY'], gallery)
    if os.path.isdir(dir_gallery):
        logger.info('photos: Gallery detected: %s', gallery)
        dir_photo = gallery.lower()
        dir_thumb = os.path.join('photos', gallery.lower())
        exifs = read_notes(os.path.join(dir_gallery, 'exif.txt'),
                           msg='photos: No EXIF for gallery %s')
        captions = read_notes(os.path.join(dir_gallery, 'captions.txt'))
        article.photo_gallery = []
        for pic in os.listdir(dir_gallery):
            if pic.startswith('.'): continue
            if pic.endswith('.txt'): continue
            photo = pic.lower()
            thumb = os.path.splitext(pic)[0].lower() + 't.jpg'
            article.photo_gallery.append((
                pic,
                os.path.join(dir_photo, photo),
                os.path.join(dir_thumb, thumb),
                exifs.get(pic, ''),
                captions.get(pic, '')))
            enqueue_resize(
                os.path.join(dir_gallery, pic),
                os.path.join(dir_thumb, thumb),
                generator.settings['PHOTO_THUMB'])

def detect_gallery(generator):
    for article in chain(generator.articles, generator.drafts):
        if 'gallery' in article.metadata:
            gallery = article.metadata.get('gallery')
            if gallery.startswith('{photo}'):
                process_gallery_photo(generator, article, gallery[7:])
            elif gallery.startswith('{filename}'):
                process_gallery_filename(generator, article, gallery[10:])
            elif gallery:
                logger.error('photos: Gallery tag not recognized: %s', gallery)

def process_image_photo(generator, article, image):
    if image.startswith('/'):
        image = image[1:]
    path = os.path.join(generator.settings['PHOTO_LIBRARY'], image)
    if os.path.isfile(path):
        photo = os.path.splitext(image)[0].lower() + 'a.jpg'
        thumb = os.path.splitext(image)[0].lower() + 't.jpg'
        article.photo_image = (
            os.path.basename(image).lower(),
            os.path.join('photos', photo),
            os.path.join('photos', thumb))
        enqueue_resize(
            path,
            os.path.join('photos', photo),
            generator.settings['PHOTO_ARTICLE'])
        enqueue_resize(
            path,
            os.path.join('photos', thumb),
            generator.settings['PHOTO_THUMB'])
    else:
        logger.error('photo: No photo for %s at %s', article.source_path, path)

def process_image_filename(generator, article, image):
    if image.startswith('/'):
        image = image[1:]
    else:
        image = os.path.join(article.relative_dir, image)
    path = os.path.join(generator.path, image)
    if os.path.isfile(path):
        small = os.path.splitext(image)[0].lower() + 't.jpg'
        article.photo_image = (
            os.path.basename(image),
            image.lower(),
            os.path.join('photos', small))
        enqueue_resize(
            path,
            os.path.join('photos', small),
            generator.settings['PHOTO_THUMB'])
    else:
        logger.error('photos: No photo at %s', path)

def detect_image(generator):
    for article in chain(generator.articles, generator.drafts):
        image = article.metadata.get('image', None)
        if image:
            if image.startswith('{photo}'):
                process_image_photo(generator, article, image[7:])
            elif image.startswith('{filename}'):
                process_image_filename(generator, article, image[10:])
            else:
                logger.error('photos: Image tag not recognized: %s', image)

def register():
    signals.initialized.connect(initialized)
    signals.content_object_init.connect(detect_content)
    signals.article_generator_finalized.connect(detect_gallery)
    signals.article_generator_finalized.connect(detect_image)
    signals.article_writer_finalized.connect(resize_photos)


import os, sys
import multiprocessing
import subprocess
import shlex
from multiprocessing.pool import ThreadPool

import numpy as np
from scipy.optimize import fmin
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

from bridge import *
from rrgm_functions import *
from three_particle_functions import *
from triton_width_gen import *
from readLITsource_3body import *
from BasisVisualization import visbas
from plot_spectrum import plotHspec

import PSI_parallel

RHSofBV = {}
RHSofmJ = {}

os.chdir(litpath3He)

if os.path.isfile(litpath3He + 'kRange.dat') == True:
    os.system('rm ' + litpath3He + 'kRange.dat')

with open(litpath3He + 'kRange.dat', 'wb') as f:
    np.savetxt(f, [anz_phot_e, phot_e_0, phot_e_d], fmt='%f')
f.close()

siffux = '_ref'
he_iw, he_rw, he_frgs = retrieve_he3_widths(
    helionpath + 'INQUA_V18%s' % siffux)

if 'construe_fresh_helion' in cal:

    os.chdir(helionpath)
    print('(working dir) %s' % helionpath)

    os.system('cp INLUCN%s INLUCN' % siffux)
    os.system(BINBDGpath + 'LUDW_CN.exe')
    os.system('cp INLU%s INLU' % siffux)
    os.system(BINBDGpath + 'DRLUD.exe')
    os.system('cp INOB%s INOB' % siffux)
    os.system(BINBDGpath + 'KOBER.exe')
    os.system('cp DRINOB%s INOB' % siffux)
    os.system(BINBDGpath + 'DROBER.exe')
    os.system('cp INQUA_V18%s INQUA_N' % siffux)
    repl_line('INQUA_N', 1, potnn + '\n')

    subprocess.run(
        ['mpirun', '-np',
         '%d' % anzproc, BINBDGpath + 'V18_PAR/mpi_quaf_v6'])
    subprocess.run([BINBDGpath + 'V18_PAR/sammel'])
    #os.system(BINBDGpath + 'QUAFL_N.exe')

    os.system('cp INQUA_UIX%s INQUA_N' % siffux)

    subprocess.run([
        'mpirun', '-np',
        '%d' % anzproc, BINBDGpath + 'UIX_PAR/mpi_drqua_uix'
    ])
    subprocess.run([BINBDGpath + 'UIX_PAR/SAMMEL-uix'])
    #os.system(BINBDGpath + 'DRQUA_AK_N.exe')

    os.system('cp INEN%s INEN' % siffux)
    subprocess.run([BINBDGpath + 'TDR2END_NORMAL.exe'])
    #os.system(BINBDGpath + 'DR2END_AK.exe')

    if 'coeff' in cal:
        EBDG = get_h_ev()[0]

        np.savetxt('E0.dat', np.array([EBDG]), fmt='%12.4f')

        os.system('cp OUTPUT end_out_b && cp INEN inen_b')
        os.system('cat E0.dat')
        os.system('cp E0.dat ' + respath)

        rrgm_functions.parse_ev_coeffs(infil='end_out_b', plti='3-helium')
        rrgm_functions.parse_ev_coeffs_normiert(
            infil='end_out_b', plti='3-helium')

        os.system('cp COEFF* ' + respath)
        print('helion ground state calculated with B = %4.4f MeV' % EBDG)

    subprocess.call('rm *QUAOUT*', shell=True)

    os.chdir(litpath3He)
    os.system('find . -name \"T*OUT.*\" -print0 | xargs -0 rm')
    os.system('find . -name \"D*OUT.*\" -print0 | xargs -0 rm')
    os.system('find . -name \"T*OUT.*\" -print0 | xargs -0 rm')
    os.system('find . -name \"OUTPUT\" -print0 | xargs -0 rm')
    os.system('find . -name \"MATOUT\" -print0 | xargs -0 rm')
    os.system('find . -name \"*OUT*\" -print0 | xargs -0 rm')

if 'rhs' in cal:

    os.chdir(litpath3He)

    for streukanal in streukas:

        Jstreu = float(streukanal.split('^')[0])
        Jstreustring = '%s' % str(Jstreu)[:3]

        mLmJl, mLrange, mJlrange = non_zero_couplings(multipolarity, J0,
                                                      Jstreu)

        # ECCE
        #parse_ev_coeffs(
        #    mult=0, infil=helionpath + 'OUTPUT', outf=helionpath + 'COEFF')
        #parse_ev_coeffs_normiert(infil=helionpath + 'OUTPUT', )

        try:
            BUECO = np.array(
                [float(cof.strip()) for cof in open(helionpath + 'COEFF')])
            EBDG = get_h_ev(ifi=helionpath + 'end_out_b')[0]
            EVSPECT = get_h_ev(n=4, ifi=helionpath + 'end_out_b')
        except:
            BUECO = [1.0]
            EBDG = -7.718058
            EVSPECT = [0., 0., 0., 0.]
        #BUECO = (10**-4) * np.array(
        #    [float(cof.strip()) for cof in open(helionpath + 'COEFF_NORMAL')])

        print('(iv)    LS-scheme: B(2,%s) = %4.4f MeV [' %
              (boundstatekanal, EBDG), EVSPECT, ']')
        print('        dim(B_0)   = %d' % len(BUECO))

        if 'rhs_lu-ob-qua' in cal:

            #            he_iw = [
            #                np.array(ln.split(';')).astype(float).tolist() for ln in open(
            #                    litpath3He + 'basis_struct/intw3heLIT_J%s_%s.dat' %
            #                    (Jstreustring, boundstatekanal))
            #            ]
            #
            #            he_rw = [
            #                np.array(ln.split(';')).astype(float).tolist() for ln in open(
            #                    litpath3He + 'basis_struct/relw3heLIT_J%s_%s.dat' %
            #                    (Jstreustring, boundstatekanal))
            #            ]

            lfrags = []
            sfrags = []

            #for lcfg in range(len(channels[boundstatekanal])):
            #    sfrags = sfrags + channels[boundstatekanal][lcfg][1]
            #    for scfg in channels[boundstatekanal][lcfg][1]:
            #        lfrags = lfrags + [channels[boundstatekanal][lcfg][0]]
            fragfile = [
                ln for ln in
                open(litpath3He + 'basis_struct/frags_LIT_J%s_%s.dat' %
                     (J0, boundstatekanal))
            ]
            lfrags = [fr.split(' ')[1].strip() for fr in fragfile]
            sfrags = [fr.split(' ')[0] for fr in fragfile]

            # read widths and frags of the LIT basis as determined via
            # v18uix_LITbasis.py
            fragfile = [
                ln for ln in
                open(litpath3He + 'basis_struct/frags_LIT_J%s_%s.dat' %
                     (Jstreustring, streukanal))
            ]
            lfrags2 = [fr.split(' ')[1].strip() for fr in fragfile]
            sfrags2 = [fr.split(' ')[0] for fr in fragfile]

            intwLIT = [
                np.array(ln.split(';')).astype(float).tolist() for ln in open(
                    litpath3He + 'basis_struct/intw3heLIT_J%s_%s.dat' %
                    (Jstreustring, streukanal))
            ]

            relwLIT = [
                np.array(ln.split(';')).astype(float).tolist() for ln in open(
                    litpath3He + 'basis_struct/relw3heLIT_J%s_%s.dat' %
                    (Jstreustring, streukanal))
            ]

            if 'dbg' in cal:
                print(
                    '\n3He components (full) + LIT-basis components (bare):\n',
                    len(lfrags))
                print(sfrags)
                print('\nLIT-basis components (full):\n', len(lfrags2))
                print(sfrags2)

            for lit_zerl in range(len(lfrags2)):

                if os.path.isdir(litpath3He + 'tmp_%d' % lit_zerl) == False:
                    os.mkdir(litpath3He + 'tmp_%d' % lit_zerl)
                os.chdir(litpath3He + 'tmp_%d' % lit_zerl)

                for file in os.listdir(os.getcwd()):
                    if fnmatch.fnmatch(file, '*J%s*.log' % Jstreu):
                        print('I would have deleted .log files...')
                        #exit()
                        if 'dbg' in cal:
                            print('removing old <*.log> files.')
                        os.system('rm *.log')
                        break

                lit_3inqua_seq(
                    intwi=he_iw + [intwLIT[lit_zerl]],
                    relwi=he_rw + [relwLIT[lit_zerl]],
                    anzo=11,
                    LREG='  0  0  0  0  0  0  0  0  0  1  1',
                    outfile=litpath3He + 'tmp_%d/INQUA' % (lit_zerl))
                lit_3inlu(
                    mul=multipolarity,
                    frag=lfrags + [lfrags2[lit_zerl]],
                    fn=litpath3He + 'tmp_%d/INLU' % (lit_zerl))
                lit_3inob(
                    fr=sfrags + [sfrags2[lit_zerl]],
                    fn=litpath3He + 'tmp_%d/INOB' % (lit_zerl))

        leftpar = int(1 + 0.5 * (1 + (-1)**
                                 (int(channels[streukanal][0][0][0]
                                      ) + int(channels[streukanal][0][0][1]))))

        def cal_rhs_lu_ob_qua(para, procnbr):

            slave_pit = litpath3He + 'tmp_%d' % para
            cmdlu = BINLITpath + 'luise.exe > dump'
            cmdob = BINLITpath + 'obem.exe > dump'
            cmdqu = BINLITpath + 'qual.exe'
            print('%s in %s' % (cmdlu, slave_pit))
            plu = subprocess.Popen(
                shlex.split(cmdlu),
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                cwd=slave_pit)
            out, err = plu.communicate()
            print('process = %d-1 : luise exits.' % para)

            print('%s in %s' % (cmdob, slave_pit))
            pob = subprocess.Popen(
                shlex.split(cmdob),
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                cwd=slave_pit)
            out, err = pob.communicate()
            print('process = %d-1 : ober exits.' % para)

            print('%s in %s' % (cmdqu, slave_pit))
            pqu = subprocess.Popen(
                shlex.split(cmdqu),
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                cwd=slave_pit)
            out, err = pqu.communicate()
            print('process = %d-1 : qual exits.' % para)

        def cal_rhs_end(para, procnbr):

            slave_pit = litpath3He + 'tmp_%d/' % para[3]

            inenf = 'inenlit%d-%d_J%3.1f_mJ%3.1f-mL%d.log' % (para[1], para[2],
                                                              Jstreu,
                                                              para[0][1],
                                                              para[0][0])
            outfseli = 'endlit%d-%d_J%3.1f_mJ%3.1f-mL%d.log' % (
                para[1],
                para[2],
                Jstreu,
                para[0][1],
                para[0][0],
            )
            outfsbare = '%d_inhomo%d-%d_J%3.1f_mJ%3.1f-mL%d.log' % (
                para[3],
                para[1],
                para[2],
                Jstreu,
                para[0][1],
                para[0][0],
            )

            lit_3inen(
                MREG='  0  0  0  0  0  0  0  0  0  1  1',
                #                   (shifted) QBV                     nr.rw
                KSTREU=[para[1], para[2]],
                JWSL=Jstreu,
                JWSLM=para[0][1],
                MULM2=para[0][0],
                NPARL=leftpar,
                JWSR=J0,
                NPARR=2,
                EB=EBDG,
                BUECO=BUECO,
                NZE=anz_phot_e,
                EK0=phot_e_0,
                EKDIFF=phot_e_d,
                #bnd=helionpath + 'INEN',
                bnd='',
                outfile=slave_pit + inenf)

            cmdend = BINLITpath + 'enemb.exe %s %s %s' % (inenf, outfseli,
                                                          outfsbare)

            pend = subprocess.Popen(
                shlex.split(cmdend),
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                cwd=slave_pit)
            out, err = pend.communicate()

            return (out, err)

        parameter_set_lu_ob_qua = range(len(lfrags2))
        parameter_set_end = []

        wfn = litpath3He + 'basis_struct/LITbas_full_J%s_%s.dat' % (
            Jstreustring, streukanal)

        print('[...] reading BV-rw tupel from %s' % wfn)
        litbas = [np.loadtxt(wfn).astype(int)[0]]

        for lit_zerl in range(len(lfrags2)):
            bsbv = sum([len(b) for b in he_iw])
            parameter_set = []
            bvrange = range(
                sum([len(z) for z in intwLIT[:lit_zerl]]) + 1,
                sum([len(z) for z in intwLIT[:lit_zerl]]) + 1 +
                len(intwLIT[lit_zerl]))

            litbas3 = []
            for bv in filter(lambda x: (x[0] in bvrange), litbas):
                litbas3.append([int(bv[0] - (bvrange[0] - 1) + bsbv), bv[1]])

            with open(litpath3He + 'tmp_%d/LITbas_full_J%s.dat' %
                      (lit_zerl, Jstreustring), 'wb') as f:
                np.savetxt(f, [[jj[0], jj[1]] for jj in litbas3], fmt='%d')
            f.close()

            for mM in mLmJl:
                parameter_set.append([mM, 1, 1, lit_zerl])
                #for bv in filter(lambda x: (x[0] in bvrange), litbas):
                #    parameter_set.append([
                #        mM,
                #        int(bv[0] - (bvrange[0] - 1) + bsbv), bv[1], lit_zerl
                #    ])

            parameter_set_end.append(parameter_set)

        if 'rhs-qual' in cal:
            parameter_set = parameter_set_lu_ob_qua
            results = []
            pool = ThreadPool(anzproc)
            for procnbr in range(len(parameter_set)):
                pars = parameter_set[procnbr]
                results.append(
                    pool.apply_async(cal_rhs_lu_ob_qua, (
                        pars,
                        procnbr,
                    )))
            pool.close()
            pool.join()

            for lit_zerl in range(len(lfrags2)):
                os.system('mv ' + litpath3He + 'tmp_%d/QUAOUT ' % lit_zerl +
                          litpath3He + 'tmp_%d/QUAOUT_J%3.1f' % (lit_zerl,
                                                                 Jstreu))

        if 'rhs-end' in cal:
            for lit_zerl in range(len(lfrags2)):
                print('(J=%s)  werkle in %d' % (Jstreu, lit_zerl))
                try:
                    os.system('cp ' + litpath3He + 'tmp_%d/QUAOUT_J%3.1f ' % (
                        lit_zerl,
                        Jstreu) + litpath3He + 'tmp_%d/QUAOUT' % (lit_zerl))
                except:
                    print('<QUAOUT> na for this channel.')
                    exit()

                results = []
                pool = ThreadPool(anzproc)
                parameter_set = parameter_set_end[lit_zerl]

                for procnbr in range(len(parameter_set)):

                    pars = parameter_set[procnbr]

                    results.append(
                        pool.apply_async(cal_rhs_end, (
                            pars,
                            procnbr,
                        )))

                pool.close()
                pool.join()

if 'lhs' in cal:

    print('(ii)    calculating norm/ham in scattering-channel basis')

    if os.path.isdir(litpath3He + 'lit_bas_lhs/') == False:
        os.mkdir(litpath3He + 'lit_bas_lhs/')
    os.chdir(litpath3He + 'lit_bas_lhs/')

    for streukanal in streukas:

        Jstreu = float(streukanal.split('^')[0])
        Jstreustring = '%s' % str(Jstreu)[:3]

        fragfile = [
            ln
            for ln in open(litpath3He + 'basis_struct/frags_LIT_J%s_%s.dat' %
                           (Jstreustring, streukanal))
        ]

        lfrags = [fr.split(' ')[1].strip() for fr in fragfile]
        sfrags = [fr.split(' ')[0] for fr in fragfile]

        intwLIT = [
            np.array(ln.split(';')).astype(float)
            for ln in open(litpath3He + 'basis_struct/intw3heLIT_J%s_%s.dat' %
                           (Jstreustring, streukanal))
        ]

        anzLITbv = sum([len(frgm) for frgm in intwLIT])

        if (len([len(frgm) for frgm in intwLIT]) != (len(fragfile))):
            print('LIT-basis fragments inconcistent!',
                  len([len(frgm) for frgm in intwLIT]), (len(fragfile)))
            exit()

        relwLIT = [
            np.array(ln.split(';')).astype(float)
            for ln in open(litpath3He + 'basis_struct/relw3heLIT_J%s_%s.dat' %
                           (Jstreustring, streukanal))
        ]

        if 'dbg' in cal:
            print(lfrags, sfrags)
            #print(intwLIT, relwLIT)

        Jstreu = float(streukanal.split('^')[0])

        if 'lhs_lu-ob-qua' in cal:

            os.chdir(pathbase + '/data/eob/')
            n3_inob(
                [
                    'he_no1', 'he_no1i', 'he_no2', 'he_no2i', 'he_no6',
                    'he_no6i', 'he_no3i', 'he_no5i'
                ],
                8,
                fn='INOB',
                indep=+1)
            os.system(BINBDGpath + 'KOBER.exe')
            os.chdir(pathbase + '/data/eob-tni/')
            n3_inob(
                [
                    'he_no1', 'he_no1i', 'he_no2', 'he_no2i', 'he_no6',
                    'he_no6i', 'he_no3i', 'he_no5i'
                ],
                15,
                fn='INOB',
                indep=+1)
            os.system(BINBDGpath + 'DROBER.exe')

            os.chdir(pathbase + '/data/elu/')
            n3_inlu(
                8,
                fn='INLUCN',
                fr=[
                    '000', '202', '022', '110', '101', '011', '111', '112',
                    '211', '121', '122', '212', '222', '221', '220'
                ],
                indep=+1)
            os.system(BINBDGpath + 'LUDW_CN.exe')
            os.chdir(pathbase + '/data/elu-tni/')
            n3_inlu(
                8,
                fn='INLU',
                fr=[
                    '000', '202', '022', '110', '101', '011', '111', '112',
                    '211', '121', '122', '212', '222', '221', '220'
                ],
                indep=+1)
            os.system(BINBDGpath + 'DRLUD.exe')

            os.chdir(litpath3He + 'lit_bas_lhs/')

            n3_inlu(8, fn='INLU', fr=lfrags, indep=-0)
            os.system(BINBDGpath + 'DRLUD.exe')
            n3_inlu(8, fn='INLUCN', fr=lfrags, indep=-0)
            os.system(BINBDGpath + 'LUDW_CN.exe')

            n3_inob(sfrags, 8, fn='INOB', indep=-0)
            os.system(BINBDGpath + 'KOBER.exe')
            n3_inob(sfrags, 15, fn='INOB', indep=-0)
            os.system(BINBDGpath + 'DROBER.exe')

            insam(len(lfrags))

            he3inqua(intwi=intwLIT, relwi=relwLIT, potf=potnn)

            os.system('cp INQUA_N INQUA_N%s' % boundstatekanal)

            parallel_mod_of_3inqua(
                lfrags, sfrags, infile='INQUA_N', outfile='INQUA_N')

            subprocess.run([
                'mpirun', '-np',
                '%d' % anzproc, BINBDGpath + 'V18_PAR/mpi_quaf_v6'
            ])

            subprocess.run([BINBDGpath + 'V18_PAR/sammel'])

            he3inqua(intwi=intwLIT, relwi=relwLIT, potf=potnnn)

            parallel_mod_of_3inqua(
                lfrags, sfrags, infile='INQUA_N', outfile='INQUA_N', tni=1)

            subprocess.run([
                'mpirun', '-np',
                '%d' % anzproc, BINBDGpath + 'UIX_PAR/mpi_drqua_uix'
            ])

            subprocess.run([BINBDGpath + 'UIX_PAR/SAMMEL-uix'])

        litbas = np.loadtxt(
            litpath3He + 'basis_struct/LITbas_full_J%s_%s.dat' %
            (Jstreustring, streukanal)).astype(int)
        litbas = [bv for bv in np.unique(litbas, axis=0) if bv[1] != 0]

        anzbs = []

        for lit_zerl in range(len(lfrags)):
            bvmax = 0
            ntmp = 0
            for mm in range(lit_zerl + 1):
                bvmax += len(intwLIT[mm])

            for bv in litbas:
                if bv[0] <= bvmax:
                    ntmp += 1
                else:
                    continue
            anzbs.append(ntmp)

        if len(anzbs) != len(lfrags):
            print('Basis blocks inconsistent. Exiting...')
            exit()

        lit_zerl = 0

        for anzbtmp in anzbs:

            mypath = litpath3He + 'tmp_%d/' % lit_zerl
            lit_zerl += 1
            n3_inen_rhs(
                litbas,
                Jstreu,
                costr,
                np.ones(len(relwLIT[0])),
                fn='INEN',
                pari=0,
                nzop=31,
                tni=11,
                anzb=anzbtmp)
            os.system('cp INEN ' + mypath + 'inen-lit-%s_1-%d' % (streukanal,
                                                                  anzbtmp))
            #if anzbtmp==anzbs[-1]:
            #    subprocess.run([BINBDGpath + 'TDR2END_NORMAL.exe'])
            #    os.system('cp %s/MATOUT ' % (litpath3He + 'lit_bas_lhs/') + respath
            #          + 'norm-ham-litME-%s_1-%d' % (streukanal, anzbtmp))
            #os.system('cp ' + v18uixpath + 'mat_* ' + respath)
            os.system('cp ' + litpath3He + 'tmp_*/*_inhomo* ' + respath)

        #plotHspec(Jstreustring)

if 'couple' in cal:

    os.system('cp ' + helionpath + 'E0.dat ' + litpath3He)

    for streukanal in streukas:

        Jstreu = float(streukanal.split('^')[0])
        Jstreustring = '%s' % str(Jstreu)[:3]

        intwLIT = [
            np.array(ln.split(';')).astype(float).tolist()
            for ln in open(litpath3He + 'basis_struct/intw3heLIT_J%s_%s.dat' %
                           (Jstreustring, streukanal))
        ]

        bv_pro_zerl = [len(zset) for zset in intwLIT]
        first_bv_pro_zerl = [
            sum(bv_pro_zerl[:n]) + 1 for n in range(len(bv_pro_zerl))
        ]

        relwLIT = [
            np.array(ln.split(';')).astype(float).tolist()
            for ln in open(litpath3He + 'basis_struct/relw3heLIT_J%s_%s.dat' %
                           (Jstreustring, streukanal))
        ]

        fragfile = [
            ln
            for ln in open(litpath3He + 'basis_struct/frags_LIT_J%s_%s.dat' %
                           (Jstreustring, streukanal))
        ]

        lfrags = [fr.split(' ')[1].strip() for fr in fragfile]

        litbas = np.loadtxt(
            litpath3He + 'basis_struct/LITbas_full_J%s_%s.dat' %
            (Jstreustring, streukanal)).astype(int)
        litbas = [bv for bv in np.unique(litbas, axis=0) if bv[1] != 0]

        print('He3 structure: anz[int,rel]weiten:', np.sum(he_frgs, axis=0))

        bv_offset = 0

        RHSofBV[streukanal] = {}
        RHSofmJ[streukanal] = {}

        for lit_zerl in range(len(lfrags)):

            # read uncoupled source ME's
            os.chdir(litpath3He + 'tmp_%d' % lit_zerl)

            litbas = np.loadtxt(
                litpath3He + 'basis_struct/LITbas_full_J%s_%s.dat' %
                (Jstreustring, streukanal)).astype(int)
            litbas = [bv for bv in np.unique(litbas, axis=0) if bv[1] != 0]

            print('werkle in %s' % os.getcwd())
            tmp_iw, tmp_rw, tmp_frgs = retrieve_he3_widths('INQUA')

            he_bv = np.sum(he_frgs, axis=0)[0]
            print('bv in He3 : %d' % he_bv)
            bv_in_zerl = tmp_frgs[-1][0]

            print('bv in zerl : %d' % bv_in_zerl)

            zerl_bas = [
                bv for bv in litbas if first_bv_pro_zerl[lit_zerl] <= bv[0] <
                first_bv_pro_zerl[lit_zerl + 1]
            ] if (lit_zerl < len(lfrags) - 1) else [
                bv for bv in litbas if first_bv_pro_zerl[lit_zerl] <= bv[0]
            ]

            zerl_bas = [[
                bv[0] + 1 + he_bv - first_bv_pro_zerl[lit_zerl], bv[1]
            ] for bv in zerl_bas]

            photEn = []

            read_uncoupled_source(
                RHSofBV[streukanal],
                photEn,
                streukanal,
                basisSET=zerl_bas,
                firstbv=bv_offset)
            #RHSofBV[streukanal] = read_norm(streukanal, basisSET=litbas)
            #photEn = MeVfm * np.array(
            #    [phot_e_0 + en * phot_e_d for en in range(anz_phot_e)])
            # couple incoming state with photon multipole to Jlit
            couple_source(
                RHSofmJ[streukanal],
                streukanal,
                RHSofBV[streukanal],
                basisSET=zerl_bas,
                firstbv=bv_offset)

            bv_offset += len(zerl_bas)

        os.system('rm -rf ' + litpath3He + '/tmp*')

os.chdir(litpath3He)

os.system('find . -name \"T*OUT.*\" -print0 | xargs -0 rm')


import numpy as np
import matplotlib.pyplot as plt

raw_input=lambda  x: None

def f(t):
    return t**2*np.exp(-t**2)

t = np.linspace(0, 3, 51)    # 51 points between 0 and 3
y = np.zeros(len(t))         # allocate y with float elements
y = f(t)                     # compute all f values at once
plt.plot(t, y)

plt.savefig('tmp1.eps') # produce PostScript
plt.savefig('tmp1.png') # produce PNG

plt.show()
raw_input('Press the Return key to quit: ')

#plt.plot(t, y)
#plt.legend(['t^2*exp(-t^2)'])
plt.plot(t, y, label='t^2*exp(-t^2)')
plt.xlabel('t')
plt.ylabel('y')
plt.axis([0, 3, -0.05, 0.6])   # [tmin, tmax, ymin, ymax]
plt.title('My First Matplotlib Demo')
plt.show()
plt.savefig('tmp2.eps') # produce PostScript
raw_input('Press the Return key to quit: ')

# Multiple curves

def f1(t):
    return t**2*np.exp(-t**2)

def f2(t):
    return t**2*f1(t)

t = np.linspace(0, 3, 51)
y1 = f1(t)
y2 = f2(t)

plt.plot(t, y1, 'r-')
plt.plot(t, y2, 'bo')
plt.xlabel('t')
plt.ylabel('y')
plt.legend(['t^2*exp(-t^2)', 't^4*exp(-t^2)'])
plt.title('Plotting two curves in the same plot')
plt.savefig('tmp3.eps')
plt.show()

# multiple plots
plt.figure()
plt.subplot(2, 1, 1)
t = np.linspace(0, 3, 51)
y1 = f1(t)
y2 = f2(t)

plt.plot(t, y1, 'r-', t, y2, 'bo')
plt.xlabel('t')
plt.ylabel('y')
plt.axis([t[0], t[-1], min(y2)-0.05, max(y2)+0.5])
plt.legend(['t^2*exp(-t^2)', 't^4*exp(-t^2)'])
plt.title('Top figure')

plt.subplot(2, 1, 2)
t3 = t[::4]
y3 = f2(t3)

plt.plot(t, y1, 'b-', t3, y3, 'ys')
plt.xlabel('t')
plt.ylabel('y')
plt.axis([0, 4, -0.2, 0.6])
plt.legend(['t^2*exp(-t^2)', 't^4*exp(-t^2)'])
plt.title('Bottom figure')
plt.savefig('tmp4.eps')
plt.show()

raw_input('Press the Return key to quit: ')


import PyPDF2

def rotate_pdf(pdf_path, output_path, rotation_angle):
    with open(pdf_path, 'rb') as file:
        reader = PyPDF2.PdfReader(file)
        writer = PyPDF2.PdfWriter()

        for page_num in range(len(reader.pages)):
            page = reader.pages[page_num]
            page.rotate(rotation_angle)
            writer.add_page(page)

        with open(output_path, 'wb') as output_file:
            writer.write(output_file)

# Example usage
# Put target pdf file in the same folder
pdf_path = 'file_name.pdf'
output_path = 'file_name_rotated.pdf'
rotation_angle = 90  # Change this value to the desired rotation angle

rotate_pdf(pdf_path, output_path, rotation_angle)


#!/usr/bin/env python3

"""
Utility to compile the requirements-*.txt file with consistent dependencies

This script takes the requirements-*.in and tries to compile a fully pinned set
of requirement files with all requirements between packages fulfilled.

To upgrade all python packages to the latest version just run it with
`--upgrade`. To just add a python package just add it unpinned to the .in and
the .txt files and run without the upgrade option.
"""

import subprocess
import re
import os
import requests
import argparse

def get_annotations(filename):
    """Get the `# via package` annotations from previously pinned requirements.txt files"""
    result = {}
    pkg = re.compile(r"^(.*?)==(.*?)\s*\\?$")
    comment = re.compile(r"#\s*(.*)$")
    with open(filename) as f:
        package = None
        for line in f:
            m = pkg.match(line)
            if m:
                package = m.groups()
            m = comment.search(line)
            if m:
                result[package[0]] = m.group(1)
    return result

def get_info(name, version):
    """Get JSON information for a package from pypi"""
    req = requests.get(f"https://pypi.org/pypi/{name}/{version}/json")
    req.raise_for_status()
    return req.json()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--upgrade", default=False, action="store_true", help="If present update all packages to the "
                        "latest possible version. Otherwise just determine missing dependencies when addding new packages")
    parser.add_argument("--no-root", default=False, action="store_true",
                        help="Ignore packages depending on ROOT if ROOT isn't compiled yet")
    args, remaining = parser.parse_known_args()
    # make sure root is setup, otherwise root-numpy fails horribly
    subprocess.run("which root-config", shell=True)

    # Read the existing README.md and split at python package list table
    old = open("README.md").readlines()
    start_marker = None
    end_marker = None
    for i, l in enumerate(old):
        if l.startswith("[//]: # (START PYTHON_PKG)"):
            start_marker = i
        if l.startswith("[//]: # (END PYTHON_PKG)"):
            end_marker = i
    if start_marker is None or end_marker is None:
        raise ValueError("cannot find markers")

    # input files
    files = ["requirements-base.in", "requirements-core.in"]
    if not args.no_root:
        files.insert(1, "requirements-root.in")
    # make sure pip-tools are installed
    subprocess.run(["python3", "-m", "pip", "install", "--user", "pip-tools"])
    # and compile the requirements.txt
    if not args.upgrade:
        processed_files = [os.path.splitext(e)[0] + ".txt" for e in files]
        subprocess.run(["python3", "-m", "piptools", "compile", '-v', "--no-header", "--annotate", "--allow-unsafe", "-o", "requirements.txt"] +
                       processed_files + remaining)
    else:
        subprocess.run(["python3", "-m", "piptools", "compile", '-v', "--no-header", "--annotate", "--upgrade", "--allow-unsafe", "-o", "requirements.txt"] +
                       files + remaining)

    found = {}
    infos = []

    # now lets go through it and find all packages (and their annotations)
    with open("requirements.txt") as f:
        for line in f:
            if not line.strip() or line.startswith("#"):
                continue

            m = re.match(r"^(.*?)(\[[^\]]*\]*)?==(.*?)\s*(#.*)?$", line)
            if m:
                name = m.group(1)
                extra = m.group(2)
                if extra:
                    print(name, extra)
                else:
                    extra = ""
                version = m.group(3)
                comment = m.group(4)
                print(f"Obtain info for {name}=={version}")
                json_data = get_info(name, version)
                # make list of all checksums
                content = []
                for f in json_data["urls"]:
                    try:
                        sha256 = f["digests"]["sha256"]
                        content.append(f"    --hash=sha256:{sha256}")
                    except KeyError:
                        pass
                # sort by checksum and insert package requirement
                content.sort()
                content.insert(0, f"{name}{extra}=={version}")
                # add dependency comment if present
                if comment:
                    content.append(f"    {comment}")
                # make it one string
                found[name+extra] = " \\\n".join(content) + "\n"
                # and remember package information
                info = json_data["info"]
                info["link"] = f"[{info['name']}]({info['project_url']})"
                if not info["license"]:
                    for package in old:
                        if info["name"] in package:
                            fields = package.split("|")
                            if len(fields) > 4:
                                info["license"] = fields[4].strip()
                if info["license"] is None:
                    info["license"] = ""
                infos.append(info)

            # check pypi for https urls
            m = re.search("http.*/(.*?)-(.*?)((\\+|%2B).*)?-cp", line)
            if m:
                name = m.group(1)
                version = m.group(2)
                print(f"Obtain info from url for {name}=={version}")
                try:
                    json_data = get_info(name, version)
                    info = json_data["info"]
                    info["link"] = f"[{info['name']}]({info['project_url']})"
                    if not info["license"]:
                        for package in old:
                            if info["name"] in package:
                                fields = package.split("|")
                                if len(fields) > 4:
                                    info["license"] = fields[4].strip()
                    if info["license"] is None:
                        info["license"] = ""
                    infos.append(info)
                except Exception as e:
                    print(e)
                    pass

    print("Update requirement files")
    for i, filename in enumerate(files):
        content = open(filename).readlines()
        with open(os.path.splitext(filename)[0] + ".txt", "w") as out:
            for line in content:
                n = line.strip().lower()
                if n.find("==") >= 0:
                    n, v = n.split("==")
                # did we find the package? if so delete from list
                if n in found:
                    out.write(found[n])
                    del found[n]
                else:
                    out.write(line)

            # last file, put in all the remaining packages
            if i == len(files)-1:
                for n, c in sorted(found.items()):
                    out.writelines(c)

    print("Update readme")
    widths = {}
    for key, minwidth in ("link", 4), ("summary", 7), ("version", 7), ("license", 7):
        widths["max_"+key] = max(minwidth, max(len(e[key]) for e in infos))

    table = ["\n"]
    table.append("| {link:{max_link}} | {summary:{max_summary}} | {version:{max_version}} | {license:{max_license}} |\n".format(
        link="Name", summary="Summary", version="Version", license="License", **widths))
    table.append(
        "|-{link:-<{max_link}}-|-{summary:-<{max_summary}}-|-{version:-<{max_version}}-|-{license:-<{max_license}}-|\n".format(
            link="",
            summary="",
            version="",
            license="",
            **widths))
    for i in sorted(infos, key=lambda x: x['name'].lower()):
        table.append(
            "| {link:{max_link}} | {summary:{max_summary}} | {version:{max_version}} | {license:{max_license}} |\n".format(
                **i,
                **widths))

    table.append("\n")

    with open("README.md", "w") as f:
        f.writelines(old[:start_marker+1] + table + old[end_marker:])


def open_file():
    find = True
    while find:
        try:
            file=input("Enter a file name:")
            fp=open("GDP.txt")
            return fp
        except IOError:
            print("Error. please try again")

def find_min_percent(line):
    x=76
    y=88
    index=0
    min_value=10**7
    min_value_index=-1
    for i in range(47):
        try:
            value=float(line[x:y].strip())
        except ValueError:
            continue
        if min_value > value:
            min_value = value
            min_value_index=i
        else:
            pass
        x+=12
        y+=12
        index+=1
    return min_value,min_value_index

def find_max_percent(line):
    x=76
    y=88
    max_value=0
    max_value_index=-1
    for i in range(46):
        try:
            value=float(line[x:y].strip())
        except ValueError:
            continue
        if max_value<value:
            max_value=value
            max_value_index=i
        else:
            pass
        x+=12
        y+=12
    return max_value,max_value_index

def find_gdp(line, index):
    x=76
    y=88
    for i in range(49):
        if i ==index:
            return float(line[x:y].strip())
        else:
            pass
        x+=12
        y+=12

def display(min_val, min_year, min_val_gdp, max_val, max_year, max_val_gdp):
    min_val_gdp=min_val_gdp/1000
    max_val_gdp=max_val_gdp/1000
    print('\nGross Domestic Product')
    print('{:<10s}{:>8s}{:>6s}{:>18s}'.format('min/max','change','year','GDP (trillions)'))
    print("{:<10s}{:>8f}{:>6d}{:>18.2f}".format("min", min_val, min_year, min_val_gdp))
    print("{:<10s}{:>8f}{:>6d}{:>18.2f}".format("max", max_val, max_year, max_val_gdp))

def main():
    main_file=open_file()
    line_number=0
    max_percent=max_index=max_gdp=0
    min_percent=min_index=min_gdp=0
    for line in main_file.readlines():
        if line_number==9:
            max_percent=find_max_percent(line)
            min_percent=find_max_percent(line)
        elif line_number==44:
            min_gdp=find_gdp(line,min_index)
            max_gdp=find_gdp(line,max_index)
        else:
            pass
        line_number+=1
    display(min_percent, min_index + 1969, min_gdp, max_percent, max_index + 1969, max_gdp )

if __name__ == "__main__":
    main()


from Utility.getRankedLists import getRankedLists
from Utility.prepareEval import prepareEval

methods = ["HARE"]

classes = ["City", "University", "VideoGame", "SpaceStation", "Mountain", "Hotel",
	            "EurovisionSongContestEntry", "Drug", "Comedian", "ChessPlayer",
	            "Band", "Album", "Person", "Automobile", "HistoricPlace", "Town",
	            "MilitaryConflict", "ProgrammingLanguage", "Country"]

for method in methods:
	getRankedLists(classes, method)
	prepareEval(classes, method)


# -*- coding: UTF-8 -*-
"""
title: 后缀表达式
根据 逆波兰表示法，求该后缀表达式的计算结果。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数，也可以是另一个逆波兰表达式。
说明：
    整数除法只保留整数部分。
    给定逆波兰表达式总是有效的。换句话说，表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1：
输入：tokens = ["2","1","+","3","*"]
输出：9
解释：该算式转化为常见的中缀算术表达式为：((2 + 1) * 3) = 9

示例 2：
输入：tokens = ["4","13","5","/","+"]
输出：6
解释：该算式转化为常见的中缀算术表达式为：(4 + (13 / 5)) = 6

示例 3：
输入：tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出：22
解释：
该算式转化为常见的中缀算术表达式为：
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

提示：
1 <= tokens.length <= 10^4
tokens[i] 要么是一个算符（"+"、"-"、"*" 或 "/"），要么是一个在范围 [-200, 200] 内的整数

逆波兰表达式：
逆波兰表达式是一种后缀表达式，所谓后缀就是指算符写在后面。
    平常使用的算式则是一种中缀表达式，如 ( 1 + 2 ) * ( 3 + 4 ) 。
    该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点：
    去掉括号后表达式无歧义，上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
    适合用栈操作运算：遇到数字则入栈；遇到算符则取出栈顶两个数字进行计算，并将结果压入栈中。
"""
from typing import List

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        """栈"""
        stack = []
        for ch in tokens:
            if ch in ['+', '-', '*', '/']:
                n2 = stack.pop()
                n1 = stack.pop()
                if ch == '+':
                    tmp = n1 + n2
                elif ch == '-':
                    tmp = n1 - n2
                elif ch == '*':
                    tmp = n1 * n2
                else:
                    # 注意：不能写成 n1 // n2，负数整除会有问题
                    tmp = int(n1 / n2)
                stack.append(tmp)
            else:
                stack.append(int(ch))
        return stack[0]

if __name__ == '__main__':
    print(Solution().evalRPN(["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]))


import os
import logging

ROOT = './'
path = ROOT + '/zimu_text'
logging.basicConfig(filename='clean_subtitle.log', level=logging.DEBUG)

for root, dirs, files in os.walk(path):
    for file_name in files:
        name, extension = os.path.splitext(file_name)

        if extension.lower() in ['.txt', '.srt', '.ass', '.ssa', '.lrc']:
            old_file = root + '/' + file_name
            new_file = './zimu_cleaned/' + file_name
            try:
                os.copy(old_file, new_file)
            except Exception as e:
                logging.error(str(e))
                logging.info()