from data.NyuHandPoseDataset import NyuAnnoType
from data.loaderfactory import DatasetType
from util.transformations import transformPoint2D
import nets

import numpy
import os
import time
import matplotlib.pyplot as plt
import cPickle

# Pytorch
import torch

#%% Helper
def evaluate_model(model, testset_loader, used_device):
    """
    Evaluates a learned model on the data provided by the loader

    Arguments:
        model (torch.nn.Module): the trained model
        testset_loader (torch.utils.data.DataLoader): loader for test samples;
            need to be a loader for a NyuHandPoseMultiViewDataset
        used_device (torch.device)
    """
    with torch.no_grad():
        numJoints = testset_loader.dataset.args_data.num_joints
        sample_size = testset_loader.dataset[0][0].numpy().shape

        model = model.to(used_device)
        model.eval()

        data_all = numpy.zeros((0, sample_size[0], sample_size[1], sample_size[2]))
        targets_all = numpy.zeros((0, numJoints, 3))
        estimates_all = numpy.zeros((0, numJoints, 3))
        transforms_all = numpy.zeros((0, 3, 3))
        coms_all = numpy.zeros((0, 3))

        t0 = time.time()
        time_sum = 0.0
        for data,_,_, targets,_,_, transforms,_,_, coms,_,_, sizes,_,_, \
            data_s,_,_, targets_s,_,_, transforms_s,_,_, coms_s,_,_, sizes_s,_,_, _ \
                in testset_loader:
            # Compute prediction
            data = data.to(used_device)
            t0_ = time.time()
            if isinstance(model, nets.resnet.ResNet_Map_PreView):
                estimates = model(data, [], do_preview=False)
            else:
                estimates = model(data)
            t1_ = time.time()
            time_sum += t1_ - t0_
            if isinstance(estimates, tuple):    # a model which returns a tuple shall return the pose first
                estimates = estimates[0]

            # Prepare output transform result space, to numpy arrays, ...
            data_all = numpy.append(data_all, data.data.cpu().numpy(), axis=0)
            com_n = coms.numpy()
            targets = targets.numpy()
            sizes = sizes.numpy()
            targets = testset_loader.dataset.denormalize_joint_pos(targets, sizes)
            targets = targets + com_n.reshape(com_n.shape[0], 1, com_n.shape[1])
            targets_all = numpy.append(targets_all, targets, axis=0)

            estimates = estimates.data.cpu().view(-1,numJoints,3).numpy()
            estimates = testset_loader.dataset.denormalize_joint_pos(estimates, sizes)
            estimates = estimates + com_n.reshape(com_n.shape[0], 1, com_n.shape[1])
            estimates_all = numpy.append(estimates_all, estimates, axis=0)

            transforms_all = numpy.append(transforms_all, transforms.numpy(), axis=0)

            coms_all = numpy.append(coms_all, com_n, axis=0)

        t1 = time.time()
        runtime = t1 - t0
        num_images = len(testset_loader.sampler)
        print("  Time for evaluating on {} images: {:.2f} sec. ({:.1f} msec./image)".format(
            num_images, runtime, runtime / float(num_images) * 1000.0))
        print("  Time measured only for fprops: {:.2f} sec. ({:.1f} msec./image)".format(
            time_sum, time_sum / float(num_images) * 1000.0))

        return targets_all, estimates_all, transforms_all, coms_all, data_all

def compute_and_output_results(targets, predictions, dataset, args, out_path):
    hpe = get_evaluator(targets, predictions, args)
    hpe.outputPath = out_path
    num_joints = targets.shape[1]

    # Compute (and print) basic errors
    mean_error = hpe.getMeanError()
    print("  Mean error: {:.2f} mm".format(mean_error))
    print("  FS80: {:.3f}".format(hpe.getMDscore(80)))

    # Write evaluation results to textfile
    out_result_filepath = os.path.join(out_path, args.out_filename_result)
    hpe.writeResults2Textfile(out_result_filepath)

    # Write predicted joint positions (binary)
    filepath_est = os.path.join(out_path, args.out_filename_joint_positions_estimated)
    cPickle.dump(predictions, open(filepath_est, "wb"), protocol=cPickle.HIGHEST_PROTOCOL)
    # Write ground truth joint positions (binary)
    filepath_gt = os.path.join(out_path, args.out_filename_joint_positions_groundtruth)
    cPickle.dump(targets, open(filepath_gt, "wb"), protocol=cPickle.HIGHEST_PROTOCOL)

    # Write predicted joint positions (xyz, to text-file)
    jts_out = predictions.reshape(-1, num_joints * 3)
    filepath_txt_xyz = os.path.join(out_path, args.out_filename_joint_positions_estimated_xyz_txt)
    numpy.savetxt(filepath_txt_xyz, jts_out, args.out_fmt_txt)

    # Convert predicted joint positions to uvd and write to text-file
    predictions_uvd = dataset.points3DToImg(predictions.reshape(-1, 3))
    predictions_uvd = predictions_uvd.reshape(-1, num_joints * 3)
    filepath_txt_uvd = os.path.join(out_path, args.out_filename_joint_positions_estimated_uvd_txt)
    numpy.savetxt(filepath_txt_uvd, predictions_uvd, args.out_fmt_txt)

    hpe.plotEvaluation(args.exp_name, methodName='Ours')

def get_evaluator(targets, predictions, args):
    """
    Create an evaluator for the dataset specified by args.dataset_type
    """
    if args.dataset_type == DatasetType.NYU:
        hpe = NYUHandposeEvaluation(targets, predictions, joints=args.num_joints)
    elif args.dataset_type == DatasetType.ICG:
        hpe = ICGHandposeEvaluation(targets, predictions)
    return hpe

class HandposeEvaluation(object):
    """
    Different evaluation metrics for handpose, L2 distance used
    """

    def __init__(self, gt, joints):
        """
        Initialize class

        :type gt: groundtruth joints
        :type joints: calculated joints
        """

        if not (isinstance(gt, numpy.ndarray) or isinstance(gt, list)) or not (
                isinstance(joints, list) or isinstance(joints, numpy.ndarray)):
            raise ValueError("Params must be list or ndarray")

        if len(gt) != len(joints):
            print("Error: groundtruth has {} elements, eval data has {}".format(len(gt), len(joints)))
            raise ValueError("Params must be the same size")

        if len(gt) == len(joints) == 0:
            print("Error: groundtruth has {} elements, eval data has {}".format(len(gt), len(joints)))
            raise ValueError("Params must be of non-zero size")

        if gt[0].shape != joints[0].shape:
            print("Error: groundtruth has {} dims, eval data has {}".format(gt[0].shape, joints[0].shape))
            raise ValueError("Params must be of same dimensionality")

        self.gt = numpy.asarray(gt)
        self.joints = numpy.asarray(joints)
        assert (self.gt.shape == self.joints.shape)

        self.colors = ['blue', 'green', 'red', 'cyan', 'magenta', 'black', 'brown', 'gray', 'indigo', 'pink',
                       'lightgreen', 'darkorange', 'peru', 'steelblue', 'turquoise']
        self.linestyles = ['-']  # , '--', '-.', ':', '-', '--', '-.', ':']
        self.lineWidth = 2.0
        self.jointcolors = [(0.0, 0.0, 1.0), (0.0, 0.5, 0.0), (1.0, 0.0, 0.0), (0.0, 0.75, 0.75),
                            (0.75, 0, 0.75), (0.75, 0.75, 0), (0.0, 0.0, 0.0)]

        self.outputPath = './results'
        self.visiblemask = numpy.ones((self.gt.shape[0], self.gt.shape[1], 3))

        self.jointNames = None
        self.jointConnections = []
        self.jointConnectionColors = []
        self.plotMaxJointDist = 80
        self.plotMeanJointDist = 80
        self.plotMedianJointDist = 80
        self.VTKviewport = [0, 0, 0, 0, 0]

    def getMeanError(self):
        """
        get average error over all joints, averaged over sequence
        :return: mean error
        """
        return numpy.nanmean(numpy.sqrt(numpy.square(self.gt - self.joints).sum(axis=2)), axis=1).mean()

    def getStdError(self):
        """
        get standard deviation of error over all joints, averaged over sequence
        :return: standard deviation of error
        """
        return numpy.nanstd(numpy.sqrt(
            numpy.square(self.gt - self.joints).sum(axis=2)), axis=1).mean()

    def getMaxError(self):
        """
        get max error over all joints, averaged over sequence
        :return: maximum error
        """
        return numpy.nanmax(numpy.sqrt(numpy.square(self.gt - self.joints).sum(axis=2))).mean()

    def getMeanErrorPerFrame(self):
        """
        get average error over all joints, for each frame
        :return: mean error per frame
        """
        return numpy.nanmean(numpy.sqrt(numpy.square(self.gt - self.joints).sum(axis=2)), axis=1)

    def getMaxErrorPerFrame(self):
        """
        get max error over all joints for each image of sequence
        :return: maximum error per frame
        """
        return numpy.nanmax(numpy.sqrt(numpy.square(self.gt - self.joints).sum(axis=2)), axis=1)

    def getJointMeanError(self, jointID):
        """
        get error of one joint, averaged over sequence
        :param jointID: joint ID
        :return: mean joint error
        """
        return numpy.nanmean(numpy.sqrt(numpy.square(self.gt[:, jointID, :] - self.joints[:, jointID, :]).sum(axis=1)))

    def getJointStdError(self, jointID):
        """
        get standard deviation of one joint, averaged over sequence
        :param jointID: joint ID
        :return: standard deviation of joint error
        """
        return numpy.nanstd(numpy.sqrt(
            numpy.square(
            self.gt[:, jointID, :] - self.joints[:, jointID, :]).sum(axis=1)))

    def getJointMaxError(self, jointID):
        """
        get maximum error of one joint
        :param jointID: joint ID
        :return: maximum joint error
        """
        return numpy.nanmax(numpy.sqrt(numpy.square(
            self.gt[:, jointID, :] - self.joints[:, jointID, :]).sum(axis=1)))

    def getNumFramesWithinMaxDist(self, dist):
        """
        calculate the number of frames where the maximum difference of a joint is within dist mm
        :param dist: distance between joint and GT
        :return: number of frames
        """
        return (numpy.nanmax(numpy.sqrt(
            numpy.square(self.gt - self.joints).sum(axis=2)), axis=1) <= dist).sum()

    def getNumFramesWithinMeanDist(self, dist):
        """
        calculate the number of frames where the mean difference over all joints of a hand are within dist mm
        :param dist: distance between joint and GT
        :return: number of frames
        """
        return (numpy.nanmean(numpy.sqrt(
            numpy.square(self.gt - self.joints).sum(axis=2)), axis=1) <= dist).sum()

    def getNumFramesWithinMedianDist(self, dist):
        """
        calculate the number of frames where the median difference over all joints of a hand are within dist mm
        :param dist: distance between joint and GT
        :return: number of frames
        """
        return (numpy.median(numpy.sqrt(
            numpy.square(self.gt - self.joints).sum(axis=2)), axis=1) <= dist).sum()

    def getNumJointsWithinDist(self, dist):
        """
        calculate the number of joints within dist mm to ground truth
        :param dist: distance between joint and GT
        :return: number of frames
        """
        return (numpy.sqrt(
            numpy.square(self.gt - self.joints).sum(axis=2)) <= dist).sum()

    def getMDscore(self, dist):
        """
        Calculate the max dist score, ie. MD=\int_0^d{\frac{|F<x|}{|F|}dx = \sum
        That is the area-under-the-curve (AuC) for the frame based success rate
        up to the given dist.
        :param dist: distance between joint and GT
        :return: score value [0-1]
        """
        vals = [(numpy.nanmax(numpy.sqrt(numpy.square(
            self.gt - self.joints).sum(axis=2)), axis=1) <= j).sum()
            / float(self.joints.shape[0]) for j in range(0, dist)]
        return numpy.asarray(vals).sum() / float(dist)

    def getJointSuccesAuC(self, dist):
        """
        Compute the area-under-the-curve (AuC) score for the joint based
        success rate (i.e., AuC for 'joints within distance' plot up to given
        dist). This is the 'MD-score' for individual joints (instead of frames)
        :param dist: distance between joint and GT
        :return: score value [0-1]
        """
        vals = [(numpy.sqrt(numpy.square(self.gt - self.joints).sum(axis=2)) <= j).sum() \
            / float(self.joints.shape[0] * self.joints.shape[1]) for j in range(0, dist)]
        return numpy.asarray(vals).sum() / float(dist)

    def plotEvaluation(self, basename, methodName='Our method', baseline=None,
                       basePath=None, curveColors=None, lineWidth=None):
        """
        plot and save standard evaluation plots
        :param basename: file basename, i.e., prefix for (output) filenames
        :param methodName: our method name
        :param baseline: list of baselines as tuple (Name,evaluation object)
        :param basePath: if not None this path is used as base-path instead of
                         the default path (self.outputPath)
        :param curveColors: list of colors for each method (see self.colors for default)
        :param lineWidth: line width for the curves
        :return: None
        """
        # Sanity check
        if baseline is not None:
            for bs in baseline:
                if not (isinstance(bs[1], self.__class__)):
                    raise TypeError('baseline must be of type {} but {} provided'.format(
                        self.__class__.__name__, bs[1].__class__.__name__))

        if basePath is None:
            basePath = self.outputPath

        if curveColors is None:
            curveColors = self.colors

        if lineWidth is None:
            lineWidth = self.lineWidth

        if not os.path.exists(basePath):
            os.makedirs(basePath)

        # plot number of frames within max distance
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot([self.getNumFramesWithinMaxDist(j) / float(self.joints.shape[0]) * 100. for j in range(0, self.plotMaxJointDist)],
                label=methodName, c=curveColors[0], linestyle=self.linestyles[0], linewidth=lineWidth)
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                ax.plot([bs[1].getNumFramesWithinMaxDist(j) / float(self.joints.shape[0]) * 100. for j in range(0, self.plotMaxJointDist)],
                        label=bs[0], c=curveColors[bs_idx % len(curveColors)],
                        linestyle=self.linestyles[bs_idx % len(self.linestyles)],
                        linewidth=lineWidth)
                bs_idx += 1
        plt.xlabel('Distance threshold / mm')
        plt.ylabel('Fraction of frames with max distance within threshold / %')
        plt.ylim([0.0, 100.0])
        ax.grid(True)
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        # lgd = ax.legend(handles, labels, loc='lower right', ncol=1) #, bbox_to_anchor=(0.5,-0.1)
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_framesMaxWithin.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

        # plot number of frames within; mean distance
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot([self.getNumFramesWithinMeanDist(j) / float(self.joints.shape[0]) * 100. for j in range(0, self.plotMeanJointDist)],
                label=methodName, c=curveColors[0], linestyle=self.linestyles[0],
                linewidth=lineWidth)
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                ax.plot([bs[1].getNumFramesWithinMeanDist(j) / float(self.joints.shape[0]) * 100. for j in range(0, self.plotMeanJointDist)],
                        label=bs[0], c=curveColors[bs_idx % len(curveColors)],
                        linestyle=self.linestyles[bs_idx % len(self.linestyles)],
                        linewidth=lineWidth)
                bs_idx += 1
        plt.xlabel('Distance threshold / mm')
        plt.ylabel('Fraction of frames with mean distance within threshold / %')
        plt.ylim([0.0, 100.0])
        ax.grid(True)
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        # lgd = ax.legend(handles, labels, loc='lower right', ncol=1) #, bbox_to_anchor=(0.5,-0.1)
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_framesMeanWithin.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

        # plot number of frames within; median distance
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot([self.getNumFramesWithinMedianDist(j) / float(self.joints.shape[0]) * 100. for j in range(0, self.plotMedianJointDist)],
                label=methodName, c=curveColors[0], linestyle=self.linestyles[0],
                linewidth=lineWidth)
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                ax.plot([bs[1].getNumFramesWithinMedianDist(j) / float(self.joints.shape[0]) * 100. for j in range(0, self.plotMedianJointDist)],
                        label=bs[0], c=curveColors[bs_idx % len(curveColors)],
                        linestyle=self.linestyles[bs_idx % len(self.linestyles)],
                        linewidth=lineWidth)
                bs_idx += 1
        plt.xlabel('Distance threshold / mm')
        plt.ylabel('Fraction of frames with median distance within threshold / %')
        plt.ylim([0.0, 100.0])
        ax.grid(True)
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        # lgd = ax.legend(handles, labels, loc='lower right', ncol=1) #, bbox_to_anchor=(0.5,-0.1)
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_framesMedianWithin.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

        # plot number of joints within
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot([self.getNumJointsWithinDist(j) / float(self.joints.shape[0] * self.joints.shape[1]) * 100. for j in range(0, self.plotMaxJointDist)],
                label=methodName, c=curveColors[0], linestyle=self.linestyles[0],
                linewidth=lineWidth)
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                ax.plot([bs[1].getNumJointsWithinDist(j) / float(self.joints.shape[0] * self.joints.shape[1]) * 100. for j in range(0, self.plotMaxJointDist)],
                        label=bs[0], c=curveColors[bs_idx % len(curveColors)],
                        linestyle=self.linestyles[bs_idx % len(self.linestyles)],
                        linewidth=lineWidth)
                bs_idx += 1
        plt.xlabel('Distance threshold / mm')
        plt.ylabel('Fraction of joints within threshold to ground truth / %')
        plt.ylim([0.0, 100.0])
        ax.grid(True)
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        # lgd = ax.legend(handles, labels, loc='lower right', ncol=1) #, bbox_to_anchor=(0.5,-0.1)
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_jointsWithin.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

        # plot mean error per frame
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot(numpy.nanmean(numpy.sqrt(numpy.square(self.gt - self.joints).sum(axis=2)), axis=1),
                label=methodName, c=curveColors[0], linestyle=self.linestyles[0],
                linewidth=lineWidth)
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                ax.plot(numpy.nanmean(numpy.sqrt(numpy.square(self.gt - bs[1].joints).sum(axis=2)), axis=1),
                        label=bs[0], c=curveColors[bs_idx % len(curveColors)],
                        linestyle=self.linestyles[bs_idx % len(self.linestyles)],
                        linewidth=lineWidth)
                bs_idx += 1
        plt.xlabel('frame ID')
        plt.ylabel('Error / mm')
        plt.ylim([0.0, 150.0])
        ax.grid(True)
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        # lgd = ax.legend(handles, labels, loc='lower right', ncol=1) #, bbox_to_anchor=(0.5,-0.1)
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_frameerror.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

        # plot mean error for each joint
        ind = numpy.arange(self.joints.shape[1]+1)  # the x locations for the groups, +1 for mean
        if baseline is not None:
            width = (1 - 0.33) / (1. + len(baseline))  # the width of the bars
        else:
            width = 0.67
        fig, ax = plt.subplots()
        mean = [self.getJointMeanError(j) for j in range(self.joints.shape[1])]
        mean.append(self.getMeanError())
        std = [self.getJointStdError(j) for j in range(self.joints.shape[1])]
        std.append(self.getStdError())
        ax.bar(ind, numpy.array(mean), width, label=methodName, color=curveColors[0])  # , yerr=std)
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                mean = [bs[1].getJointMeanError(j) for j in range(self.joints.shape[1])]
                mean.append(bs[1].getMeanError())
                std = [bs[1].getJointStdError(j) for j in range(self.joints.shape[1])]
                std.append(bs[1].getStdError())
                ax.bar(ind + width * float(bs_idx), numpy.array(mean), width,
                       label=bs[0], color=curveColors[bs_idx % len(curveColors)])  # , yerr=std)
                bs_idx += 1
        ax.set_xticks(ind + width)
        ll = list(self.jointNames)
        ll.append('Avg')
        label = tuple(ll)
        ax.set_xticklabels(label)
        plt.ylabel('Mean error of joint / mm')
        # plt.ylim([0.0,50.0])
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_joint_mean.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

        # plot maximum error for each joint
        ind = numpy.arange(self.joints.shape[1])  # the x locations for the groups
        if baseline is not None:
            width = (1 - 0.33) / (1. + len(baseline))  # the width of the bars
        else:
            width = 0.67
        fig, ax = plt.subplots()
        ax.bar(ind, numpy.array([self.getJointMaxError(j) for j in range(self.joints.shape[1])]),
               width, label=methodName, color=curveColors[0])
        bs_idx = 1
        if baseline is not None:
            for bs in baseline:
                ax.bar(ind + width * float(bs_idx),
                       numpy.array([bs[1].getJointMaxError(j) for j in range(self.joints.shape[1])]),
                       width, label=bs[0], color=curveColors[bs_idx % len(curveColors)])
                bs_idx += 1
        ax.set_xticks(ind + width)
        ax.set_xticklabels(self.jointNames)
        plt.ylabel('Maximum error of joint / mm')
        plt.ylim([0.0, 200.0])
        # Put a legend below current axis
        handles, labels = ax.get_legend_handles_labels()
        lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=3)
        plt.show(block=False)
        fig.savefig(os.path.join(basePath,'{}_joint_max.pdf'.format(basename)),
                    bbox_extra_artists=(lgd,), bbox_inches='tight')
        plt.close(fig)

    def writeQualitativeResults(self, data, crop_transforms, predictions, targets, test_loader, args):
        """
        Plot qualitative results on image data and write to disk
        :param data: image crops
        :param crop_transforms: transformation matrices specifying tranformation
            of each crop as returned by the data loader
        :param predictions: predicted joint positions
        :param targets: ground truth joint positions
        :param test_loader: dataset loader (only used for proper transformations)
        :param args: additional paramters
        """
        for ind in range(0, predictions.shape[0], args.step_result_overlay):
            jt = predictions[ind]
            gt = targets[ind]
            jtI = test_loader.dataset.points3DToImg(jt)
            gtI = test_loader.dataset.points3DToImg(gt)
            for joint in range(jt.shape[0]):
                t = transformPoint2D(jtI[joint], crop_transforms[ind])
                jtI[joint, 0] = t[0]
                jtI[joint, 1] = t[1]
                t_g = transformPoint2D(gtI[joint], crop_transforms[ind])
                gtI[joint, 0] = t_g[0]
                gtI[joint, 1] = t_g[1]
            self.plotResult(data[ind,0], gtI, jtI, "{}_i{}".format(args.exp_name, ind))

    def plotResult(self, dpt, gtcrop, joint, name=None, showGT=True,
                   niceColors=False, showJoints=True, showDepth=True,
                   basePath=None, connection_linewidth=2.0):
        """
        Show the annotated depth image
        :param dpt: depth image to show
        :param gtcrop: cropped 2D coordinates
        :param joint: joint data
        :param name: name of file to save, if None return image
        :param showGT: show groundtruth annotation
        :param niceColors: plot nice gradient colors for each joint
        :param basePath: if not None this path is used as base-path instead of
                         the default path (self.outputPath)
        :return: None, or image if name = None
        """

        if basePath is None:
            basePath = self.outputPath

        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.axis('off')
        # plot depth image with annotations
        if showDepth:
            imgcopy = dpt.copy()
            # display hack to hide nd depth
            msk = imgcopy > 0
            msk2 = imgcopy == 0
            if msk.max() == True:
                min = imgcopy[msk].min()
                max = imgcopy[msk].max()
                if (max - min) > 0:
                    imgcopy = (imgcopy - min) / (max - min) * 255.
            imgcopy[msk2] = 255.
            ax.imshow(imgcopy, cmap='gray')
        else:
            # same view as with image
            ax.set_xlim([0, dpt.shape[0]])
            ax.set_ylim([0, dpt.shape[1]])
            plt.gca().set_aspect('equal', adjustable='box')
            plt.gca().invert_yaxis()
        # use child class plots
        if showJoints:
            self.plotJoints(ax, joint, color=('r' if niceColors is False else 'nice'),
                            jcolor=('r' if niceColors is False else None),
                            connection_linewidth=connection_linewidth)  # ours
        if showGT:
            self.plotJoints(ax, gtcrop, color=('b' if niceColors is False else 'nice'),
                            jcolor=('b' if niceColors is False else None),
                            connection_linewidth=connection_linewidth)  # groundtruth
        plt.tight_layout(pad=0)
        plt.show(block=False)
        if name is not None:
            fig.savefig(os.path.join(basePath,'annotated_{}.png'.format(name)), bbox_inches='tight')
            plt.close(fig)
        else:
            # If we haven't already shown or saved the plot, then we need to draw the figure first...
            fig.patch.set_facecolor('w')
            fig.canvas.draw()
            # Now we can save it to a numpy array.
            data = numpy.fromstring(fig.canvas.tostring_rgb(), dtype=numpy.uint8, sep='')
            data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))
            plt.close(fig)
            return data

    def plotJoints(self, ax, joint, color='nice', jcolor=None, connection_linewidth=2.0):
        """
        Plot connected joints
        :type ax: axis to plot on
        :type joint: joints to connect
        :type color: line color
        """
        # Draw skeleton
        for i in range(len(self.jointConnections)):
            ax.plot(numpy.hstack((joint[self.jointConnections[i][0], 0], joint[self.jointConnections[i][1], 0])),
                    numpy.hstack((joint[self.jointConnections[i][0], 1], joint[self.jointConnections[i][1], 1])),
                    c=(color if color is not 'nice' else self.jointConnectionColors[i]),
                    linewidth=connection_linewidth)
        # Draw joints
        color_index = 0
        for i in range(joint.shape[0]):
            idx = color_index % len(self.jointcolors)
            ax.scatter(joint[i, 0], joint[i, 1],
                       c=(self.jointcolors[idx] if jcolor is None else jcolor),
                       marker='.', s=400)
            color_index += 1

    def writeResults2Textfile(self, filepath):
        with open(filepath, 'w') as f:
            f.write("Mean-error(mm): {}\n".format(self.getMeanError()))
            f.write("Max-error(mm): {}\n".format(self.getMaxError()))
            f.write("Std-error(mm): {}\n".format(self.getStdError()))
            dists = [20, 30, 40, 50, 60, 70, 80]
            for d in dists:
                f.write("MD-score({}mm): {}\n".format(d, self.getMDscore(d)))
            f.write("Joint-Mean-error(mm): {}\n".format(
                [self.getJointMeanError(j) for j in range(self.gt[0].shape[0])]))
            f.write("Joint-Max-error(mm): {}\n".format(
                [self.getJointMaxError(j) for j in range(self.gt[0].shape[0])]))
            for d in dists:
                f.write("JSAuC({}mm): {}\n".format(d, self.getJointSuccesAuC(d)))

class NYUHandposeEvaluation(HandposeEvaluation):
    """
    Different evaluation metrics for handpose specific for NYU dataset
    """

    def __init__(self, gt, joint, joints=NyuAnnoType.EVAL_JOINTS_ORIGINAL):
        """
        Initialize class

        :type gt: groundtruth joints
        :type joint: predicted joints
        """

        super(NYUHandposeEvaluation, self).__init__(gt, joint)
        import matplotlib

        # setup specific stuff
        if joints == NyuAnnoType.ALL_JOINTS:
            self.jointNames = ('P1', 'P2', 'P3', 'P4', 'P5', 'P6',
                               'R1', 'R2', 'R3', 'R4', 'R5', 'R6',
                               'M1', 'M2', 'M3', 'M4', 'M5', 'M6',
                               'I1', 'I2', 'I3', 'I4', 'I5', 'I6',
                               'T1', 'T2', 'T3', 'T4', 'T5',
                               'C1', 'C2', 'C3',
                               'W1', 'W2', 'W3', 'W4')
            self.jointConnections = [[33, 5], [5, 4], [4, 3], [3, 2], [2, 1], [1, 0],
                                     [32, 11], [11, 10], [10, 9], [9, 8], [8, 7], [7, 6],
                                     [32, 17], [17, 16], [16, 15], [15, 14], [14, 13], [13, 12],
                                     [32, 23], [23, 22], [22, 21], [21, 20], [20, 19], [19, 18],
                                     [34, 29], [29, 28], [28, 27], [27, 26], [26, 25], [25, 24],
                                     [34, 32], [34, 33], [33, 32],
                                     [34, 30], [34, 31], [35, 30], [35, 31]]
            self.jointConnectionColors = [matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.2]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.3]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.4]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 1]]]))[0, 0],
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.2]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.3]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.4]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 1]]]))[0, 0],
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.2]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.3]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.4]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 1]]]))[0, 0],
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.2]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.3]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.4]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 1]]]))[0, 0],
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.2]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.3]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.4]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 1]]]))[0, 0],
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.0]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.0]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.0]]]))[0, 0],
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.16, 1, 0.7]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.16, 1, 0.7]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.16, 1, 1.0]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.16, 1, 1.0]]]))[0, 0]]

        elif joints == NyuAnnoType.EVAL_JOINTS_ORIGINAL:
            self.jointNames = ('P1', 'P2', 'R1', 'R2', 'M1', 'M2',
                               'I1', 'I2', 'T1', 'T2', 'T3', 'W1', 'W2', 'C')
            # re-ordered joint conncections to better correspond to joints (fingers from tip to "mcp"-joint)
            self.jointConnections = [[1, 0], [13, 1], # pinky
                                     [3, 2], [13, 3], # ring
                                     [5, 4], [13, 5], # middle
                                     [7, 6], [13, 7], # index
                                     [9, 8], [10, 9], # thumb
                                     [13, 10], [13, 11], [13, 12]] # palm
            self.jointConnectionColors = [matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 1]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.7]]]))[0, 0], # pinky
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 1]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.7]]]))[0, 0], # ring
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 1]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.7]]]))[0, 0], # middle
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 1]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.7]]]))[0, 0], # index
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 1]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.6]]]))[0, 0], # thumb
                                          matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.16, 1, 0.7]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.16, 1, 1]]]))[0, 0]]
            self.jointcolors = self.jointConnectionColors + [self.jointConnectionColors[-1]]

        else:
            raise ValueError("Unknown annotation type")
        self.plotMaxJointDist = 80
        self.VTKviewport = [0, 0, 0, 180, 40]
        self.fps = 25.0

class ICGHandposeEvaluation(HandposeEvaluation):
    """
    Evaluation for specific annotation (24 annotated joint positions)
    """

    def __init__(self, gt, joints):
        """
        Initialize class

        :type gt: groundtruth joints
        :type joints: calculated joints
        """

        super(ICGHandposeEvaluation, self).__init__(gt, joints)
        import matplotlib

        # setup specific stuff
        self.jointNames = ( 'PalmT',                    # palm (thumb)
                            'T1', 'T2', 'T3',           # thumb
                            'PalmI',                    # palm (index)
                            'I1', 'I2', 'I3', 'I4',     # index
                            'PalmM',                    # palm (middle)
                            'M1', 'M2', 'M3', 'M4',     # middle
                            'PalmR',                    # palm (ring)
                            'R1', 'R2', 'R3', 'R4',     # ring
                            'PalmP',                    # palm (pinky)
                            'P1', 'P2', 'P3', 'P4',     # pinky
                            )
        self.jointConnections = [[0, 1], [1, 2], [2, 3],                    # thumb
                                 [4, 5], [5, 6], [6, 7], [7, 8],            # index
                                 [9, 10], [10, 11], [11, 12], [12, 13],     # middle
                                 [14, 15], [15, 16], [16, 17], [17, 18],    # ring
                                 [19, 20], [20, 21], [21, 22], [22, 23]]    # pinky
        self.jointConnectionColors = [matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.00, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.33, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.50, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 0.8]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.66, 1, 1]]]))[0, 0],
                                      matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.6]]]))[0, 0], matplotlib.colors.hsv_to_rgb(numpy.asarray([[[0.83, 1, 0.8]]]))[0, 0]]

        self.plotMaxJointDist = 80
        self.VTKviewport = [0, 0, 180, 40, 40]
        self.fps = 10.0


import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import cairocffi
import scanpy as sc
import scipy.io as scio
import csv
import scipy.sparse as sp
from scipy.sparse import isspmatrix
from matplotlib.backends.backend_pdf import PdfPages

def read_and_filter_grn(grn_path, var=0, ci=0, net=None):
    grn = pd.read_table(grn_path, header=0)
    grn['id'] = grn[['regulator', 'target']].apply(lambda x: '{}_{}'.format(
        x[0], x[1]), axis=1)
    if var > 0:
        grn = grn[grn['var.exp.median'] >= var]
    if ci > 0:
        grn = grn[grn['combined_confidences.values'] >= ci]
    if net is not None:
        grn['net'] = net

    return grn

def shared_edges(grns):
    edges = [grns[x].id for x in grns]
    ret = set(edges[0])
    for x in range(len(edges)-1, 0, -1):
        ret = ret & set(edges[x]) & set(edges[x-1])
    return ret

def shared_edges_variance(grns, var):
    out = []
    for v_ in var:
        sub = {}
        for x in grns:
            grn = grns[x]
            sub[x] = grn[grn['var.exp.median'] >= v_]

        out.append(len(shared_edges(sub)))

    return out

def fraction_edges_variance(grns, var):
    pass

def extract_tf(data, TF, obs_key=None, obs_value=None, my_layer='Ms'):
    sub = -1
    if(obs_key is not None and obs_key in data.obs.keys()):
        data = data[data.obs[obs_key].isin([obs_value])]
        if(isspmatrix(data.layers[my_layer])):
            data.layers[my_layer] = data.layers[my_layer].todense()
        sub = pd.DataFrame(data.layers[my_layer][:,
                           data.var.index.isin(TF[0])])
        sub.columns = data.var.index[data.var.index.isin(TF[0])]
        sub.index = data.obs.index
    return(sub)

def load_expression(paths):
    adatas = {k.split('_')[0]: sc.read_h5ad(paths[k]) for k in paths if
              '_exp' in k}
    adatas['P2'].obs['cardinal_class'] = adatas['P2'].obs['CellType']
    for tp in adatas:
        adatas[tp] = adatas[tp][:, adatas[tp].var.pct_dropout_by_counts != 100]
        adatas[tp] = adatas[tp][adatas[tp].obs.cardinal_class.isin(['PV',
                                                                   'SST'])]
        sc.tl.rank_genes_groups(adatas[tp], groupby='cardinal_class',
                                rankby_abs=True, pts=True, use_raw=False,
                                layers='normed', method='wilcoxon',
                                n_genes=adatas[tp].shape[1],key_added='DE')

    return adatas

def targets_by_var(grn, var=.05, tfs=None):
    tmpg = grn
    if tfs is not None:
        tmpg = grn[grn.regulator.isin(tfs.index.values)]
    tmpg = tmpg[tmpg['var.exp.median'] >= var]
    return(len(set(tmpg.target)))

def shared_targets(grns):
    edges = [grns[x].target for x in grns]
    ret = set(edges[0])
    for x in range(len(edges)-1, 0, -1):
        ret = ret & set(edges[x]) & set(edges[x-1])
    return(ret)

def shared_targets_by_var(grns, by_var=.05):
    sub = {}
    for x in grns:
        grn = grns[x]
        sub[x] = grn[grn['var.exp.median'] >= by_var]
    return(shared_targets(sub))

def get_de_info(adatas, seltf):
    dedf = {}
    for tp in adatas:
        dedf[tp] = sc.get.rank_genes_groups_df(adatas[tp], group="PV",key='DE')
        gene_ids = adatas[tp].var.index.values
        if(isspmatrix(adatas[tp].layers['normed'])):
            obs = adatas[tp].layers['normed'].todense()
        else:
            obs = adatas[tp].layers['normed'].copy()
        obs = pd.DataFrame(obs, columns=gene_ids,
                           index=adatas[tp].obs['cardinal_class'])
        obs_bool = obs.astype(bool)
        fraction_obs = obs_bool.groupby(level=0).sum()
        fraction_obs /= obs_bool.groupby(level=0).count()
        mean_obs = obs.groupby(level=0).mean()
        mean_obs.loc['SST'][mean_obs.loc['SST'] == 0] = 1
        df = np.log2(mean_obs.loc['PV']/mean_obs.loc['SST'])
        mean_obs = pd.DataFrame(df)
        mean_obs.columns = ['l2']
        dedf[tp] = dedf[tp].merge(fraction_obs.T, left_on='names',
                                  right_index=True)
        dedf[tp] = dedf[tp].merge(mean_obs, left_on='names', right_index=True)
        dedf[tp] = dedf[tp][dedf[tp].names.isin(seltf[tp])]
    return dedf

def get_non_specific_tf(dedf, lfc=.25, pval=.05, fpv=.1, fsst=.1):
    return dedf[((abs(dedf.l2) <= lfc) |
                ((abs(dedf.l2) > lfc) &
                 (dedf.pvals > pval))) &
                ((dedf.PV >= fpv) |
                 (dedf.SST >= fsst))].names.values.tolist()

def compute_edge_fractions(f_grns):
    out_reg_s = {}
    tp = set([x.split('_')[0] for x in f_grns])
    for t in tp:
        report = {}
        network_a, network_b = [x for x in f_grns.keys() if
                                t == x.split('_')[0]]
        networks = [network_a, network_b]
        cell_type_a, cell_type_b = [x.split('_')[1] for x in networks]
        f_net_a = f_grns[network_a]
        f_net_b = f_grns[network_b]
        for regulator in set(set(f_net_a.regulator) | set(f_net_b.regulator)):
            n = 0
            edge_id = []
            if regulator in f_net_a.regulator.values:
                n = f_net_a.regulator.value_counts()[regulator]
                edge_id = list(f_net_a[f_net_a.regulator == regulator].id.values)
            report[regulator] = {cell_type_a: {'n': n, 'edge_id': edge_id}}
            n = 0
            edge_id = []
            if regulator in f_net_b.regulator.values:
                n = f_net_b.regulator.value_counts()[regulator]
                edge_id = list(f_net_b[f_net_b.regulator == regulator].id.values)

            report[regulator][cell_type_b] = {'n': n, 'edge_id': edge_id}
            s_edges = set(set(report[regulator][cell_type_b]['edge_id']) &
                          set(report[regulator][cell_type_a]['edge_id']))
            report[regulator]['shared_id'] = len(s_edges)
            divider = report[regulator][cell_type_a]['n']
            if divider == 0:
                divider = 1
            report[regulator][cell_type_a]['prop_s'] = report[regulator]['shared_id'] / divider
            report[regulator][cell_type_a]['prop_u'] = None
            if report[regulator][cell_type_a]['n'] > 0:
                report[regulator][cell_type_a]['prop_u'] = abs(1 - report[regulator][cell_type_a]['prop_s'])
            divider = report[regulator][cell_type_b]['n']
            if divider == 0:
                divider = 1
            report[regulator][cell_type_b]['prop_s'] = report[regulator]['shared_id'] / divider
            report[regulator][cell_type_b]['prop_u'] = None
            if report[regulator][cell_type_b]['n'] > 0:
                report[regulator][cell_type_b]['prop_u'] = abs(1 - report[regulator][cell_type_b]['prop_s'])
        out_reg_s[t] = report

    rpl = {}

    for t in tp:
        rpl[t] = pd.DataFrame(zip([out_reg_s[t][x]['PV']['prop_u'] for x in out_reg_s[t].keys()],
                                  [out_reg_s[t][x]['SST']['prop_u'] for x in out_reg_s[t]]))
        rpl[t].columns = ['PV', 'SST']
        rpl[t].index = out_reg_s[t].keys()

    return rpl

def read_betas(t, cardinal_class, boot_path, n_boot=10):
    return {'boot_{}'.format(b): pd.read_table(
            boot_path.format(t=t, b=b),
            index_col=0,header=0) for b in range(1, n_boot+1)}

def B_dot_A(B, A, n_boot):
    acc = B['boot_1']
    for i in range(2, n_boot+1):
        acc += B['boot_{}'.format(i)]
    return acc.dot(A.T) / n_boot

def write_reconstructed_exp(cell_names, gene_names, paths, exp):
    scio.mmwrite(paths['mtx'], sp.csr_matrix(exp.to_numpy()))
    with open(paths['genes'], 'w', newline='') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter='\t')
        spamwriter.writerow(gene_names)
    with open(paths['cells'], 'w', newline='') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter='\t')
        spamwriter.writerow(cell_names)


from cmath import sqrt

def intervalo(centro, raio, altura, largura):
    coord = [0]*2
    dx = abs(sqrt(raio**2 - ((altura**2)/4)))
    coord[0] = centro - dx
    coord[1] = centro + dx
    if(coord[0] < 0):
        coord[0] = 0
    if(coord[1] > largura):
        coord[1] = largura
    return coord

def minimo(n, l, intervals):

    resultado = 0
    ultimo = 0
    i = -1
    while i < n - 1:
        j = i + 1
        if intervals[j][0] > ultimo:
            return -1
        primeiro = ultimo
        while j < n:
            if intervals[j][0] <= ultimo:
                primeiro = max(primeiro, intervals[j][1])
                i += 1
            j += 1
        ultimo = primeiro
        resultado += 1
        if ultimo >= l:
            return resultado
    return resultado if ultimo >= l else -1

if '__name__' == '__main__':

    while True:
        try:
            entrada =  list(map(int,input().split()))
            qtde = entrada[0]
            largura = entrada[1]
            altura = entrada[2]
            irr = []
            intervalos = []
            for i in range(qtde):
                irr.append(list(map(int,input().split())))
                if(irr[i][1] >= altura/2):
                    intervalos.append(intervalo(irr[i][0],irr[i][1],altura, largura))
            intervalos.sort()
            #print(intervalos)
            print(minimo(len(intervalos), largura, intervalos))

        except EOFError:
            break


# import pandas as pd
# import re
# from ckipnlp.pipeline import CkipPipeline, CkipDocument

# stop_pos = ['COLONCATEGORY','COMMACATEGORY','DASHCATEGORY','DOTCATEGORY','ETCCATEGORY','EXCLAMATIONCATEGORY','PARENTHESISCATEGORY','PAUSECATEGORY','PERIODCATEGORY','QUESTIONCATEGORY','SEMICOLONCATEGORY','SPCHANGECATEGORY','FW']
# text = "◾花生餅（$15）：餅皮帶著淡淡的麵粉香，口感上比較紮實，花生口味的內陷讓人驚艷，花生很濃郁，可以微微吃到沙沙的口感，也因為餅皮比較紮實，讓整體的甜度中和了不少，整體來說滿推薦的！是路過可以買的小點心，吃了會心情很好🤩🫶(蔥花)，原本以為是青蔥，結果不是，是油蔥，一點點鹹味也不錯吃。是一家難得的超值饅頭店！必買！"

# pipeline = CkipPipeline()
# doc = CkipDocument(raw=text)
# pipeline.get_ws(doc)
# pipeline.get_pos(doc)

# ws = doc.ws.to_text()[0].split('\u3000')
# pos = doc.pos.to_text()[0].split('\u3000')

# xxxxxx = doc.ws[0].to_text()
# print(xxxxxx)
# print(type(xxxxxx))

# # for i,j in zip(ws,pos):
# #   print('('+i,j+')',end='')
# # print('')

# for x in range(len(pos)-1,-1,-1):
#   if pos[x] in stop_pos :
#     # print(ws.pop(x),end = ' ')
#     # print(pos.pop(x))
#     ws.pop(x)
#     pos.pop(x)

# # for i,j in zip(ws,pos):
# #   print('('+i,j+')',end='')
# # print('')

# clearnws = ''
# clearnpos = ''
# for i,j in zip(ws,pos):
#   clearnws = clearnws + '\u3000' + i
#   # clearnpos.append(j)

import pandas as pd
import re
from ckipnlp.pipeline import CkipPipeline, CkipDocument

datanum = 1
pipeline = CkipPipeline()
pattern = r'\bDfa\s+VK|D\s+VC|VH|VHC|VI\b'
rows = pd.read_csv(r'D:\vscode\python\reviews_rating\CSV\訓練集\data45.csv')
stop_pos = ['COLONCATEGORY','COMMACATEGORY','DASHCATEGORY','DOTCATEGORY','ETCCATEGORY','EXCLAMATIONCATEGORY','PARENTHESISCATEGORY','PAUSECATEGORY','PERIODCATEGORY','QUESTIONCATEGORY','SEMICOLONCATEGORY','SPCHANGECATEGORY','FW']

for index, row in rows.iterrows():
    review = row['Reviews']
    doc = CkipDocument(raw=review)
    pipeline.get_ws(doc)
    pipeline.get_pos(doc)

    # 預處理
    ws = doc.ws.to_text()[0].split('\u3000')
    pos = doc.pos.to_text()[0].split('\u3000')
    clearnws = ''
    clearnpos = ''
    for x in range(len(pos)-1,-1,-1):
      if pos[x] in stop_pos :
        ws.pop(x)
        pos.pop(x)
    for i,j in zip(ws,pos):
      clearnws = clearnws + '\u3000' + i
      clearnpos = clearnpos + '\u3000' + j
    clearnws = clearnws[1:]
    clearnpos = clearnpos[1:]

    #算字數
    chinese_chars = re.findall(u"[\u4e00-\u9fff]", clearnws)
    chars = len(chinese_chars)

    #算形容次數
    matches = re.findall(pattern, clearnpos)
    count = len(matches)

    if chars >= 24:
        if count <= 8:
            chat_score = count * 1
        elif count > 8:
            chat_score = 8 * 1
    elif chars < 24:
        if count <= 8:
            chat_score = count * 0.8
    total = chat_score

    nws = clearnws.replace('\u3000', '|')
    rows.loc[index,['ckipnlp','score']] = [nws,total]
    datanum+=1

    if datanum%100 == 0:
        print("已分析{}筆".format(datanum))
rows.to_csv(r"D:\vscode\python\reviews_rating\CSV\訓練集\data45.csv", index=False)
print("處理了{}筆資料".format(datanum-1))


from datetime import datetime, timedelta
import os
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from airflow.operators import FeatureExtractorOperator
from airflow.operators import FeatureLabelOperator
from airflow.operators import FeatureVerificationOperator
from airflow.operators import CsvVerificationOperator
from airflow.operators import s3UploadOperator

default_args = {
    'owner': 'ahmad-chaiban',
    'start_date': datetime(2019, 1, 12),
    'depends_on_past': False,
    'email': ['ahmadchaiban@gmail.com'],
    'email_on_retry': True,
    'retries': 3,
    'retry_delay': timedelta(minutes=5),
    'catchup': False
}

dag = DAG(
    'image_pipeline',
    default_args=default_args,
    description='Extract features from images, create, load, transform and upload to DB',
    schedule_interval='0 * * * *'
)

start_operator = DummyOperator(
    task_id='Begin_execution',
    dag=dag
)

extract_img_features_plant = FeatureExtractorOperator(
    task_id = 'feature_extraction_plant',
    dag = dag,
    path_features = '../../../../capstone data/imgFeatures/*/*.*',
    path_images = '../../../../capstone data',
    path_output = '../../../../capstone data/imgFeatures/',
    category = 'plant'
)

extract_img_features_animal = FeatureExtractorOperator(
    task_id = 'feature_extraction_animal',
    dag = dag,
    path_features = '../../../../capstone data/imgFeatures/*/*.*',
    path_images = '../../../../capstone data',
    path_output = '../../../../capstone data/imgFeatures/',
    category = 'animal'
)

extract_img_features_human = FeatureExtractorOperator(
    task_id = 'feature_extraction_human',
    dag = dag,
    path_features = '../../../../capstone data/imgFeatures/*/*.*',
    path_images = '../../../../capstone data',
    path_output = '../../../../capstone data/imgFeatures/',
    category = 'human'
)

preVerificationStandbyOperator = DummyOperator(
    task_id='Verification_Standby',
    dag=dag
)

Feature_verify_plant = FeatureVerificationOperator(
    task_id = 'feature_verify_plant',
    dag = dag,
    path = '../../../../capstone data/imgFeatures',
    category = 'plant'
)

Feature_verify_animal = FeatureVerificationOperator(
    task_id = 'feature_verify_animal',
    dag = dag,
    path = '../../../../capstone data/imgFeatures',
    category = 'animal'
)

Feature_verify_human = FeatureVerificationOperator(
    task_id = 'feature_verify_human',
    dag = dag,
    path = '../../../../capstone data/imgFeatures',
    category = 'human'
)

preDataStorageStandbyOperator = DummyOperator(
    task_id='CSV_Storage_Standby',
    dag=dag
)

create_csv_plant = FeatureLabelOperator(
    task_id = 'feature_labeler_plant',
    dag = dag,
    main_path = '../../../../capstone data/imgFeatures',
    category = 'plant'
)

create_csv_animal = FeatureLabelOperator(
    task_id = 'feature_labeler_animal',
    dag = dag,
    main_path = '../../../../capstone data/imgFeatures',
    category = 'animal'
)

create_csv_human = FeatureLabelOperator(
    task_id = 'feature_labeler_human',
    dag = dag,
    main_path = '../../../../capstone data/imgFeatures',
    category = 'human'
)

preCSVVerificationStandbyOperator = DummyOperator(
    task_id='CSV_Verification_Standby',
    dag=dag
)

csv_verification_plant = CsvVerificationOperator(
    task_id = 'plant_csv_verification',
    dag = dag,
    path = '../../../dataFunctions/final_data',
    category = 'plant'

)

csv_verification_animal = CsvVerificationOperator(
    task_id = 'animal_csv_verification',
    dag = dag,
    path = '../../../dataFunctions/final_data',
    category = 'animal'

)

csv_verification_human = CsvVerificationOperator(
    task_id = 'human_csv_verification',
    dag = dag,
    path = '../../../dataFunctions/final_data',
    category = 'human'

)

preUploadStandbyOperator = DummyOperator(
    task_id = 'Pre_upload_Standby',
    dag = dag
)

s3_upload_plant = s3UploadOperator(
    task_id = 'plant_upload',
    dag = dag,
    path = '../../../dataFunctions/final_data',
    category = 'plant',
    bucket_name = 'capstone-project-2187'
)

s3_upload_animal = s3UploadOperator(
    task_id = 'animal_upload',
    dag = dag,
    path = '../../../dataFunctions/final_data',
    category = 'animal',
    bucket_name = 'capstone-project-2187'
)

s3_upload_human = s3UploadOperator(
    task_id = 'human_upload',
    dag = dag,
    path = '../../../dataFunctions/final_data',
    category = 'human',
    bucket_name = 'capstone-project-2187'
)

end_operator = DummyOperator(
    task_id='end_execution',
    dag=dag
)

start_operator >> [extract_img_features_plant, extract_img_features_animal, extract_img_features_human] >> preVerificationStandbyOperator

preVerificationStandbyOperator >> [Feature_verify_plant, Feature_verify_animal, Feature_verify_human] >> preDataStorageStandbyOperator

preDataStorageStandbyOperator >> [create_csv_plant, create_csv_animal, create_csv_human] >> preCSVVerificationStandbyOperator

preCSVVerificationStandbyOperator >> [csv_verification_plant, csv_verification_animal, csv_verification_human] >> preUploadStandbyOperator

preUploadStandbyOperator >> [s3_upload_plant, s3_upload_animal, s3_upload_human] >> end_operator


#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""

@Author : kazi
@File : edge_lists.py
@Time : 4/20/22 1:02 PM
@Desc:
  2
 / \
1   3
edge_list = [[1,2] [2,3] [3,1]]
"""

class Graph:
    """
    A Graph Class
    """

    def __init__(self, edge_list, num_of_nodes):
        self.adjacency_list = [[] for _ in range(num_of_nodes)]
        for origin, dest in edge_list:
            self.adjacency_list[origin].append(dest)

def print_graph(graph):
    """
    Print the graph
    :param graph:
    :return:
    """
    for origin in range(len(graph.adjacency_list)):
        for dest in graph.adjacency_list[origin]:
            print(f"{origin} —> {dest} ", end="")
        print()

def main():
    """
    Main function for run the the code
    :return:
    """
    edge_list = [(0, 1), (1, 2), (2, 3), (0, 2), (3, 2), (4, 5), (5, 4)]
    num_of_nodes = 6
    graph = Graph(edge_list, num_of_nodes)
    print_graph(graph)

if __name__ == "__main__":
    main()


def add(a,b) : return a+b
def sub(a,b) : return a-b
def mul(a,b) : return a*b
def div(a,b) : return a/b

def main() :
    print("first operand is 0")
    N = 0
    operator = "1"
    while 1:
        operator = input("operator(+, -, *, /, exit)\n >> ")
        caninput = ["+", "-", "*", "/", "exit"]
        while operator not in caninput :
            operator = input("worng input\noperator(+, -, *, /, exit)\n >> ")
        if operator == "exit" : break;
        M = float(input("operand\n >> "))
        if operator == "+" :
            N = add(N,M)
        elif operator == "-" :
            N = sub(N,M)
        elif operator == "*" :
            N = mul(N,M)
        elif operator == "/" :
            N = div(N,M)
        print("result")
        print("-"*10)
        print(">>> " + str(N))
        print("-"*10)
    print("Terminate")

def callmain() :
    if __name__ == '__main__' :
        main()

def fibo(N) :
    if N==0 : return 0
    if N==1 : return 1
    return fibo(N-1) + fibo(N-2)

def printfibo() :
    N = int(input("fibo\n"))
    for i in range(N+1) :
        print("Fibonacci(" + str(i) + ")=" + str(fibo(i)))

def lists() :
    import random
    a = []
    for i in range(50) :
        a.append(random.randint(0, 1000))
    maxnum = -1
    minnum = 1001
    for i in a :
        print(i, end=' ')
        if maxnum < i : maxnum=i
        if minnum > i : minnum=i
    for i in range(len(a)) :
        for j in range(i+1, len(a)) :
            if a[i] > a[j] :
                a[i],a[j] = a[j],a[i];
    print()
    print("maxvalue = " + str(maxnum))
    print("minvalue = " + str(minnum))
    for i in a :
        print(i, end=' ')
    print()

def snail() :
    N = int(input("snail\n"))
    a = []
    for i in range(N+2) :
        temp = []
        for j in range(N+2) :
            temp.append(0)
        a.append(temp)
    cnt = 1
    x = -1
    y = 0
    while cnt<=N*N :
        while not a[x+1][y] and x<N-1 :
#         print(x, y, cnt)
            x+=1
            a[x][y] = cnt
            cnt+=1
#        x-=1
        while not a[x][y+1] and y<N-1 :
#          print(x, y, cnt)
            y+=1
            a[x][y] = cnt
            cnt+=1
#       y-=1
        while not a[x-1][y] and x>0 :
#           print(x, y, cnt)
            x-=1
            a[x][y] = cnt
            cnt+=1
#      x+=1
        while not a[x][y-1] and y>0 :
            y-=1
#            print(x, y, cnt)
            a[x][y] = cnt
            cnt+=1
#     y+=1
    for i in range(N) :
        for j in range(N) :
            print(a[j][i], end=' ')
        print()

def test() :
    N = int(input("input 1~4, 0 to exit\n"))
    F = [0, callmain, printfibo, lists, snail]
    while N :
        F[N]()
        N = int(input())

test()


import math

inf  = 10**9
def timeToZero( wr, ri, pr ):
       p = 100
       y = 0
       wr = wr
       ri = (1 + ri/100.0)
       pr = (1 + pr/100.0)
       #pdb.set_trace()
       pfr = [] # pf return  post withdrawal each year
       p  = p -wr
       while( p > 0 ):
          p1 = p*pr
          p2 = p1 - wr
          pfr.append( p2*1.0/p )
          if ( len(pfr) > 1 ):
              # check if slope is negative
              if ( pfr[-1] > pfr[-2] ) :
                 return inf
          p = p2
          wr = wr*ri
          y = y+1
       return y

def timeToZeroFormula ( wr, ri , pr ) :
    # ri -> rate of increase of withdrawals annualy
    # pr -> constant portfolio returns
    # wr -> wr % per year starting from year i1
     ri = ri * 1.0/100
     pr = pr * 1.0/100
     wr = wr * 1.0/100
     if (pr-ri)>= wr:
         return  inf
     return int ( math.log( 1 - ((pr-ri)/wr))/math.log((1+ri)/(1+pr)) )

def Swr(  pr, ri   ) :
    # calulcate the SWR given pr, ri
    # pr -> constant portfolio returns
    # ri -> rate of increase of withdrawals annualy
    wrMin = 0
    wrMax = 100
    c = 0
    while ( 1 ) :
       c = c+1
       tmin = timeToZero( wrMin, ri, pr )
       tmax =  timeToZero( wrMax , ri, pr )
       wrMid = (wrMax+ wrMin)/2.0
       tmid  = timeToZero( wrMid , ri, pr )
       import pdb
       #pdb.set_trace()
       #print( locals() )
       if (tmin == inf and  tmax < inf ):
           if tmid == inf:
              wrMin  =  wrMid
           else:
              wrMax = wrMid

       if ( (wrMax-wrMin) <= 0.02  or c>50):
            #print( " {}  {}".format( wrMin,wrMax) )
            #pdb.set_trace()
            return (wrMax)

#for pr in np.arange(8,12,0.1):
    #print ( " pr {} , TimeToZero = {} years ".format( pr , timeToZero( wr,ri,pr) ))
#print ( timeToZero( 2.22,5,7) )
#print ( Swr( 11, 8 ) )
print(  "Swr for (8% pf , 6% wr increase a year ) = {} % of intial  PF ".format( Swr ( 8,6) ) )
print ( "PF growing at 6%, with withdrwals growing at 7%/yr and initial widhdrawal rate at 3% of PF lasts for {} years ".format( timeToZero( 3 ,7, 6 ) ) )
print( "PF growing at 7%,  with withdrawals growing at 8%/yr and initial withdrwal rate at 4% of PF lasts for {} years ".format( timeToZeroFormula( 4 ,8, 7 ) ) )


from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
from selenium.webdriver.chrome.options import Options
from Pages.Login import LoginPage
from Pages.Desktop import Desktop

import unittest

o = Options()
o.add_argument('--no-sandbox')

s = Service('d:\chromedriver_win32\chromedriver.exe')
# driver = webdriver.Chrome(ChromeDriverManager().install())

class Test_Pinvest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.driver = webdriver.Chrome(service=s, options=o)
        cls.driver.maximize_window()
        cls.driver.implicitly_wait(5)

    def test01_login_register(self):
        self.driver.get("https://admin-ha1.pinvest.ir/signin")
        login = LoginPage(driver=self.driver)
        login.enter_username("superadmin")
        login.enter_password("1234kaKA!")
        login.enter_show_password()
        login.enter_btn_submit()
        sleep(3)
        login.enter_site_header()
        # login.enter_logout()
        # login.enter_logout_no()
        # login.enter_logout()
        # login.enter_logout_yes()

# # # desktop

    def test02_desktop(self):
        desktop = Desktop(driver=self.driver)
        desktop.enter_click_desktop()
        # assert desktop
        desktop.enter_desktop_show()
        desktop.enter_desktop_sub_percent()
        desktop.enter_desktop_sub_value()
        desktop.enter_desktop_sub_active_units()
        desktop.enter_desktop_sub_price_unit()
        desktop.enter_desktop_all_users()
        desktop.enter_desktop_direct_customers()
        desktop.enter_desktop_indirect_customers()

    @classmethod
    def tearDownClass(cls) -> None:
        sleep(5)
        cls.driver.close()
        cls.driver.quit()


import os

import discord
from dotenv import load_dotenv
from discord.ext import commands

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(command_prefix='!')

@bot.command(name='close')
async def close(ctx):
    await ctx.message.delete()
    await bot.close()

@bot.command(name='add')
async def add(ctx, *args):
    await ctx.message.delete()
    if len(args) < 3:
        await ctx.send("expected command: !add \"section\" \"band\” \"music1\" \"music2\" ...")
    else:
        lst_bands = await create_band_list(args[0], await ctx.channel.history(limit=200).flatten())
        add_music(lst_bands, args[1], args[2:])
        for msg in create_messages(args[0], lst_bands):
            await ctx.send(msg)

@bot.command(name='rm')
async def remove(ctx, *args):
    await ctx.message.delete()
    if len(args) != 3:
        await ctx.send("expected command: !rm \"section\" \"band\” \"music\"")
    else:
        lst_bands = await create_band_list(args[0], await ctx.channel.history(limit=200).flatten())
        remove_music(lst_bands, args[1], args[2:])
        for msg in create_messages(args[0], lst_bands):
            await ctx.send(msg)

@bot.command(name='redo')
async def redo(ctx):
    await ctx.message.delete()
    lst_bands = await create_band_list_from_java(await ctx.channel.history(limit=200).flatten())
    for msg in create_messages("7 cordes", lst_bands):
        await ctx.send(msg)

def is_in_tuple_list(lst, key):
    for list_key, obj in lst:
        if list_key == key:
            return True
    return False

async def create_band_list_from_java(messages):
    lst_bands = []

    for msg in messages:
        if "```java\n" in msg.content:
            lines = msg.content.splitlines()
            for line in lines[3:-1]:
                if line != "":
                    line = line.split()
                    if len(line) > 0:
                        if "()" in line[-1]:
                            name =  ' '.join(line[0:])
                            if not is_in_tuple_list(lst_bands, name[:-2]):
                                lst_bands.append((name[:-2], []))
                        else:
                            lst_bands[-1][1].append(' '.join(line[0:]))

            await discord.Message.delete(msg)

    return lst_bands

async def create_band_list(section, messages):
    lst_bands = []

    for msg in messages:
        if ("```md\n> " + section) in msg.content:
            lines = msg.content.splitlines()
            for line in lines[3:-1]:
                if line != "":
                    line = line.split()
                    if line[0] == '#' and not is_in_tuple_list(lst_bands, line[1]):
                        lst_bands.append((line[1], []))
                    elif line[0] == '*':
                        lst_bands[-1][1].append(' '.join(line[1:]))

            await discord.Message.delete(msg)

    return lst_bands

def add_music(lst_bands, name, lst_musics):
    name_exists = False
    for band_name, band_musics in lst_bands:
        if band_name == name:
            band_musics.extend(x for x in lst_musics if x not in band_musics)
            name_exists = True
            break

    if not name_exists:
        lst_bands.append((name, lst_musics))

def remove_music(lst_bands, name, music):
    for band_name, band_musics in lst_bands:
        if band_name == name:
            if music in band_musics:
                band_musics.remove(music)
            break

def create_messages(section, lst_bands):
    lst_msgs = []

    appended = False
    msg = "```md\n"
    msg += "> " + section + '\n'
    for name, lst_musics in sorted(lst_bands, key=lambda tup: tup[0]):
        next_line = "\n# " + name + "\n"
        if (len(msg) + len(next_line) + len("```")) >= 1990:
            msg += "```"
            lst_msgs.append(msg)
            appended = True
            msg = "```md\n"
            msg += "> " + section + '\n'
        msg += next_line
        for music in sorted(lst_musics):
            next_line = "* " + music + '\n'
            if (len(msg) + len(next_line) + len("```")) >= 1990:
                msg += "```"
                lst_msgs.append(msg)
                appended = True
                msg = "```md\n"
                msg += "> " + section + '\n'
                msg += "\n# " + name + "\n"

            msg += next_line
            appended = False

    if not appended:
        msg += "```"
        lst_msgs.append(msg)

    return lst_msgs

bot.run(TOKEN)


from requests import get
from bs4 import BeautifulSoup
import re
import json

def get_ano(soup):
    ano = soup.select("a[href*=/year/]")
    if len(ano):
        return int(ano[0].text)
    else:
        return None

def get_genero(soup):
    genero = soup.find('div', {'id': 'titleStoryLine'})
    genero = genero.select("a[href*=/search/title?genres]")
    if genero != None:
        return [a.text for a in genero]
    else:
        return None

def get_diretor(soup):
    diretor = soup.find('div', attrs={'class': re.compile("^credit_summary_item")})
    diretor = [elemento.get_text() for elemento in diretor.find_all(re.compile(r'(a)'))]
    if diretor != None:
        return diretor
    else:
        return None

def pegaDuracao(soup):
    durFilme = soup.find('div', {'id': 'titleDetails'})
    durFilme = durFilme.find('time')
    if durFilme != None:
        string = durFilme.text
        return string
    else:
        return None

def dados(mv_containers, teste):
    filme = {
        'link': 'string', 'title': 'string', 'year': 'int', 'categories': 'string',
        'director': 'string', 'duration': 'string'
    }

    info = []

    for x in range(1):
        for container in mv_containers:
            url = 'https://www.imdb.com/' + container.h3.a.attrs['href']
            response = get(url)
            soup = BeautifulSoup(response.text, 'html.parser')

            filme['link'] = url
            filme['title'] = container.h3.a.text
            filme['year'] = get_ano(soup)
            filme['categories'] = get_genero(soup)
            filme['director'] = get_diretor(soup)
            filme['duration'] = pegaDuracao(soup)

            info.append(filme)

        link = teste.find('a', class_='lister-page-next next-page')
        url = 'https://www.imdb.com/' + link.attrs['href']
        response = get(url)
        html_soup = BeautifulSoup(response.text, 'html.parser')
        mv_containers = html_soup.find_all('div', class_='lister-item mode-advanced')
        teste = html_soup.find('div', class_='desc')

    return info

def links(genrs):
    # url de inicio da varredura
    url = 'https://www.imdb.com/search/title?genres=fantasy&explore=title_type,genres&ref_=tt_ov_inf'
    response = get(url)
    html_soup = BeautifulSoup(response.text, 'html.parser')

    ##Pegando as categorias de filmes no site
    categoria_filmes = html_soup.find_all('div', class_='aux-content-widget-2')
    categoria_filmes = categoria_filmes[1].find_all('a')

    # Pegando o link da categoria
    for categoria in categoria_filmes:
        if categoria.text == genrs:
            link = categoria.attrs['href']

    # link da categoria
    url = 'https://www.imdb.com/' + link
    response = get(url)
    html_soup = BeautifulSoup(response.text, 'html.parser')
    mv_containers = html_soup.find_all('div', class_='lister-item mode-advanced')
    teste = html_soup.find('div', class_='desc')

    return json.dumps(dados(mv_containers, teste))


"""
973. K Closest Points to Origin

We have a list of points on the plane.  Find the K closest points to the origin (0, 0).
(Here, the distance between two points on a plane is the Euclidean distance.)

You may return the answer in any order.  The answer is guaranteed to be unique (except for the order that it is in.)
"""
class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        # return self.heap(points, K)
        return self.bySort(points, K)

    # Time complexity: O(nlogn), Space complexity: O(n)
    def heap(self, points, K) -> list:
        if not points or len(points) <= 1:
            return points

        heap = []
        heapq.heapify(heap)

        for point in points:
            distance = self.distanceFromOrigin(point)
            heapq.heappush(heap, (distance, point))

        closest = []
        for i in range(K):
            closest.append(heapq.heappop(heap)[1])

        return closest

    # Time complexity: O(nlogn), Space complexity: O(1)
    def bySort(self, points, K) -> list:
        if not points or len(points) <= 1:
            return points

        points.sort(key=lambda x: self.distanceFromOrigin(x))

        return points[0:K]

    def distanceFromOrigin(self, point) -> int:
        x, y = point
        return x*x + y*y


from rest_framework.views import APIView
from rest_framework import generics
from rest_framework.response import Response
from django.http import HttpResponse
import requests
from .apps import YOUTUBE_API_KEY
from .serializers import ChannelSerializer, GroupSerializer
from .models import Channel, Group

class GreetingView(APIView):
    def get(self, request, format=None):
        return HttpResponse('Hello there')

class GroupView(generics.ListCreateAPIView):
    serializer_class = GroupSerializer
    def get_queryset(self):
        return Group.objects.filter(user=self.request.user)

class GroupDetailView(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = GroupSerializer
    def get_queryset(self):
        return Group.objects.filter(user=self.request.user)

class ChannelView(generics.ListCreateAPIView):
    serializer_class = ChannelSerializer
    def get_queryset(self):
        return Channel.objects.filter(pk__in=self.request.query_params.getlist('ids', ''))

class ChannelDetailView(generics.RetrieveAPIView):
    queryset = Channel.objects.all()
    serializer_class = ChannelSerializer

class SearchView(APIView):
    def get(self, request):
        q = request.query_params.get('q', '')
        url = "https://youtube.googleapis.com/youtube/v3/search?part=snippet&order=relevance&q=" + q + "&type=channel&key=" + YOUTUBE_API_KEY
        res = requests.get(url)
        return HttpResponse(res)

class VideoView(APIView):
    def get(self, request):
        list_ids = request.query_params.getlist('ids', '')
        videos = []
        for id in list_ids:
            url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=" + id + "&key=" + YOUTUBE_API_KEY
            res = requests.get(url)
            for video in res.json()['items']:
                videos.append(video)

        return Response(videos)


import sqlite3,cgi
print("Content-Type:text/html\r\n\r\n")
conn=sqlite3.connect("mydatadb.db")
cur=conn.cursor()
query="create table emplyee(emp_id integer,emp_name text,department text)"
try:
	cur.execute(query)
except Exception as e:
	print(e)
else:
	conn.commit()
	print("created successfully")
finally:
	conn.close()
	cur.close()


import logging
import os
import yaml

from page.app import App
from page.logit import logit

class TestWatchList:
    """
    测试Video 待看列表
    """
    def setup_class(self):
        """
        点击banner图中的添加待看列表按钮，避免没有待看视频 导致没有待看列表按钮
        :return:
        """
        logging.info("TestWatchList:setup_class")
        # 获取测试数据文件名
        testcase_file = os.path.dirname(__file__).split("testcase")[0] + r"data/testcase_data/video/watch_list.yaml"
        # 获取测试数据内容
        self.watch_list_data = yaml.safe_load(open(testcase_file, "r", encoding="utf-8"))
        self.video_page = App.start_logged_in().to_video_home_page()
        # 添加待看视频
        banner_count = self.video_page.get_banner_num()
        self.video_page.add_watch_list(banner_count)
        # 当banner数小于2时，需要另外添加视频
        if banner_count < 2:
            self.video_page.click_movies_tab()
            movie_banner_count = self.video_page.get_banner_num()
            self.video_page.add_watch_list(min(movie_banner_count, 2))
            self.video_page.click_home_tab()
        self.watch_list_page = self.video_page.click_watch_list_more()

    def setup_method(self, method):
        """
        记录日志
        :param method:
        :return:
        """
        logging.info("TestWatchList:setup")
        # 记录类名和方法名
        logging.info(type(self).__name__ + ":" + method.__name__)

    def teardown_class(self):
        logging.info("TestWatchList:teardown_class")
        App.quit()

    @logit()
    def test_title(self):
        """
        测试页面标题
        :return:
        """
        data_list = self.watch_list_data["test_title"]
        title = data_list["title"]
        actual_title = self.watch_list_page.get_toolbar_title()
        assert actual_title == title

    @logit()
    def test_delete_one_video(self):
        """
        测试删除一条待看视频
        :return:
        """
        data_list = self.watch_list_data["test_delete_one_video"]
        toast = data_list["toast"]
        # 获取删除前视频数
        before_count = self.watch_list_page.get_watch_list_count()
        self.watch_list_page.select_one_video_and_delete(before_count)
        # 点击确认删除按钮
        self.watch_list_page.click_delete_button()
        # 获取删除后提示
        actual_toast = self.watch_list_page.get_snackbar_text()
        assert toast in actual_toast

    @logit()
    def test_delete_all_video(self):
        """
        测试删除全部待看视频
        :return:
        """
        data_list = self.watch_list_data["test_delete_all_video"]
        toast = data_list["toast"]
        # 获取删除前视频数
        before_count = self.watch_list_page.get_watch_list_count()
        self.watch_list_page.select_all_video_and_delete(before_count)
        # 点击确认删除按钮
        self.watch_list_page.click_delete_action()
        # 获取删除后提示
        actual_toast = self.watch_list_page.get_snackbar_text()
        assert toast in actual_toast

    @logit()
    def test_default_text(self):
        """
        测试默认文案显示是否正确
        :return:
        """
        data_list = self.watch_list_data["test_default_text"]
        text = data_list["text"]
        actual_text = self.watch_list_page.get_default_text()
        assert actual_text == text


#!/usr/bin/python2.7

# This file is to extract the Ensembl id(s) in each file in the veNOG folder.
# Note, the any of the three ids can occur multiple times in the output file,
# to make the map file is complete.

import os
import numpy as np
from optparse import OptionParser

def get_veNOG_id(data_dir, org_id="9606"):
    """To get the Ensembl id for a given organism in all files in veNOG folder.
    """
    path_old = os.getcwd()
    os.chdir(data_dir)

    veNOG_Ensem = []
    file_cnt, pair_cnt = 0, 0
    for file_name in os.listdir(data_dir):
        file_cnt += 1
        veNOG_ID = file_name.split(".")[1]

        f_tmp = open(data_dir + "/" + file_name,"r")
        all_lines = f_tmp.readlines()
        f_tmp.close()

        # check the rule for display organism id
        for _line in all_lines:
            if _line.startswith(">" + org_id):
                pair_cnt += 1
                Ensembl_ID = _line.rstrip().split('.')[1]
                veNOG_Ensem.append([veNOG_ID, Ensembl_ID])
    veNOG_Ensem = np.array(veNOG_Ensem)

    os.chdir(path_old)
    print("[PTM-pre] %d files returning %d id pairs: %d veNOG and %d Ensembl."
        %(file_cnt, pair_cnt, len(np.unique(veNOG_Ensem[:,0])),
            len(np.unique(veNOG_Ensem[:,1]))))
    return veNOG_Ensem

def map_biomart_ids(biomart_file, veNOG_Ensem):
    """map Ensembl-Uniprot and veNOG-Ensembl ids.
    Note, the Ensembl id in veNOG-Ensembl pair need to be unique.
    """
    fid = open(biomart_file, "r")
    dat = fid.readlines()
    fid.close()

    eff_line = 0
    Ensem_Unip = []
    for i in range(len(dat)):
        if len(dat[i].split()) == 2:
            eff_line += 1
            Ensem_Unip.append(dat[i].split())
    Ensem_Unip = np.array(Ensem_Unip)
    Ensem_Unip = Ensem_Unip[np.argsort(Ensem_Unip[:,0]),:]
    print("[PTM-pre] %d out of %d lines are effective in biomart file."
        %(eff_line, len(dat)))

    veNOG_Ensem = veNOG_Ensem[np.argsort(veNOG_Ensem[:,1]),:]
    if len(np.unique(veNOG_Ensem[:,1])) < len(veNOG_Ensem[:,1]):
        print("[PTM-pre] Warning: the Ensembl ID in veNOG-Ensembl map is not "
              "unique. Only the first one of the repeated id will be output. "
              "Please check!")

    i, j = 0, 0
    veNOG_Ensem_Unip = []
    while i < len(Ensem_Unip) and j < len(veNOG_Ensem):
        if Ensem_Unip[i,0] < veNOG_Ensem[j,1]:
            i += 1
        elif Ensem_Unip[i,0] == veNOG_Ensem[j,1]:
            veNOG_Ensem_Unip.append(np.append(veNOG_Ensem[j,:], Ensem_Unip[i,1]))
            i += 1
        elif Ensem_Unip[i,0] > veNOG_Ensem[j,1]:
            j += 1
    veNOG_Ensem_Unip = np.array(veNOG_Ensem_Unip)

    print("[PTM-pre] %d veNOG-Ensembl-Uniprot id pair returned: %d veNOG, %d "
          "Ensemble, %d Unirpot." %(veNOG_Ensem_Unip.shape[0],
            len(np.unique(veNOG_Ensem_Unip[:,0])),
            len(np.unique(veNOG_Ensem_Unip[:,1])),
            len(np.unique(veNOG_Ensem_Unip[:,2]))))
    return veNOG_Ensem_Unip

def lite_folder(data_dir, list_to_keep):
    for file_name in os.listdir(data_dir):
        veNOG_ID = file_name.split(".")[1]
        file_name.split(".")[1]
        if veNOG_ID not in list_to_keep:
            os.remove(data_dir + "/" + file_name)
    return None

def main():
    #0. parse command line options
    parser = OptionParser()
    parser.add_option("--veNOG-dir", "-d", dest="data_dir", default=None,
        help="The diroctory of the veNOG data")
    parser.add_option("--biomart-file", "-m", dest="biomart_file", default=None,
        help="The biomart file containing Enesmbl ID and Uniprot ID")
    parser.add_option("--out-file", "-o", dest="out_file", default=None,
        help="The file to save processed veNOG-Ensembl-Uniprot ID pairs")
    parser.add_option("--lite-folder", action="store_true", dest="remove_no_use",
        default=False, help="remove no used files in veNOG folder.")

    (options, args) = parser.parse_args()
    data_dir = options.data_dir
    out_file = options.out_file
    biomart_file = options.biomart_file
    if out_file is None:
        out_file = data_dir + "/../veNOG_Ensem_Unip.txt"

    if os.path.isdir(data_dir) == False:
        print("[PTM-pre] Error: Data directory does not exist. Please check!")
        sys.exit()

    #save mapped veNOG, Ensembl ids
    fid = open(out_file, "w")
    veNOG_Ensem = get_veNOG_id(data_dir)
    veNOG_Ensem_Unip = map_biomart_ids(biomart_file, veNOG_Ensem)

    head_line = "veNOG_ID\tEnsem_ID\tUnip_ID\n"
    fid.writelines(head_line)
    for id_pair in veNOG_Ensem_Unip:
        fid.writelines("\t".join(list(id_pair)) + "\n")
    fid.close()

    #lite folder
    if options.remove_no_use:
        lite_folder(data_dir, veNOG_Ensem_Unip[:,0])

if __name__ == '__main__':
    main()


__copyright__ = "Copyright (c) 2020-2021 Jina AI Limited. All rights reserved."
__license__ = "Apache-2.0"

import os
import librosa

from jina import Flow, Document, DocumentArray
from vggish import vggish_input

try:
    from vggish_audio_encoder import VggishAudioEncoder
except:
    from jinahub.encoders.audio.vggish_audio_encoder import VggishAudioEncoder

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

def test_flow_from_yml():

    doc = DocumentArray([Document()])
    with Flow.load_config(os.path.join(cur_dir, 'flow.yml')) as f:
        resp = f.post(on='test', inputs=doc, return_results=True)

    assert resp is not None

def test_embedding_exists():

    x_audio, sample_rate = librosa.load(os.path.join(cur_dir, '../data/sample.wav'))
    log_mel_examples = vggish_input.waveform_to_examples(x_audio, sample_rate)
    doc = DocumentArray([Document(blob=log_mel_examples)])

    with Flow.load_config(os.path.join(cur_dir, 'flow.yml')) as f:
        responses = f.post(on='index', inputs=doc, return_results=True)

    assert responses[0].docs[0].embedding is not None


try:
	from tkinter import *
except:
	from Tkinter import *
from PIL import Image, ImageTk
import random, pyglet, sys, os

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)

pyglet.font.add_file(resource_path('images/arcadeclassic.ttf'))
ArcadeClassic = pyglet.font.load('ArcadeClassic')

score = 0
bullet = 0
player_1_x = []
player_1_y = []
for i in range(560,(560+79)):
	player_1_x.append(i)
for i in range((590-87),590):
	player_1_y.append(i)
spawn_x = []
spawn_y = []
for i in range(500-150,(500+199+150)):
	spawn_x.append(i)
for i in range((650-150-147),650+150):
	spawn_y.append(i)

history=[]
moves = [10,0]
star=[]
z = 0
left_right_shoot = "middle"
name = "shoot "
root = Tk()
var = StringVar()
life = 3
stop_asteroids = "go"

def spawn_kill():
	global star, c, spawn_x, spawn_y, i_star, q, root
	for i_star in range(len(star)):
		q = c.coords(star[i_star])
		if q[1] in spawn_y:
			if q[0] in spawn_x:
				c.delete(star[i_star])
	root.after(1, if_hit)
def if_hit():
	global star, c, player_1, life, player_1_x, player_1_y,stop_asteroids,score
	for i in range(len(star)):
		a = c.coords(star[i])
		if a[1] in player_1_y:
			if a[0] in player_1_x:
				c.coords(star[i],random.randint(-500,1700),-600)
				root.after(1000)
				des = "life "+str(life)
				if life == 3:
					c.delete(lifeico4)
				elif life == 2:
					c.delete(lifeico3)
				elif life == 1:
					c.delete(lifeico2)
				elif life == 0:
					c.delete(lifeico1)
					moves[0] = 0
					stop_asteroids = "stop"
					c.move("all", 700,700)
					c.move("all", 700,700)
					c.create_text(600, 300,text = "game over",fill='Yellow',anchor=S,font=('ArcadeClassic', 35),tag="game over")
					score_text = "Score: " + str(score)
					score_counter=c.create_text(600, 310,text = score_text,fill='Yellow',anchor=N,font=('ArcadeClassic', 20),tag="score counter")
				life = life -1
	root.after(ms=1,func=if_hit)
def score_update():
	global score,score_counter,stop_asteroids,star, c, spawn_x, spawn_y, i_star, q
	if stop_asteroids != "stop":
		score += 100
		c.delete(score_counter)
		score_text = "Score: " + str(score)
		score_counter=c.create_text(5, 5,text = score_text,fill='yellow',anchor=NW,font=('ArcadeClassic', 15),tag="score counter")
	root.after(100,score_update)
	#if stop_asteroids == 'stop':
	#	for i_star in range(len(star)):
	#		q = c.coords(star[i_star])
	#		if q[1] in spawn_y:
	#			if q[0] in spawn_x:
	#				c.delete(star[i_star])
def keyup(e):
	if  e.keysym in history :
		history.pop(history.index(e.keysym))
		Show_key()
		var.set(str(history))
	Show_key()
def keydown(e):
	if not e.keysym in history and e.keysym!="space":
		history.append(e.keysym)
		var.set(str(history))
	elif not e.keysym in history and e.keysym=="space":
		history.append(e.keysym)
		var.set(str(history))
	Show_key()
def Show_key():
	global iship, ship, player_1, c,left_right_shoot , current, moves, stop_asteroids, name, spawn_kill
	if stop_asteroids != "stop":
		if "Left" in history or "a" in history:
			left_right_shoot = "left"
			iship = Image.open(resource_path("images/ship.png"))
			ship = ImageTk.PhotoImage(iship.rotate(15))
			c.delete(player_1)
			player_1=c.create_image(560, 590,image=ship, anchor=SW,tag="player")
			moves[1] = 5
			c.update()
		if "Right" in history or "d" in history:
			left_right_shoot = "right"
			ship = ImageTk.PhotoImage(iship.rotate(-15))
			c.delete(player_1)
			player_1=c.create_image(560, 590,image=ship, anchor=SW,tag="player")
			moves[1] = -5
		if len(history)==0:
			moves[0]=10
			left_right_shoot = "middle"
			ship = ImageTk.PhotoImage(iship.rotate(0))
			c.delete(player_1)
			player_1=c.create_image(560, 590,image=ship, anchor=SW,tag="player")
			moves[1]=0
		if "space" in history:
			history.remove("space")
			moves[0] = 15
		else:
			moves[0] = 10
	elif stop_asteroids == "stop":
			if "space" in history:
				history.remove("space")
				root.destroy()

root.bind("<Escape>", lambda e: root.destroy())
Frame(root, height=20).pack()
root.wm_state('zoomed')
root.wm_title("Robert Kimber - Space Game")
root.iconbitmap(resource_path("images/ship.ico"))
root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
c=Canvas(root,height=600,width=1200,bg="black")
root.bind_all('<KeyPress>', keydown)
root.bind_all('<KeyRelease>', keyup)
c.pack(side=TOP)
iship = Image.open(resource_path("images/ship_life.png"))
iship = iship.resize((60, 60), Image.ANTIALIAS)
ship = ImageTk.PhotoImage(iship)
player_1=c.create_image(560, 590,image=ship, anchor=SW,tag="player")
iship_life = Image.open(resource_path("images/ship_life.png"))
iship_life = iship_life.resize((40, 40), Image.ANTIALIAS)
ship_life = ImageTk.PhotoImage(iship_life)
score_text = "Score: " + str(score)
score_counter=c.create_text(5, 5,text = score_text,fill='yellow',anchor=NW,font=('ArcadeClassic', 15),tag="score counter")

def create_stars():
	global stars1, stars2, stars3, stars4, stars5, stars6, star, stars, stara
	stara = Image.open(resource_path("images/star1.png"))
	stars1 = ImageTk.PhotoImage(stara)
	stara = Image.open(resource_path("images/star2.png"))
	stars2 = ImageTk.PhotoImage(stara)
	stara = Image.open(resource_path("images/star3.png"))
	stars3 = ImageTk.PhotoImage(stara)
	stara = Image.open(resource_path("images/star4.png"))
	stars4 = ImageTk.PhotoImage(stara)
	stara = Image.open(resource_path("images/star5.png"))
	stars5 = ImageTk.PhotoImage(stara)
	stara = Image.open(resource_path("images/star6.png"))
	stars6 = ImageTk.PhotoImage(stara)
	for i in range(15):
		star.append(c.create_image(random.randint(-500,1700),random.randrange(-600,400),image= stars1))
		star.append(c.create_image(random.randint(-500,1700),random.randrange(-600,400),image= stars2))
		star.append(c.create_image(random.randint(-500,1700),random.randrange(-600,400),image= stars3))
		star.append(c.create_image(random.randint(-500,1700),random.randrange(-600,400),image= stars4))
		star.append(c.create_image(random.randint(-500,1700),random.randrange(-600,400),image= stars5))
		star.append(c.create_image(random.randint(-500,1700),random.randrange(-600,400),image= stars6))
create_stars()

def update_asteroids():
	global star, c, moves
	for i in range(len(star)):
		p = c.coords(star[i])
		p[1] = p[1] + moves[0]
		p[0] = p[0] + moves[1]
		c.coords(star[i],p[0],p[1])
		if stop_asteroids != "stop":
			if(p[1]>610):
				c.coords(star[i],random.randint(-500,1100),-600)
	root.after(50, update_asteroids)

lifeico1 = c.create_image(10, 595,image=ship_life, anchor=SW,tag="life 1")
lifeico2 = c.create_image(42, 595,image=ship_life, anchor=SW,tag="life 2")
lifeico3 = c.create_image(74, 595,image=ship_life, anchor=SW,tag="life 3")
lifeico4 = c.create_image(106, 595,image=ship_life, anchor=SW,tag="life 4")
if_hit()
update_asteroids()
score_update()
root.mainloop()


#First solution --> Pivot is the first element in the arr, it's close to O(n)
def quick_sort(arr):
    if len(arr) < 2:
        return arr     # --> base case
    else:
        pivot = arr[0]
        smaller_than_pivot = [i for i in arr[1:] if i <= pivot]
        bigger_than_pivot = [i for i in arr[1:] if i > pivot]

        return quick_sort(smaller_than_pivot) + [pivot] + quick_sort(bigger_than_pivot)   # --> Recursive case

print( quick_sort([5, 2, 8, 3, 7, 4, 22]) )

#Second solution --> Pivot is the middle element in the arr, it's close to O(logn)
def quick_sort2(arr):
    if len(arr) < 2:
        return arr  # --> Base Case

    mid = len(arr) // 2

    pivot = arr[mid]
    arr.pop(mid)

    smaller_than_pivot = [i for i in arr if i <= pivot]
    bigger_than_pivot = [i for i in arr if i > pivot]

    return quick_sort2(smaller_than_pivot) + [pivot] + quick_sort2(bigger_than_pivot)

print( quick_sort2([7, 2, 5, 3, 8, 8, 9]) )


import sys

class Sudoku:
    def __init__(self, board):
        self.board = board
        self.blank = []
        self.flag = False

    def find_blank(self):
        for i in range(9):
            for j in range(9):
                if not self.board[i][j]:
                    self.blank.append((i, j))

    def promising(self, target, row, col):
        for i in range(9):
            if target == self.board[row][i]:
                return False

        for r in range(9):
            if target == self.board[r][col]:
                return False

        row = row // 3 * 3
        col = col // 3 * 3

        for r in range(row, row + 3):
            for c in range(col, col + 3):
                if target == self.board[r][c]:
                    return False

        return True

    def fill(self, n):
        if n == len(self.blank):
            self.flag = True
            return

        for i in range(1, 10):
            if self.promising(i, self.blank[n][0], self.blank[n][1]):
                self.board[self.blank[n][0]][self.blank[n][1]] = i
                self.fill(n + 1)
                if not self.flag:
                    self.board[self.blank[n][0]][self.blank[n][1]] = 0

    def print_board(self):
        for r in self.board:
            for i, c in enumerate(r):
                if i:
                    print(" ", end="")
                print(c, end="")
            print()

if __name__ == '__main__':
    unfilled_sudoku = []
    for _ in range(9):
        unfilled_sudoku.append(list(map(int, sys.stdin.readline().split())))

    sudoku = Sudoku(unfilled_sudoku)
    sudoku.find_blank()
    sudoku.fill(0)

    sudoku.print_board()


from django.urls import path, re_path
from .views import OrderDetail, OrderList

app_name = 'orders'

urlpatterns = [
    path('order/list/', OrderList.as_view(), name='order-list'),
    re_path(r'^order/detail/(?P<order_id>[0-9A-Za-z]+)/$', OrderDetail.as_view(), name="order-detail")
]


# 더 맵게
import heapq

def solution(scoville, K):      # 음식의 스코빌 지수를 담은 배열, 기준 스코빌 지수
    answer = 0                  # 모든 음식의 스코빌 지수를 K이상으로 만들기 위한 섞는 횟수 최소값

    heapq.heapify(scoville)     # 스코빌 지수를 힙으로 변환

    while scoville[0] < K and len(scoville) > 1:
        # 섞을 음식 힙에서 삭제하면서 스코빌 지수 가져옴
        min_scoville = heapq.heappop(scoville)  # 가장 맵지 않은 음식의 스코빌 지수
        sec_scoville = heapq.heappop(scoville)  # 두번째 스코빌 지수

        if min_scoville >= K and sec_scoville >= K:   # 둘다 이미 K 이상이면 섞지 않음
            break

        # 섞은 음식의 스코빌 지수 삽입
        mix_scoville = min_scoville + (sec_scoville * 2)    # 가장 맵지 않은 음식 + (두번째 스코빌 지수 * 2)
        heapq.heappush(scoville, mix_scoville)      # 새로운 음식 추가

        answer += 1                                 # 섞은 횟수 증가

    return answer if scoville[0] >= K else -1

# print(solution([1, 1, 1, 1, 1, 1, 1, 1], 8))        # 6
# print(solution([1000000, 1], 999999))                # 1
# print(solution([1000000, 1], 1000000))               # 1
# print(solution([1000000, 1], 10000000))               # -1

print(solution([1, 2], 5))               # 1