        for i, label in enumerate(labels):
            color = self.colours[self.labels.index(label)]
            color = [int(x) for x in color]
            conf = round(confidence[i] * 100, 2)
            label = label + " " + str(conf)
            cv2.rectangle(img, (bbox[i][0], bbox[i][1]), (bbox[i][2], bbox[i][3]), color, 2)
            cv2.putText(img, label, (bbox[i][0], bbox[i][1] - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

        return img

import numpy as np
import cv2
import os
import sys
import logging
from visionlib.utils.webutils import web
from visionlib.utils.imgutils import Image

class ODetection:
    """This class contains all functions to detect objects from an image.
                . . .

    Methods:

        detect_objects():

            Used to detect objects from an image. Returns the bounding
            boxes, labels and confidence. Uses detector set by set_detector() method.

        draw_box():

            Used to draw the bounding box, labels and confidence in an image. Returns
            the frame with bounding boxes. Uses detector set by set_detector() method.

        set_detector():

            Used to set detector to used by detect_objects() method. If not set
            will use tiny yolo as default.

    """

    def __init__(self):
        self.model = None
        self.model_ln = None
        np.random.seed(62)
        self.web_util = web()
        self.img_util = Image()
        self.min_confindence = 0.5
        self.threshold = 0.3

    def set_detector(self, model_name='tiny_yolo', model_path=None, cfg_path=None,
                     label_path=None):
        '''Set's the detector to use. Can be tiny-yolo or yolo.
        Setting to tiny-yolo will use Tiny-yolov3.
        Setting to yolo will use Yolov3.

        Args:
            model_name (str):
                    The model to use. If the given model is
                    not present in pc, it will download and use it.
            model_path (str):
                        Set this to path where the custom model You want
                        to load is.
            cfg_path (str):
                        Set this to path where the config file for custom model,
                        You want to load is.
            label_path (str):
                        Set this to path where the labels file for custom model,
                        You want to load is.
        '''

        if model_path is not None and cfg_path is not None:
            if os.path.exists(model_path) & os.path.exists(cfg_path):
                model = model_path
                cfg = cfg_path
                if label_path is not None:
                    labels = label_path
                else:
                    raise Exception("Provided label path does not exist")
            else:
                raise Exception("Provided model path or config path does not exist")

        elif model_name == "Tiny-yolov3":
            model, cfg, labels = self.web_util.download_file(model_name, labels=True)

        elif model_name == "Yolov3":
            model, cfg, labels = self.web_util.download_file(model_name, labels=True)

        else:
            raise Exception("Invalid model name")

        if model and cfg is not None:

            with open(labels, 'r') as file:
                self.labels = file.read().strip().split("\n")
            self.colours = np.random.randint(0, 255, size=(len(self.labels), 3), dtype="uint8")

            self.model = cv2.dnn.readNetFromDarknet(cfg, model)
            self.model_ln = self.model.getLayerNames()
            self.model_ln = [
                self.model_ln[i[0] - 1] for i in self.model.getUnconnectedOutLayers()
            ]

    def detect_objects(self, frame, enable_gpu=False):
        '''This method is used to detect objects in an image.

        Args:
            frame (np.array):
                        Image to detect objects from.

            enable_gpu (bool):
                         Set to true if You want to use gpu.

        Returns:
            list :
                The detected bounding box.

            list :
                The detected class.

            list :
                Confidence for each detected class

        '''
        (H, W) = frame.shape[:2]
        blob = cv2.dnn.blobFromImage(
            frame, 1 / 255.0, (416, 416), swapRB=True, crop=False
        )

        if enable_gpu:
            self.model.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
            self.model.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

        self.model.setInput(blob)
        layerOutputs = self.model.forward(self.model_ln)
        boxes, confidences, classIDs = [], [], []

        for output in layerOutputs:

            for detection in output:
                scores = detection[5:]
                classID = np.argmax(scores)
                confidence = scores[classID]

                if confidence > self.min_confindence:
                    box = detection[0:4] * np.array([W, H, W, H])
                    (centerX, centerY, width, height) = box.astype("int")
                    x = int(centerX - (width / 2))
                    y = int(centerY - (height / 2))
                    boxes.append([x, y, int(width), int(height)])
                    confidences.append(float(confidence))
                    classIDs.append(classID)

        idxs = cv2.dnn.NMSBoxes(
            boxes, confidences, self.min_confindence, self.threshold
        )
        bbox = []
        label = []
        conf = []
        if len(idxs) > 0:
            for ix in idxs:
                ix = ix[0]
                box = boxes[ix]
                x = box[0]
                y = box[1]
                w = box[2]
                h = box[3]
                bbox.append([int(x), int(y), int(x + w), int(y + h)])
                label.append(str(self.labels[classIDs[ix]]))
                conf.append(confidences[ix])
        return bbox, label, conf

    def draw_bbox(self, img, bbox, labels, confidence):
        '''Draw's Box around the detected objects.

        Args
            img (numpy.array):
                The image to draw bounding boxes
            bbox (list):
                bounding boxes given detect_objects function.
            labels (list):
                labels given detect_objects function.
            confidence (list):
                Confidence for the detected label.

        Returns
            numpy.array :
                The image with bounding boxes and labels.
        '''

    def extract_objects(self, img=None, boxes=None, sdir=None, labels=None):
        i = 0
        if img is not None and boxes is not None:
            if labels is not None:
                for box, label in zip(boxes, labels):
                    ex_img = self.img_util.crop(img, box)
                    if sdir is not None:
                        save_name = "extracted_object_{0}_{1}.jpg".format(label, i)
                        save_dir = os.path.join(sdir, save_name)
                        logging.info("Writing image to {0}".format(save_dir))
                        cv2.imwrite(save_dir, ex_img)
                        i += 1
                    yield ex_img
            else:
                for box in boxes:
                    ex_img = self.img_util.crop(img, box)
                    if sdir is not None:
                        save_name = "extracted_object_{0}.jpg".format(i)
                        save_dir = os.path.join(sdir, save_name)
                        logging.info("Writing image to", sdir + save_name)
                        cv2.imwrite(save_dir, ex_img)
                        i += 1
                    yield ex_img


    if in_place:
        y = x
    elif out is None:
        y = x.copy()
    else:
        y = out
    if type(y) in [sparse.csc_matrix, sparse.csr_matrix]:
        z = y.data
    else:
        z = y
    nz = z.nonzero()
    z[nz] *= np.log2(z[nz])
    return y

from .base import Metric

import numpy as np
import scipy.sparse as sparse

class VoiScore(Metric):
    """
    Computes a score based on the variation of information according to [1].
    References
    ----------
    [1] Meila, M. (2007). Comparing clusterings - an information based
    distance. Journal of Multivariate Analysis 98, 873-895.
    """
    def forward(self, prediction, target):
        assert(len(prediction) == len(target))
        segmentation = prediction.cpu().numpy()
        target = target.cpu().numpy()
        return np.mean([sum(voi(segmentation[i], target[i]))
                        for i in range(len(prediction))])

# Copied from `cremi-python`

# Evaluation code courtesy of Juan Nunez-Iglesias, taken from

def voi(seg, gt, ignore_reconstruction=[], ignore_groundtruth=[0]):
    """Return the conditional entropies of the variation of information metric. [1]

    Let X be a seg, and Y a ground truth labelling. The variation of
    information between the two is the sum of two conditional entropies:

        VI(X, Y) = H(X|Y) + H(Y|X).

    The first one, H(X|Y), is a measure of oversegmentation, the second one,
    H(Y|X), a measure of undersegmentation. These measures are referred to as
    the variation of information split or merge error, respectively.

    Parameters
    ----------
    seg : np.ndarray, int type, arbitrary shape
        A candidate segmentation.
    gt : np.ndarray, int type, same shape as `seg`
        The ground truth segmentation.
    ignore_seg, ignore_gt : list of int, optional
        Any points having a label in this list are ignored in the evaluation.
        By default, only the label 0 in the ground truth will be ignored.

    Returns
    -------
    (split, merge) : float
        The variation of information split and merge error, i.e., H(X|Y) and H(Y|X)

    References
    ----------
    [1] Meila, M. (2007). Comparing clusterings - an information based
    distance. Journal of Multivariate Analysis 98, 873-895.
    """
    hyxg, hxgy = split_vi(seg, gt, ignore_reconstruction, ignore_groundtruth)
    return hxgy, hyxg

def split_vi(x, y=None, ignore_x=[0], ignore_y=[0]):
    """Return the symmetric conditional entropies associated with the VI.

    The variation of information is defined as VI(X,Y) = H(X|Y) + H(Y|X).
    If Y is the ground-truth segmentation, then H(Y|X) can be interpreted
    as the amount of under-segmentation of Y and H(X|Y) is then the amount
    of over-segmentation.  In other words, a perfect over-segmentation
    will have H(Y|X)=0 and a perfect under-segmentation will have H(X|Y)=0.

    If y is None, x is assumed to be a contingency table.

    Parameters
    ----------
    x : np.ndarray
        Label field (int type) or contingency table (float). `x` is
        interpreted as a contingency table (summing to 1.0) if and only if `y`
        is not provided.
    y : np.ndarray of int, same shape as x, optional
        A label field to compare to `x`.
    ignore_x, ignore_y : list of int, optional
        Any points having a label in this list are ignored in the evaluation.
        Ignore 0-labeled points by default.

    Returns
    -------
    sv : np.ndarray of float, shape (2,)
        The conditional entropies of Y|X and X|Y.

    See Also
    --------
    vi
    """
    _, _, _, hxgy, hygx, _, _ = vi_tables(x, y, ignore_x, ignore_y)
    # false merges, false splits
    return np.array([hygx.sum(), hxgy.sum()])

def vi_tables(x, y=None, ignore_x=[0], ignore_y=[0]):
    """Return probability tables used for calculating VI.

    If y is None, x is assumed to be a contingency table.

    Parameters
    ----------
    x, y : np.ndarray
        Either x and y are provided as equal-shaped np.ndarray label fields
        (int type), or y is not provided and x is a contingency table
        (sparse.csc_matrix) that may or may not sum to 1.
    ignore_x, ignore_y : list of int, optional
        Rows and columns (respectively) to ignore in the contingency table.
        These are labels that are not counted when evaluating VI.

    Returns
    -------
    pxy : sparse.csc_matrix of float
        The normalized contingency table.
    px, py, hxgy, hygx, lpygx, lpxgy : np.ndarray of float
        The proportions of each label in `x` and `y` (`px`, `py`), the
        per-segment conditional entropies of `x` given `y` and vice-versa, the
        per-segment conditional probability p log p.
    """
    if y is not None:
        pxy = contingency_table(x, y, ignore_x, ignore_y)
    else:
        cont = x
        total = float(cont.sum())
        # normalize, since it is an identity op if already done
        pxy = cont / total

    # Calculate probabilities
    px = np.array(pxy.sum(axis=1)).ravel()
    py = np.array(pxy.sum(axis=0)).ravel()
    # Remove zero rows/cols
    nzx = px.nonzero()[0]
    nzy = py.nonzero()[0]
    nzpx = px[nzx]
    nzpy = py[nzy]
    nzpxy = pxy[nzx, :][:, nzy]

    # Calculate log conditional probabilities and entropies
    lpygx = np.zeros(np.shape(px))
    lpygx[nzx] = xlogx(divide_rows(nzpxy, nzpx)).sum(axis=1).squeeze()  # \sum_x{p_{y|x} \log{p_{y|x}}}
    hygx = -(px * lpygx)  # \sum_x{p_x H(Y|X=x)} = H(Y|X)

    lpxgy = np.zeros(np.shape(py))
    lpxgy[nzy] = xlogx(divide_columns(nzpxy, nzpy)).sum(axis=0)
    hxgy = -(py * lpxgy)

    return [pxy] + list(map(np.asarray, [px, py, hxgy, hygx, lpygx, lpxgy]))

def contingency_table(seg, gt, ignore_seg=[0], ignore_gt=[0], norm=True):
    """Return the contingency table for all regions in matched segmentations.

    Parameters
    ----------
    seg : np.ndarray, int type, arbitrary shape
        A candidate segmentation.
    gt : np.ndarray, int type, same shape as `seg`
        The ground truth segmentation.
    ignore_seg : list of int, optional
        Values to ignore in `seg`. Voxels in `seg` having a value in this list
        will not contribute to the contingency table. (default: [0])
    ignore_gt : list of int, optional
        Values to ignore in `gt`. Voxels in `gt` having a value in this list
        will not contribute to the contingency table. (default: [0])
    norm : bool, optional
        Whether to normalize the table so that it sums to 1.

    Returns
    -------
    cont : scipy.sparse.csc_matrix
        A contingency table. `cont[i, j]` will equal the number of voxels
        labeled `i` in `seg` and `j` in `gt`. (Or the proportion of such voxels
        if `norm=True`.)
    """
    segr = seg.ravel()
    gtr = gt.ravel()
    ignored = np.zeros(segr.shape, np.bool)
    data = np.ones(len(gtr))
    for i in ignore_seg:
        ignored[segr == i] = True
    for j in ignore_gt:
        ignored[gtr == j] = True
    data[ignored] = 0
    cont = sparse.coo_matrix((data, (segr, gtr))).tocsc()
    if norm:
        cont /= float(cont.sum())
    return cont

def divide_columns(matrix, row, in_place=False):
    """Divide each column of `matrix` by the corresponding element in `row`.

    The result is as follows: out[i, j] = matrix[i, j] / row[j]

    Parameters
    ----------
    matrix : np.ndarray, scipy.sparse.csc_matrix or csr_matrix, shape (M, N)
        The input matrix.
    column : a 1D np.ndarray, shape (N,)
        The row dividing `matrix`.
    in_place : bool (optional, default False)
        Do the computation in-place.

    Returns
    -------
    out : same type as `matrix`
        The result of the row-wise division.
    """
    if in_place:
        out = matrix
    else:
        out = matrix.copy()
    if type(out) in [sparse.csc_matrix, sparse.csr_matrix]:
        if type(out) == sparse.csc_matrix:
            convert_to_csc = True
            out = out.tocsr()
        else:
            convert_to_csc = False
        row_repeated = np.take(row, out.indices)
        nz = out.data.nonzero()
        out.data[nz] /= row_repeated[nz]
        if convert_to_csc:
            out = out.tocsc()
    else:
        out /= row[np.newaxis, :]
    return out

def divide_rows(matrix, column, in_place=False):
    """Divide each row of `matrix` by the corresponding element in `column`.

    The result is as follows: out[i, j] = matrix[i, j] / column[i]

    Parameters
    ----------
    matrix : np.ndarray, scipy.sparse.csc_matrix or csr_matrix, shape (M, N)
        The input matrix.
    column : a 1D np.ndarray, shape (M,)
        The column dividing `matrix`.
    in_place : bool (optional, default False)
        Do the computation in-place.

    Returns
    -------
    out : same type as `matrix`
        The result of the row-wise division.
    """
    if in_place:
        out = matrix
    else:
        out = matrix.copy()
    if type(out) in [sparse.csc_matrix, sparse.csr_matrix]:
        if type(out) == sparse.csr_matrix:
            convert_to_csr = True
            out = out.tocsc()
        else:
            convert_to_csr = False
        column_repeated = np.take(column, out.indices)
        nz = out.data.nonzero()
        out.data[nz] /= column_repeated[nz]
        if convert_to_csr:
            out = out.tocsr()
    else:
        out /= column[:, np.newaxis]
    return out

def xlogx(x, out=None, in_place=False):
    """Compute x * log_2(x).

    We define 0 * log_2(0) = 0

    Parameters
    ----------
    x : np.ndarray or scipy.sparse.csc_matrix or csr_matrix
        The input array.
    out : same type as x (optional)
        If provided, use this array/matrix for the result.
    in_place : bool (optional, default False)
        Operate directly on x.

    Returns
    -------
    y : same type as x
        Result of x * log_2(x).
    """


        if self.no_of_connected_dev < 2:
            self.ui.secondary_combo.addItem("Disabled")
            self.ui.secondary_combo.setEnabled(False)
            self.ui.primary_combobox.addItem("Disabled")
            self.ui.primary_combobox.setEnabled(False)
            return

        for display in self.displays:
            self.ui.secondary_combo.addItem(display)
            self.ui.primary_combobox.addItem(display)

# This file is part of Brightness Controller.
import sys
import getpass
from os import path, remove, makedirs
from qtpy import QtGui, QtCore, QtWidgets
from qtpy.QtCore import QSize
from qtpy.QtGui import QIcon
from util.QtSingleApplication import QtSingleApplication
from ui.mainwindow import Ui_MainWindow
from ui.license import Ui_Form as License_Ui_Form
from ui.about import Ui_Form as About_Ui_Form
from ui.help import Ui_Form as Help_Ui_Form
import util.executor as Executor
import util.check_displays as CDisplay
import util.write_config as WriteConfig
import util.read_config as ReadConfig
import util.filepath_handler as Filepath_handler

class MyApplication(QtWidgets.QMainWindow):

    def __assign_displays(self):
        """assigns display name """
        self.displays = CDisplay.detect_display_devices()
        self.no_of_displays = len(self.displays)
        self.no_of_connected_dev = self.no_of_displays

        if self.no_of_displays == 1:
            self.display1 = self.displays[0]
        elif self.no_of_displays == 2:

            self.display1 = self.displays[0]
            self.display2 = self.displays[1]

    def __init__(self, parent=None):
        """Initializes"""
        QtWidgets.QMainWindow.__init__(self, parent)

        self.tray_menu = None
        self.tray_icon = None
        self.display1 = None
        self.display2 = None
        self.license_widget = None
        self.about_widget = None
        self.help_widget = None

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui_icon = QIcon()
        self.ui_icon.addFile(Filepath_handler.get_icon_path(),
                     QSize(), QIcon.Normal, QIcon.Off)
        self.setWindowIcon(self.ui_icon)
        self.temperature = 'Default'
        self.no_of_connected_dev = 0
        self.__assign_displays()
        self.setup_default_directory()
        self.generate_dynamic_items()
        self.default_config = '/home/{}/.config/' \
                              'brightness_controller/settings' \
            .format(getpass.getuser())
        self.values = []
        self.array_value = 0.01
        for i in range(0, 100):
            self.values.append(self.array_value)
            self.array_value += 0.01
        self.connect_handlers()
        self.setup_widgets()

        if path.exists(self.default_config):
            self.load_settings(self.default_config)

    def setup_default_directory(self):
        """ Create default settings directory if it doesnt exist """
        directory = '/home/{}/.config/' \
                    'brightness_controller/' \
            .format(getpass.getuser())
        if not path.exists(directory):
            try:
                makedirs(directory)
            except OSError as e:
                self._show_error(str(e))

    #         reply = QtWidgets.QMessageBox.question(self, 'Message', "Are you sure to quit?",
    #             reply = QtWidgets.QMessageBox.question(self, 'Message', "Are you sure to quit?",
                # # fixes an odd event bug, the app never shows but prevents closing

    #     """ Setup system tray """

    def setup_widgets(self):
        """connects the form widgets with functions"""
        self.license_widget = LicenseForm()
        self.license_widget.set_main_window(self)
        self.license_widget.hide()

        self.about_widget = AboutForm()
        self.about_widget.set_main_window(self)
        self.about_widget.hide()

        self.help_widget = HelpForm()
        self.help_widget.set_main_window(self)
        self.help_widget.hide()

    def generate_dynamic_items(self):
        """
        manages widgets that may contain dynamic items.
        """
        self.generate_brightness_sources()

    def generate_brightness_sources(self):
        """
        generates assigns display sources to combo boxes
        """

    def connect_handlers(self):
        """Connects the handlers of GUI widgets"""
        self.ui.primary_brightness.valueChanged[int]. \
            connect(self.change_value_pbr)
        self.ui.primary_red.valueChanged[int]. \
            connect(self.change_value_pr)
        self.ui.primary_blue.valueChanged[int]. \
            connect(self.change_value_pb)
        self.ui.primary_green.valueChanged[int]. \
            connect(self.change_value_pg)
        self.enable_secondary_widgets(False)

        if self.no_of_connected_dev >= 2:
            self.enable_secondary_widgets(True)
            self.connect_secondary_widgets()

        if path.exists(self.default_config):
            self.ui.actionClearDefault.setVisible(True)
            self.ui.actionClearDefault.triggered.connect(
                self.delete_default_settings)

        self.ui.actionDefault.triggered.connect(
            lambda: self.save_settings(True))

        self.ui.comboBox.activated[str].connect(self.combo_activated)
        self.ui.primary_combobox.activated[
            str].connect(self.primary_source_combo_activated)
        self.ui.secondary_combo.activated[
            str].connect(self.secondary_source_combo_activated)
        self.ui.actionAbout.triggered.connect(self.show_about)
        self.ui.actionExit.triggered.connect(self.close)
        self.ui.actionHelp.triggered.connect(self.show_help)
        self.ui.actionLicense.triggered.connect(self.show_license)
        self.ui.actionSave.triggered.connect(self.save_settings)
        self.ui.actionLoad.triggered.connect(self.load_settings)

    def enable_secondary_widgets(self, boolean):
        """
        boolean - assigns boolean value to setEnabled(boolean)
        """
        self.ui.secondary_brightness.setEnabled(boolean)
        self.ui.secondary_blue.setEnabled(boolean)
        self.ui.secondary_red.setEnabled(boolean)
        self.ui.secondary_green.setEnabled(boolean)

    def connect_secondary_widgets(self):
        """
        connects the secondary widgets with functions
        """
        self.ui.secondary_brightness.valueChanged[int]. \
            connect(self.change_value_sbr)
        self.ui.secondary_red.valueChanged[int]. \
            connect(self.change_value_sr)
        self.ui.secondary_blue.valueChanged[int]. \
            connect(self.change_value_sb)
        self.ui.secondary_green.valueChanged[int]. \
            connect(self.change_value_sg)

    def change_value_pbr(self, value):
        """Changes Primary Display Brightness"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display1,
                     self.values[value],
                     self.values[self.ui.primary_red.value()],
                     self.values[self.ui.primary_green.value()],
                     self.values[self.ui.primary_blue.value()])
        Executor.execute_command(cmd_value)

    def change_value_pr(self, value):
        """Changes Primary Display Red ratio"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display1,
                     self.values[self.ui.primary_brightness.value()],
                     self.values[value],
                     self.values[self.ui.primary_green.value()],
                     self.values[self.ui.primary_blue.value()])
        Executor.execute_command(cmd_value)

    def change_value_pg(self, value):
        """Changes Primary Display Green ratio"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display1,
                     self.values[self.ui.primary_brightness.value()],
                     self.values[self.ui.primary_red.value()],
                     self.values[value],
                     self.values[self.ui.primary_blue.value()])

        Executor.execute_command(cmd_value)

    def change_value_pb(self, value):
        """Changes Primary Display Blue ratio"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display1,
                     self.values[self.ui.primary_brightness.value()],
                     self.values[self.ui.primary_red.value()],
                     self.values[self.ui.primary_green.value()],
                     self.values[value])
        Executor.execute_command(cmd_value)

    def change_value_sbr(self, value):
        """
        Changes Secondary Display Brightness
        """
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display2,
                     self.values[value],
                     self.values[self.ui.secondary_red.value()],
                     self.values[self.ui.secondary_green.value()],
                     self.values[self.ui.secondary_blue.value()])
        Executor.execute_command(cmd_value)

    def change_value_sr(self, value):
        """Changes Secondary Display Red ratio"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display2,
                     self.values[self.ui.secondary_brightness.value()],
                     self.values[value],
                     self.values[self.ui.secondary_green.value()],
                     self.values[self.ui.secondary_blue.value()])
        Executor.execute_command(cmd_value)

    def change_value_sg(self, value):
        """Changes Secondary Display Green ratio"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display2,
                     self.values[self.ui.secondary_brightness.value()],
                     self.values[self.ui.secondary_red.value()],
                     self.values[value],
                     self.values[self.ui.secondary_blue.value()])

        Executor.execute_command(cmd_value)

    def change_value_sb(self, value):
        """Changes Primary Display Blue ratio"""
        cmd_value = "xrandr\
        --output %s \
        --brightness %s\
        --gamma %s:%s:%s" % \
                    (self.display2,
                     self.values[self.ui.secondary_brightness.value()],
                     self.values[self.ui.secondary_red.value()],
                     self.values[self.ui.secondary_green.value()],
                     self.values[value])
        Executor.execute_command(cmd_value)

    def changed_state(self, state):
        if state == QtCore.Qt.Checked:
            temp = self.display1
            self.display1 = self.display2
            self.display2 = temp
        else:
            temp = self.display1
            self.display1 = self.display2
            self.display2 = temp

    def secondary_source_combo_activated(self, text):
        """
        assigns combo value to display
        """
        self.display2 = text

    def primary_source_combo_activated(self, text):
        """assigns combo value to display"""
        self.display1 = text

    def combo_activated(self, text):
        """ Designates values to display and to sliders """
        self.temperature = text
        if text == 'Default':
            rgb = [255, 255, 255]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)

        elif text == '1900K Candle':
            rgb = [255, 147, 41]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '2600K 40W Tungsten':
            rgb = [255, 197, 143]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '2850K 100W Tungsten':
            rgb = [255, 214, 170]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '3200K Halogen':
            rgb = [255, 241, 224]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '5200K Carbon Arc':
            rgb = [255, 250, 244]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '5400K High Noon':
            rgb = [255, 255, 251]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '6000K Direct Sun':
            rgb = [255, 255, 255]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '7000K Overcast Sky':
            rgb = [201, 226, 255]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)
        elif text == '20000K Clear Blue Sky':
            rgb = [64, 156, 255]
            self.change_primary_sliders(rgb)
            if self.no_of_connected_dev == 2:
                self.change_secondary_sliders(rgb)

    def change_primary_sliders(self, rgb):
        """
        rgb - based on the rgb array, assign values to primary display sliders
        and in turn changes primary display color
        """
        slider_r = int((rgb[0] * 100) / 255)
        slider_g = int((rgb[1] * 100) / 255)
        slider_b = int((rgb[2] * 100) / 255)

        self.ui.primary_red.setValue(slider_r)
        self.ui.primary_green.setValue(slider_g)
        self.ui.primary_blue.setValue(slider_b)

    def change_secondary_sliders(self, rgb):
        """
        rgb - based on the rgb array, assign values to
        secondary display sliders and in turn changes secondary display color
        rgb is given in array from a range of 0 to 255
        """
        slider_r = int((rgb[0] * 100) / 255)
        slider_g = int((rgb[1] * 100) / 255)
        slider_b = int((rgb[2] * 100) / 255)

        self.ui.secondary_red.setValue(slider_r)
        self.ui.secondary_green.setValue(slider_g)
        self.ui.secondary_blue.setValue(slider_b)

    def change_secondary_sliders_in_rgb_0_99(self, br_rgb):
        """
        change slider values in rgb from a range of 0 to 99 value
        for secondary monitor slider
        """
        self.ui.secondary_brightness.setValue(br_rgb[0])
        self.ui.secondary_red.setValue(br_rgb[1])
        self.ui.secondary_green.setValue(br_rgb[2])
        self.ui.secondary_blue.setValue(br_rgb[3])

    def primary_sliders_in_rgb_0_99(self, br_rgb):
        """
        change slider values in rgb from a range of 0 to 99 value
        for primary monitor sliders
        """
        self.ui.primary_brightness.setValue(br_rgb[0])
        self.ui.primary_red.setValue(br_rgb[1])
        self.ui.primary_green.setValue(br_rgb[2])
        self.ui.primary_blue.setValue(br_rgb[3])

    def secondary_sliders_in_rgb_0_99(self, br_rgb):
        """
        change slider values in rgb from a range of 0 to 99 value
        for primary monitor sliders
        """
        self.ui.secondary_brightness.setValue(br_rgb[0])
        self.ui.secondary_red.setValue(br_rgb[1])
        self.ui.secondary_green.setValue(br_rgb[2])
        self.ui.secondary_blue.setValue(br_rgb[3])

    def show_about(self):
        """ Shows the About widget"""
        self.about_widget.show()

    def show_license(self):
        """ Shows the License widget"""
        self.license_widget.show()

    def show_help(self):
        """ Shows the Help Widget"""
        self.help_widget.show()

    def save_settings(self, default=False):
        """ save current primary and secondary display settings"""
        file_path = self.default_config if default else QtWidgets.QFileDialog.getSaveFileName()[
            0]
        # just a number. path.exists won't work in case it is a new file.
        if len(file_path) > 5:
            if default:
                self.ui.actionClearDefault.setVisible(True)
            if self.no_of_connected_dev == 1:
                WriteConfig.write_primary_display(
                    self.return_current_primary_settings(),
                    file_path
                )
            elif self.no_of_connected_dev >= 2:
                WriteConfig.write_both_display(
                    self.return_current_primary_settings(),
                    self.return_current_secondary_settings(),
                    file_path

                )

    def _show_error(self, message):
        """ Shows an Error Message"""
        QtWidgets.QMessageBox.critical(self, 'Error', message)

    def delete_default_settings(self):
        """
        delete default settings
        """
        if path.exists(self.default_config):
            try:
                remove(self.default_config)
                self.ui.actionClearDefault.setVisible(False)
            except OSError as e:
                self._show_error(str(e))
        else:
            return False

    def _load_temperature(self, text='Default'):
        """
        Load current temperature settings
        """
        self.temperature = text
        primary_temperature_index = self.ui.comboBox.findText(
            text, QtCore.Qt.MatchFixedString)
        if primary_temperature_index >= 0:
            self.ui.comboBox.setCurrentIndex(primary_temperature_index)

    def load_settings(self, location=None):
        """
        Load current primary and secondary display settings
        """
        file_path = location or QtWidgets.QFileDialog.getOpenFileName()[0]
        if path.exists(file_path):
            loaded_settings = ReadConfig.read_configuration(file_path)
            if len(loaded_settings) == 5:
                self._load_temperature(loaded_settings[4])
                self.primary_sliders_in_rgb_0_99(loaded_settings)
            elif len(loaded_settings) == 11:
                # checks just in case saved settings are for two displays,
                # but loads when only one display is connected
                if self.no_of_connected_dev == 1:
                    self.primary_sliders_in_rgb_0_99(
                        (loaded_settings[0],
                         loaded_settings[1],
                         loaded_settings[2],
                         loaded_settings[3]))
                    return
                # sets reverse control
                primary_source = loaded_settings[4]
                secondary_source = loaded_settings[10]
                self._load_temperature(loaded_settings[5])
                primary_combo_index = self.ui.primary_combobox.findText(
                    primary_source, QtCore.Qt.MatchFixedString)
                second_combo_index = self.ui.secondary_combo.findText(
                    secondary_source, QtCore.Qt.MatchFixedString)
                if primary_combo_index >= 0:
                    self.ui.primary_combobox.setCurrentIndex(
                        primary_combo_index)
                    self.primary_source_combo_activated(primary_source)
                if second_combo_index >= 0:
                    self.ui.secondary_combo.setCurrentIndex(second_combo_index)
                    self.secondary_source_combo_activated(secondary_source)

                self.primary_sliders_in_rgb_0_99(
                    (loaded_settings[0],
                     loaded_settings[1],
                     loaded_settings[2],
                     loaded_settings[3]))

                self.secondary_sliders_in_rgb_0_99(
                    (loaded_settings[6],
                     loaded_settings[7],
                     loaded_settings[8],
                     loaded_settings[9]))

    def return_current_primary_settings(self):
        """
        return p_br_rgb(
        primary_brightness,
        primary_red,
        primary_green,
        primary_blue,
        primary_display_name,
        temperature
        )
        """
        p_br_rgb = [
            self.ui.primary_brightness.value(),
            self.ui.primary_red.value(),
            self.ui.primary_green.value(),
            self.ui.primary_blue.value(),
            self.display1,
            self.temperature
        ]

        return p_br_rgb

    def return_current_secondary_settings(self):
        """
        return s_br_rgb(
        secondary_brightness,
        secondary_red,
        secondary_green,
        secondary_blue,
        secondary_display_name,
        temperature)
        """
        s_br_rgb = [
            self.ui.secondary_brightness.value(),
            self.ui.secondary_red.value(),
            self.ui.secondary_green.value(),
            self.ui.secondary_blue.value(),
            self.display2,
            self.temperature
        ]
        return s_br_rgb

class LicenseForm(QtWidgets.QWidget):
    """License Form widget initialization"""

    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.ui = License_Ui_Form()
        self.ui.setupUi(self)
        self.main_window = None
        self.entry_id = None

    def set_main_window(self, main_win):
        """assigns main_win as main_window"""
        self.main_window = main_win

class AboutForm(QtWidgets.QWidget):
    """About Form widget initialization"""

    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.ui = About_Ui_Form()
        self.ui.setupUi(self)
        self.main_window = None
        self.entry_id = None

    def set_main_window(self, main_win):
        """assigns main_win as main_window"""
        self.main_window = main_win

class HelpForm(QtWidgets.QWidget):
    """Help Form widget initialization"""

    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.ui = Help_Ui_Form()
        self.ui.setupUi(self)
        self.main_window = None
        self.entry_id = None

    def set_main_window(self, main_win):
        """assigns main_win as main_window"""
        self.main_window = main_win

if __name__ == "__main__":
    UUID = 'PHIR-HWOH-MEIZ-AHTA'
    APP = QtSingleApplication(UUID, sys.argv)
    if APP.isRunning():
        sys.exit(0)
    WINDOW = MyApplication()
    APP.setActivationWindow(WINDOW)
    WINDOW.show()
    sys.exit(APP.exec_())


            start = self.base
            stop  = self.base + self.len
            assert 0 <= start <= stop     # annotator hint
            return ListSlice(getitem_slice(self.list, start, stop), 0, self.len)

from rpython.rlib.rarithmetic import ovfcheck

## Lots of code for an adaptive, stable, natural mergesort.  There are many
## pieces to this algorithm; read listsort.txt for overviews and details.
##         Adapted from CPython, original code and algorithms by Tim Peters

def make_timsort_class(getitem=None, setitem=None, length=None,
                       getitem_slice=None, lt=None):

    if getitem is None:
        def getitem(list, item):
            return list[item]

    if setitem is None:
        def setitem(list, item, value):
            list[item] = value

    if length is None:
        def length(list):
            return len(list)

    if getitem_slice is None:
        def getitem_slice(list, start, stop):
            return list[start:stop]

    if lt is None:
        def lt(a, b):
            return a < b

    class TimSort(object):
        """TimSort(list).sort()

        Sorts the list in-place, using the overridable method lt() for comparison.
        """

        def __init__(self, list, listlength=None):
            self.list = list
            if listlength is None:
                listlength = length(list)
            self.listlength = listlength

        def setitem(self, item, val):
            setitem(self.list, item, val)

        def lt(self, a, b):
            return lt(a, b)

        def le(self, a, b):
            return not self.lt(b, a)   # always use self.lt() as the primitive

        # binarysort is the best method for sorting small arrays: it does
        # few compares, but can do data movement quadratic in the number of
        # "a" is a contiguous slice of a list, and is sorted via binary insertion.
        # This sort is stable.
        # On entry, the first "sorted" elements are already sorted.
        # Even in case of error, the output slice will be some permutation of

        def binarysort(self, a, sorted=1):
            for start in xrange(a.base + sorted, a.base + a.len):
                # set l to where list[start] belongs
                l = a.base
                r = start
                pivot = a.getitem(r)
                # The second is vacuously true at the start.
                while l < r:
                    p = l + ((r - l) >> 1)
                    if self.lt(pivot, a.getitem(p)):
                        r = p
                    else:
                        l = p+1
                assert l == r
                # The invariants still hold, so pivot >= all in [base, l) and
                # pivot < all in [l, start), so pivot belongs at l.  Note
                # that if there are elements equal to pivot, l points to the
                # first slot after them -- that's why this sort is stable.
                # Slide over to make room.
                for p in xrange(start, l, -1):
                    a.setitem(p, a.getitem(p-1))
                a.setitem(l, pivot)

        # Compute the length of the run in the slice "a".
        # or the longest descending sequence, with
        # For its intended use in a stable mergesort, the strictness of the defn of
        # "descending" is needed so that the caller can safely reverse a descending
        # sequence without violating stability (strict > ensures there are no equal
        # elements to get out of order).

        def count_run(self, a):
            if a.len <= 1:
                n = a.len
                descending = False
            else:
                n = 2
                if self.lt(a.getitem(a.base + 1), a.getitem(a.base)):
                    descending = True
                    for p in xrange(a.base + 2, a.base + a.len):
                        if self.lt(a.getitem(p), a.getitem(p-1)):
                            n += 1
                        else:
                            break
                else:
                    descending = False
                    for p in xrange(a.base + 2, a.base + a.len):
                        if self.lt(a.getitem(p), a.getitem(p-1)):
                            break
                        else:
                            n += 1
            return ListSlice(a.list, a.base, n), descending

        # Locate the proper position of key in a sorted vector; if the vector
        # contains an element equal to key, return the position immediately to the
        # left of the leftmost equal element -- or to the right of the rightmost
        # equal element if the flag "rightmost" is set.
        # "hint" is an index at which to begin the search, 0 <= hint < a.len.
        # The closer hint is to the final result, the faster this runs.
        # The return value is the index 0 <= k <= a.len such that
        # as long as the indices are in bound.  IOW, key belongs at index k;
        # or, IOW, the first k elements of a should precede key, and the last
        # n-k should follow key.

        def gallop(self, key, a, hint, rightmost):
            assert 0 <= hint < a.len
            if rightmost:
                lower = self.le   # search for the largest k for which a[k] <= key
            else:
                lower = self.lt   # search for the largest k for which a[k] < key

            p = a.base + hint
            lastofs = 0
            ofs = 1
            if lower(a.getitem(p), key):
                # a[hint] < key -- gallop right, until

                maxofs = a.len - hint     # a[a.len-1] is highest
                while ofs < maxofs:
                    if lower(a.getitem(p + ofs), key):
                        lastofs = ofs
                        try:
                            ofs = ovfcheck(ofs << 1)
                        except OverflowError:
                            ofs = maxofs
                        else:
                            ofs = ofs + 1
                    else:  # key <= a[hint + ofs]
                        break

                if ofs > maxofs:
                    ofs = maxofs
                # Translate back to offsets relative to a.
                lastofs += hint
                ofs += hint

            else:
                maxofs = hint + 1   # a[0] is lowest
                while ofs < maxofs:
                    if lower(a.getitem(p - ofs), key):
                        break
                    else:
                        lastofs = ofs
                        try:
                            ofs = ovfcheck(ofs << 1)
                        except OverflowError:
                            ofs = maxofs
                        else:
                            ofs = ofs + 1
                if ofs > maxofs:
                    ofs = maxofs
                # Translate back to positive offsets relative to a.
                lastofs, ofs = hint-ofs, hint-lastofs

            assert -1 <= lastofs < ofs <= a.len

            # Now a[lastofs] < key <= a[ofs], so key belongs somewhere to the
            # right of lastofs but no farther right than ofs.  Do a binary

            lastofs += 1
            while lastofs < ofs:
                m = lastofs + ((ofs - lastofs) >> 1)
                if lower(a.getitem(a.base + m), key):
                    lastofs = m+1   # a[m] < key
                else:
                    ofs = m         # key <= a[m]

            assert lastofs == ofs         # so a[ofs-1] < key <= a[ofs]
            return ofs

        # a constant (either True or False), so we can specialize the function for
        # the two cases.  (This is actually needed for technical reasons: the
        # variable 'lower' must contain a known method, which is the case in each
        # specialized version but not in the unspecialized one.)
        gallop._annspecialcase_ = "specialize:arg(4)"

        # ____________________________________________________________

        # When we get into galloping mode, we stay there until both runs win less
        # often than MIN_GALLOP consecutive times.  See listsort.txt for more info.
        MIN_GALLOP = 7

        def merge_init(self):
            # This controls when we get *into* galloping mode.  It's initialized
            # to MIN_GALLOP.  merge_lo and merge_hi tend to nudge it higher for
            # random data, and lower for highly structured data.
            self.min_gallop = self.MIN_GALLOP

            # A stack of n pending runs yet to be merged.  Run #i starts at
            # It's always true (so long as the indices are in bounds) that
            # so we could cut the storage for this, but it's a minor amount,
            # and keeping all the info explicit simplifies the code.
            self.pending = []

        # Merge the slice "a" with the slice "b" in a stable way, in-place.
        # a.len and b.len must be > 0, and a.base + a.len == b.base.
        # Must also have that b.list[b.base] < a.list[a.base], that
        # a.list[a.base+a.len-1] belongs at the end of the merge, and should have

        def merge_lo(self, a, b):
            assert a.len > 0 and b.len > 0 and a.base + a.len == b.base
            min_gallop = self.min_gallop
            dest = a.base
            a = a.copyitems()

            # Invariant: elements in "a" are waiting to be reinserted into the list
            # at "dest".  They should be merged with the elements of "b".
            # We use a finally block to ensure that the elements remaining in
            # the copy "a" are reinserted back into self.list in all cases.
            try:
                self.setitem(dest, b.popleft())
                dest += 1
                if a.len == 1 or b.len == 0:
                    return

                while True:
                    acount = 0   # number of times A won in a row
                    bcount = 0   # number of times B won in a row

                    # Do the straightforward thing until (if ever) one run
                    # appears to win consistently.
                    while True:
                        if self.lt(b.getitem(b.base), a.getitem(a.base)):
                            self.setitem(dest, b.popleft())
                            dest += 1
                            if b.len == 0:
                                return
                            bcount += 1
                            acount = 0
                            if bcount >= min_gallop:
                                break
                        else:
                            self.setitem(dest, a.popleft())
                            dest += 1
                            if a.len == 1:
                                return
                            acount += 1
                            bcount = 0
                            if acount >= min_gallop:
                                break

                    # One run is winning so consistently that galloping may
                    # be a huge win.  So try that, and continue galloping until
                    # (if ever) neither run appears to be winning consistently
                    min_gallop += 1

                    while True:
                        min_gallop -= min_gallop > 1
                        self.min_gallop = min_gallop

                        acount = self.gallop(b.getitem(b.base), a, hint=0,
                                             rightmost=True)
                        for p in xrange(a.base, a.base + acount):
                            self.setitem(dest, a.getitem(p))
                            dest += 1
                        a.advance(acount)
                        # function is consistent, but we can't assume
                        if a.len <= 1:
                            return

                        self.setitem(dest, b.popleft())
                        dest += 1
                        if b.len == 0:
                            return

                        bcount = self.gallop(a.getitem(a.base), b, hint=0,
                                             rightmost=False)
                        for p in xrange(b.base, b.base + bcount):
                            self.setitem(dest, b.getitem(p))
                            dest += 1
                        b.advance(bcount)
                        if b.len == 0:
                            return

                        self.setitem(dest, a.popleft())
                        dest += 1
                        if a.len == 1:
                            return

                        if acount < self.MIN_GALLOP and bcount < self.MIN_GALLOP:
                            break

                    min_gallop += 1  # penalize it for leaving galloping mode
                    self.min_gallop = min_gallop

            finally:
                # The last element of a belongs at the end of the merge, so we copy
                # the remaining elements of b before the remaining elements of a.
                assert a.len >= 0 and b.len >= 0
                for p in xrange(b.base, b.base + b.len):
                    self.setitem(dest, b.getitem(p))
                    dest += 1
                for p in xrange(a.base, a.base + a.len):
                    self.setitem(dest, a.getitem(p))
                    dest += 1

        # Same as merge_lo(), but should have a.len >= b.len.

        def merge_hi(self, a, b):
            assert a.len > 0 and b.len > 0 and a.base + a.len == b.base
            min_gallop = self.min_gallop
            dest = b.base + b.len
            b = b.copyitems()

            # Invariant: elements in "b" are waiting to be reinserted into the list
            # before "dest".  They should be merged with the elements of "a".
            # We use a finally block to ensure that the elements remaining in
            # the copy "b" are reinserted back into self.list in all cases.
            try:
                dest -= 1
                self.setitem(dest, a.popright())
                if a.len == 0 or b.len == 1:
                    return

                while True:
                    acount = 0   # number of times A won in a row
                    bcount = 0   # number of times B won in a row

                    # Do the straightforward thing until (if ever) one run
                    # appears to win consistently.
                    while True:
                        nexta = a.getitem(a.base + a.len - 1)
                        nextb = b.getitem(b.base + b.len - 1)
                        if self.lt(nextb, nexta):
                            dest -= 1
                            self.setitem(dest, nexta)
                            a.len -= 1
                            if a.len == 0:
                                return
                            acount += 1
                            bcount = 0
                            if acount >= min_gallop:
                                break
                        else:
                            dest -= 1
                            self.setitem(dest, nextb)
                            b.len -= 1
                            if b.len == 1:
                                return
                            bcount += 1
                            acount = 0
                            if bcount >= min_gallop:
                                break

                    # One run is winning so consistently that galloping may
                    # be a huge win.  So try that, and continue galloping until
                    # (if ever) neither run appears to be winning consistently
                    min_gallop += 1

                    while True:
                        min_gallop -= min_gallop > 1
                        self.min_gallop = min_gallop

                        nextb = b.getitem(b.base + b.len - 1)
                        k = self.gallop(nextb, a, hint=a.len-1, rightmost=True)
                        acount = a.len - k
                        for p in xrange(a.base + a.len - 1, a.base + k - 1, -1):
                            dest -= 1
                            self.setitem(dest, a.getitem(p))
                        a.len -= acount
                        if a.len == 0:
                            return

                        dest -= 1
                        self.setitem(dest, b.popright())
                        if b.len == 1:
                            return

                        nexta = a.getitem(a.base + a.len - 1)
                        k = self.gallop(nexta, b, hint=b.len-1, rightmost=False)
                        bcount = b.len - k
                        for p in xrange(b.base + b.len - 1, b.base + k - 1, -1):
                            dest -= 1
                            self.setitem(dest, b.getitem(p))
                        b.len -= bcount
                        # function is consistent, but we can't assume
                        if b.len <= 1:
                            return

                        dest -= 1
                        self.setitem(dest, a.popright())
                        if a.len == 0:
                            return

                        if acount < self.MIN_GALLOP and bcount < self.MIN_GALLOP:
                            break

                    min_gallop += 1  # penalize it for leaving galloping mode
                    self.min_gallop = min_gallop

            finally:
                # The last element of a belongs at the end of the merge, so we copy
                # the remaining elements of a and then the remaining elements of b.
                assert a.len >= 0 and b.len >= 0
                for p in xrange(a.base + a.len - 1, a.base - 1, -1):
                    dest -= 1
                    self.setitem(dest, a.getitem(p))
                for p in xrange(b.base + b.len - 1, b.base - 1, -1):
                    dest -= 1
                    self.setitem(dest, b.getitem(p))

        # Merge the two runs at stack indices i and i+1.

        def merge_at(self, i):
            a = self.pending[i]
            b = self.pending[i+1]
            assert a.len > 0 and b.len > 0
            assert a.base + a.len == b.base

            # Record the length of the combined runs and remove the run b
            self.pending[i] = ListSlice(self.list, a.base, a.len + b.len)
            del self.pending[i+1]

            # Where does b start in a?  Elements in a before that can be
            k = self.gallop(b.getitem(b.base), a, hint=0, rightmost=True)
            a.advance(k)
            if a.len == 0:
                return

            # Where does a end in b?  Elements in b after that can be
            b.len = self.gallop(a.getitem(a.base+a.len-1), b, hint=b.len-1,
                                rightmost=False)
            if b.len == 0:
                return

            # Merge what remains of the runs.  The direction is chosen to
            # minimize the temporary storage needed.
            if a.len <= b.len:
                self.merge_lo(a, b)
            else:
                self.merge_hi(a, b)

        # Examine the stack of runs waiting to be merged, merging adjacent runs
        # until the stack invariants are re-established:
        # Note these invariants will not hold for the entire pending array even
        # after this function completes. [1] This does not affect the
        # correctness of the overall algorithm.
        # See listsort.txt for more info.

        def merge_collapse(self):
            p = self.pending
            while len(p) > 1:
                if len(p) >= 3 and p[-3].len <= p[-2].len + p[-1].len:
                    if p[-3].len < p[-1].len:
                        self.merge_at(-3)
                    else:
                        self.merge_at(-2)
                elif p[-2].len <= p[-1].len:
                    self.merge_at(-2)
                else:
                    break

        # Regardless of invariants, merge all runs on the stack until only one
        # remains.  This is used at the end of the mergesort.

        def merge_force_collapse(self):
            p = self.pending
            while len(p) > 1:
                if len(p) >= 3 and p[-3].len < p[-1].len:
                    self.merge_at(-3)
                else:
                    self.merge_at(-2)

        # Compute a good value for the minimum run length; natural runs shorter
        # than this are boosted artificially via binary insertion.
        # If n < 64, return n (it's too small to bother with fancy stuff).
        # Else if n is an exact power of 2, return 32.
        # Else return an int k, 32 <= k <= 64, such that n/k is close to, but
        # strictly less than, an exact power of 2.
        # See listsort.txt for more info.

        def merge_compute_minrun(self, n):
            r = 0    # becomes 1 if any 1 bits are shifted off
            while n >= 64:
                r |= n & 1
                n >>= 1
            return n + r

        # ____________________________________________________________
        # Entry point.

        def sort(self):
            remaining = ListSlice(self.list, 0, self.listlength)
            if remaining.len < 2:
                return

            # March over the array once, left to right, finding natural runs,
            # and extending short natural runs to minrun elements.
            self.merge_init()
            minrun = self.merge_compute_minrun(remaining.len)

            while remaining.len > 0:
                # Identify next run.
                run, descending = self.count_run(remaining)
                if descending:
                    run.reverse()
                # If short, extend to min(minrun, nremaining).
                if run.len < minrun:
                    sorted = run.len
                    run.len = min(minrun, remaining.len)
                    self.binarysort(run, sorted)
                # Advance remaining past this run.
                remaining.advance(run.len)
                # Push run onto pending-runs stack, and maybe merge.
                self.pending.append(run)
                self.merge_collapse()

            assert remaining.base == self.listlength

            self.merge_force_collapse()
            assert len(self.pending) == 1
            assert self.pending[0].base == 0
            assert self.pending[0].len == self.listlength

    class ListSlice:
        "A sublist of a list."

        def __init__(self, list, base, len):
            self.list = list
            self.base = base
            self.len  = len

        def copyitems(self):
            "Make a copy of the slice of the original list."

        def advance(self, n):
            self.base += n
            self.len -= n

        def getitem(self, item):
            return getitem(self.list, item)

        def setitem(self, item, value):
            setitem(self.list, item, value)

        def popleft(self):
            result = getitem(self.list, self.base)
            self.base += 1
            self.len -= 1
            return result

        def popright(self):
            self.len -= 1
            return getitem(self.list, self.base + self.len)

        def reverse(self):
            "Reverse the slice in-place."
            list = self.list
            lo = self.base
            hi = lo + self.len - 1
            while lo < hi:
                list_hi = getitem(list, hi)
                list_lo = getitem(list, lo)
                setitem(list, lo, list_hi)
                setitem(list, hi, list_lo)
                lo += 1
                hi -= 1
    return TimSort

TimSort = make_timsort_class() #backward compatible interface


while ofs < maxofs:
                    if lower(a.getitem(p + ofs), key):
                        lastofs = ofs
                        try:
                            ofs = ovfcheck(ofs << 1)
                        except OverflowError:
                            ofs = maxofs
                        else:
                            ofs = ofs + 1
                    else:  # key <= a[hint + ofs]
                        break

from rpython.rlib.rarithmetic import ovfcheck

## Lots of code for an adaptive, stable, natural mergesort.  There are many
## pieces to this algorithm; read listsort.txt for overviews and details.
##         Adapted from CPython, original code and algorithms by Tim Peters

def make_timsort_class(getitem=None, setitem=None, length=None,
                       getitem_slice=None, lt=None):

    if getitem is None:
        def getitem(list, item):
            return list[item]

    if setitem is None:
        def setitem(list, item, value):
            list[item] = value

    if length is None:
        def length(list):
            return len(list)

    if getitem_slice is None:
        def getitem_slice(list, start, stop):
            return list[start:stop]

    if lt is None:
        def lt(a, b):
            return a < b

    class TimSort(object):
        """TimSort(list).sort()

        Sorts the list in-place, using the overridable method lt() for comparison.
        """

        def __init__(self, list, listlength=None):
            self.list = list
            if listlength is None:
                listlength = length(list)
            self.listlength = listlength

        def setitem(self, item, val):
            setitem(self.list, item, val)

        def lt(self, a, b):
            return lt(a, b)

        def le(self, a, b):
            return not self.lt(b, a)   # always use self.lt() as the primitive

        # binarysort is the best method for sorting small arrays: it does
        # few compares, but can do data movement quadratic in the number of
        # "a" is a contiguous slice of a list, and is sorted via binary insertion.
        # This sort is stable.
        # On entry, the first "sorted" elements are already sorted.
        # Even in case of error, the output slice will be some permutation of

        def binarysort(self, a, sorted=1):
            for start in xrange(a.base + sorted, a.base + a.len):
                # set l to where list[start] belongs
                l = a.base
                r = start
                pivot = a.getitem(r)
                # The second is vacuously true at the start.
                while l < r:
                    p = l + ((r - l) >> 1)
                    if self.lt(pivot, a.getitem(p)):
                        r = p
                    else:
                        l = p+1
                assert l == r
                # The invariants still hold, so pivot >= all in [base, l) and
                # pivot < all in [l, start), so pivot belongs at l.  Note
                # that if there are elements equal to pivot, l points to the
                # first slot after them -- that's why this sort is stable.
                # Slide over to make room.
                for p in xrange(start, l, -1):
                    a.setitem(p, a.getitem(p-1))
                a.setitem(l, pivot)

        # Compute the length of the run in the slice "a".
        # or the longest descending sequence, with
        # For its intended use in a stable mergesort, the strictness of the defn of
        # "descending" is needed so that the caller can safely reverse a descending
        # sequence without violating stability (strict > ensures there are no equal
        # elements to get out of order).

        def count_run(self, a):
            if a.len <= 1:
                n = a.len
                descending = False
            else:
                n = 2
                if self.lt(a.getitem(a.base + 1), a.getitem(a.base)):
                    descending = True
                    for p in xrange(a.base + 2, a.base + a.len):
                        if self.lt(a.getitem(p), a.getitem(p-1)):
                            n += 1
                        else:
                            break
                else:
                    descending = False
                    for p in xrange(a.base + 2, a.base + a.len):
                        if self.lt(a.getitem(p), a.getitem(p-1)):
                            break
                        else:
                            n += 1
            return ListSlice(a.list, a.base, n), descending

        # Locate the proper position of key in a sorted vector; if the vector
        # contains an element equal to key, return the position immediately to the
        # left of the leftmost equal element -- or to the right of the rightmost
        # equal element if the flag "rightmost" is set.
        # "hint" is an index at which to begin the search, 0 <= hint < a.len.
        # The closer hint is to the final result, the faster this runs.
        # The return value is the index 0 <= k <= a.len such that
        # as long as the indices are in bound.  IOW, key belongs at index k;
        # or, IOW, the first k elements of a should precede key, and the last
        # n-k should follow key.

        def gallop(self, key, a, hint, rightmost):
            assert 0 <= hint < a.len
            if rightmost:
                lower = self.le   # search for the largest k for which a[k] <= key
            else:
                lower = self.lt   # search for the largest k for which a[k] < key

            p = a.base + hint
            lastofs = 0
            ofs = 1
            if lower(a.getitem(p), key):
                # a[hint] < key -- gallop right, until

                maxofs = a.len - hint     # a[a.len-1] is highest

                if ofs > maxofs:
                    ofs = maxofs
                # Translate back to offsets relative to a.
                lastofs += hint
                ofs += hint

            else:
                maxofs = hint + 1   # a[0] is lowest
                while ofs < maxofs:
                    if lower(a.getitem(p - ofs), key):
                        break
                    else:
                        lastofs = ofs
                        try:
                            ofs = ovfcheck(ofs << 1)
                        except OverflowError:
                            ofs = maxofs
                        else:
                            ofs = ofs + 1
                if ofs > maxofs:
                    ofs = maxofs
                # Translate back to positive offsets relative to a.
                lastofs, ofs = hint-ofs, hint-lastofs

            assert -1 <= lastofs < ofs <= a.len

            # Now a[lastofs] < key <= a[ofs], so key belongs somewhere to the
            # right of lastofs but no farther right than ofs.  Do a binary

            lastofs += 1
            while lastofs < ofs:
                m = lastofs + ((ofs - lastofs) >> 1)
                if lower(a.getitem(a.base + m), key):
                    lastofs = m+1   # a[m] < key
                else:
                    ofs = m         # key <= a[m]

            assert lastofs == ofs         # so a[ofs-1] < key <= a[ofs]
            return ofs

        # a constant (either True or False), so we can specialize the function for
        # the two cases.  (This is actually needed for technical reasons: the
        # variable 'lower' must contain a known method, which is the case in each
        # specialized version but not in the unspecialized one.)
        gallop._annspecialcase_ = "specialize:arg(4)"

        # ____________________________________________________________

        # When we get into galloping mode, we stay there until both runs win less
        # often than MIN_GALLOP consecutive times.  See listsort.txt for more info.
        MIN_GALLOP = 7

        def merge_init(self):
            # This controls when we get *into* galloping mode.  It's initialized
            # to MIN_GALLOP.  merge_lo and merge_hi tend to nudge it higher for
            # random data, and lower for highly structured data.
            self.min_gallop = self.MIN_GALLOP

            # A stack of n pending runs yet to be merged.  Run #i starts at
            # It's always true (so long as the indices are in bounds) that
            # so we could cut the storage for this, but it's a minor amount,
            # and keeping all the info explicit simplifies the code.
            self.pending = []

        # Merge the slice "a" with the slice "b" in a stable way, in-place.
        # a.len and b.len must be > 0, and a.base + a.len == b.base.
        # Must also have that b.list[b.base] < a.list[a.base], that
        # a.list[a.base+a.len-1] belongs at the end of the merge, and should have

        def merge_lo(self, a, b):
            assert a.len > 0 and b.len > 0 and a.base + a.len == b.base
            min_gallop = self.min_gallop
            dest = a.base
            a = a.copyitems()

            # Invariant: elements in "a" are waiting to be reinserted into the list
            # at "dest".  They should be merged with the elements of "b".
            # We use a finally block to ensure that the elements remaining in
            # the copy "a" are reinserted back into self.list in all cases.
            try:
                self.setitem(dest, b.popleft())
                dest += 1
                if a.len == 1 or b.len == 0:
                    return

                while True:
                    acount = 0   # number of times A won in a row
                    bcount = 0   # number of times B won in a row

                    # Do the straightforward thing until (if ever) one run
                    # appears to win consistently.
                    while True:
                        if self.lt(b.getitem(b.base), a.getitem(a.base)):
                            self.setitem(dest, b.popleft())
                            dest += 1
                            if b.len == 0:
                                return
                            bcount += 1
                            acount = 0
                            if bcount >= min_gallop:
                                break
                        else:
                            self.setitem(dest, a.popleft())
                            dest += 1
                            if a.len == 1:
                                return
                            acount += 1
                            bcount = 0
                            if acount >= min_gallop:
                                break

                    # One run is winning so consistently that galloping may
                    # be a huge win.  So try that, and continue galloping until
                    # (if ever) neither run appears to be winning consistently
                    min_gallop += 1

                    while True:
                        min_gallop -= min_gallop > 1
                        self.min_gallop = min_gallop

                        acount = self.gallop(b.getitem(b.base), a, hint=0,
                                             rightmost=True)
                        for p in xrange(a.base, a.base + acount):
                            self.setitem(dest, a.getitem(p))
                            dest += 1
                        a.advance(acount)
                        # function is consistent, but we can't assume
                        if a.len <= 1:
                            return

                        self.setitem(dest, b.popleft())
                        dest += 1
                        if b.len == 0:
                            return

                        bcount = self.gallop(a.getitem(a.base), b, hint=0,
                                             rightmost=False)
                        for p in xrange(b.base, b.base + bcount):
                            self.setitem(dest, b.getitem(p))
                            dest += 1
                        b.advance(bcount)
                        if b.len == 0:
                            return

                        self.setitem(dest, a.popleft())
                        dest += 1
                        if a.len == 1:
                            return

                        if acount < self.MIN_GALLOP and bcount < self.MIN_GALLOP:
                            break

                    min_gallop += 1  # penalize it for leaving galloping mode
                    self.min_gallop = min_gallop

            finally:
                # The last element of a belongs at the end of the merge, so we copy
                # the remaining elements of b before the remaining elements of a.
                assert a.len >= 0 and b.len >= 0
                for p in xrange(b.base, b.base + b.len):
                    self.setitem(dest, b.getitem(p))
                    dest += 1
                for p in xrange(a.base, a.base + a.len):
                    self.setitem(dest, a.getitem(p))
                    dest += 1

        # Same as merge_lo(), but should have a.len >= b.len.

        def merge_hi(self, a, b):
            assert a.len > 0 and b.len > 0 and a.base + a.len == b.base
            min_gallop = self.min_gallop
            dest = b.base + b.len
            b = b.copyitems()

            # Invariant: elements in "b" are waiting to be reinserted into the list
            # before "dest".  They should be merged with the elements of "a".
            # We use a finally block to ensure that the elements remaining in
            # the copy "b" are reinserted back into self.list in all cases.
            try:
                dest -= 1
                self.setitem(dest, a.popright())
                if a.len == 0 or b.len == 1:
                    return

                while True:
                    acount = 0   # number of times A won in a row
                    bcount = 0   # number of times B won in a row

                    # Do the straightforward thing until (if ever) one run
                    # appears to win consistently.
                    while True:
                        nexta = a.getitem(a.base + a.len - 1)
                        nextb = b.getitem(b.base + b.len - 1)
                        if self.lt(nextb, nexta):
                            dest -= 1
                            self.setitem(dest, nexta)
                            a.len -= 1
                            if a.len == 0:
                                return
                            acount += 1
                            bcount = 0
                            if acount >= min_gallop:
                                break
                        else:
                            dest -= 1
                            self.setitem(dest, nextb)
                            b.len -= 1
                            if b.len == 1:
                                return
                            bcount += 1
                            acount = 0
                            if bcount >= min_gallop:
                                break

                    # One run is winning so consistently that galloping may
                    # be a huge win.  So try that, and continue galloping until
                    # (if ever) neither run appears to be winning consistently
                    min_gallop += 1

                    while True:
                        min_gallop -= min_gallop > 1
                        self.min_gallop = min_gallop

                        nextb = b.getitem(b.base + b.len - 1)
                        k = self.gallop(nextb, a, hint=a.len-1, rightmost=True)
                        acount = a.len - k
                        for p in xrange(a.base + a.len - 1, a.base + k - 1, -1):
                            dest -= 1
                            self.setitem(dest, a.getitem(p))
                        a.len -= acount
                        if a.len == 0:
                            return

                        dest -= 1
                        self.setitem(dest, b.popright())
                        if b.len == 1:
                            return

                        nexta = a.getitem(a.base + a.len - 1)
                        k = self.gallop(nexta, b, hint=b.len-1, rightmost=False)
                        bcount = b.len - k
                        for p in xrange(b.base + b.len - 1, b.base + k - 1, -1):
                            dest -= 1
                            self.setitem(dest, b.getitem(p))
                        b.len -= bcount
                        # function is consistent, but we can't assume
                        if b.len <= 1:
                            return

                        dest -= 1
                        self.setitem(dest, a.popright())
                        if a.len == 0:
                            return

                        if acount < self.MIN_GALLOP and bcount < self.MIN_GALLOP:
                            break

                    min_gallop += 1  # penalize it for leaving galloping mode
                    self.min_gallop = min_gallop

            finally:
                # The last element of a belongs at the end of the merge, so we copy
                # the remaining elements of a and then the remaining elements of b.
                assert a.len >= 0 and b.len >= 0
                for p in xrange(a.base + a.len - 1, a.base - 1, -1):
                    dest -= 1
                    self.setitem(dest, a.getitem(p))
                for p in xrange(b.base + b.len - 1, b.base - 1, -1):
                    dest -= 1
                    self.setitem(dest, b.getitem(p))

        # Merge the two runs at stack indices i and i+1.

        def merge_at(self, i):
            a = self.pending[i]
            b = self.pending[i+1]
            assert a.len > 0 and b.len > 0
            assert a.base + a.len == b.base

            # Record the length of the combined runs and remove the run b
            self.pending[i] = ListSlice(self.list, a.base, a.len + b.len)
            del self.pending[i+1]

            # Where does b start in a?  Elements in a before that can be
            k = self.gallop(b.getitem(b.base), a, hint=0, rightmost=True)
            a.advance(k)
            if a.len == 0:
                return

            # Where does a end in b?  Elements in b after that can be
            b.len = self.gallop(a.getitem(a.base+a.len-1), b, hint=b.len-1,
                                rightmost=False)
            if b.len == 0:
                return

            # Merge what remains of the runs.  The direction is chosen to
            # minimize the temporary storage needed.
            if a.len <= b.len:
                self.merge_lo(a, b)
            else:
                self.merge_hi(a, b)

        # Examine the stack of runs waiting to be merged, merging adjacent runs
        # until the stack invariants are re-established:
        # Note these invariants will not hold for the entire pending array even
        # after this function completes. [1] This does not affect the
        # correctness of the overall algorithm.
        # See listsort.txt for more info.

        def merge_collapse(self):
            p = self.pending
            while len(p) > 1:
                if len(p) >= 3 and p[-3].len <= p[-2].len + p[-1].len:
                    if p[-3].len < p[-1].len:
                        self.merge_at(-3)
                    else:
                        self.merge_at(-2)
                elif p[-2].len <= p[-1].len:
                    self.merge_at(-2)
                else:
                    break

        # Regardless of invariants, merge all runs on the stack until only one
        # remains.  This is used at the end of the mergesort.

        def merge_force_collapse(self):
            p = self.pending
            while len(p) > 1:
                if len(p) >= 3 and p[-3].len < p[-1].len:
                    self.merge_at(-3)
                else:
                    self.merge_at(-2)

        # Compute a good value for the minimum run length; natural runs shorter
        # than this are boosted artificially via binary insertion.
        # If n < 64, return n (it's too small to bother with fancy stuff).
        # Else if n is an exact power of 2, return 32.
        # Else return an int k, 32 <= k <= 64, such that n/k is close to, but
        # strictly less than, an exact power of 2.
        # See listsort.txt for more info.

        def merge_compute_minrun(self, n):
            r = 0    # becomes 1 if any 1 bits are shifted off
            while n >= 64:
                r |= n & 1
                n >>= 1
            return n + r

        # ____________________________________________________________
        # Entry point.

        def sort(self):
            remaining = ListSlice(self.list, 0, self.listlength)
            if remaining.len < 2:
                return

            # March over the array once, left to right, finding natural runs,
            # and extending short natural runs to minrun elements.
            self.merge_init()
            minrun = self.merge_compute_minrun(remaining.len)

            while remaining.len > 0:
                # Identify next run.
                run, descending = self.count_run(remaining)
                if descending:
                    run.reverse()
                # If short, extend to min(minrun, nremaining).
                if run.len < minrun:
                    sorted = run.len
                    run.len = min(minrun, remaining.len)
                    self.binarysort(run, sorted)
                # Advance remaining past this run.
                remaining.advance(run.len)
                # Push run onto pending-runs stack, and maybe merge.
                self.pending.append(run)
                self.merge_collapse()

            assert remaining.base == self.listlength

            self.merge_force_collapse()
            assert len(self.pending) == 1
            assert self.pending[0].base == 0
            assert self.pending[0].len == self.listlength

    class ListSlice:
        "A sublist of a list."

        def __init__(self, list, base, len):
            self.list = list
            self.base = base
            self.len  = len

        def copyitems(self):
            "Make a copy of the slice of the original list."
            start = self.base
            stop  = self.base + self.len
            assert 0 <= start <= stop     # annotator hint
            return ListSlice(getitem_slice(self.list, start, stop), 0, self.len)

        def advance(self, n):
            self.base += n
            self.len -= n

        def getitem(self, item):
            return getitem(self.list, item)

        def setitem(self, item, value):
            setitem(self.list, item, value)

        def popleft(self):
            result = getitem(self.list, self.base)
            self.base += 1
            self.len -= 1
            return result

        def popright(self):
            self.len -= 1
            return getitem(self.list, self.base + self.len)

        def reverse(self):
            "Reverse the slice in-place."
            list = self.list
            lo = self.base
            hi = lo + self.len - 1
            while lo < hi:
                list_hi = getitem(list, hi)
                list_lo = getitem(list, lo)
                setitem(list, lo, list_hi)
                setitem(list, hi, list_lo)
                lo += 1
                hi -= 1
    return TimSort

TimSort = make_timsort_class() #backward compatible interface


        self._TypeAssets = []
        self.TypeAssets = [] if TypeAssets is None else TypeAssets

        self.status = status

        super(TypeAssetCatalogue, self).__init__(*args, **kw_args)

from CIM15.IEC61970.Core.IdentifiedObject import IdentifiedObject

class TypeAssetCatalogue(IdentifiedObject):
    """Catalogue of generic types of assets (TypeAsset) that may be used for design purposes. It is not associated with a particular manufacturer.Catalogue of generic types of assets (TypeAsset) that may be used for design purposes. It is not associated with a particular manufacturer.
    """

    def __init__(self, TypeAssets=None, status=None, *args, **kw_args):
        """Initialises a new 'TypeAssetCatalogue' instance.

        @param TypeAssets:
        @param status:
        """

    _attrs = []
    _attr_types = {}
    _defaults = {}
    _enums = {}
    _refs = ["TypeAssets", "status"]
    _many_refs = ["TypeAssets"]

    def getTypeAssets(self):

        return self._TypeAssets

    def setTypeAssets(self, value):
        for x in self._TypeAssets:
            x.TypeAssetCatalogue = None
        for y in value:
            y._TypeAssetCatalogue = self
        self._TypeAssets = value

    TypeAssets = property(getTypeAssets, setTypeAssets)

    def addTypeAssets(self, *TypeAssets):
        for obj in TypeAssets:
            obj.TypeAssetCatalogue = self

    def removeTypeAssets(self, *TypeAssets):
        for obj in TypeAssets:
            obj.TypeAssetCatalogue = None

    status = None


while refcount[key]:
                        key = queue_popleft()
                        refcount[key] -= 1

# ActiveState Code Recipe
# License: PSF
# Author: Raymond Hettinger

from __future__ import print_function

import collections
import functools
from itertools import ifilterfalse
from heapq import nsmallest
from operator import itemgetter

class Counter(dict):
    'Mapping where default values are zero'
    def __missing__(self, key):
        return 0

def lru_cache(maxsize=100):
    '''Least-recently-used cache decorator.

    Arguments to the cached function must be hashable.
    Cache performance statistics stored in f.hits and f.misses.
    Clear the cache with f.clear().
    http://example.com

    '''
    maxqueue = maxsize * 10
    def decorating_function(user_function,
            len=len, iter=iter, tuple=tuple, sorted=sorted, KeyError=KeyError):
        cache = {}                  # mapping of args to results
        queue = collections.deque() # order that keys have been used
        refcount = Counter()        # times each key is in the queue
        sentinel = object()         # marker for looping around the queue
        kwd_mark = object()         # separate positional and keyword args

        # lookup optimizations (ugly but fast)
        queue_append, queue_popleft = queue.append, queue.popleft
        queue_appendleft, queue_pop = queue.appendleft, queue.pop

        @functools.wraps(user_function)
        def wrapper(*args, **kwds):
            # cache key records both positional and keyword args
            key = args
            if kwds:
                key += (kwd_mark,) + tuple(sorted(kwds.items()))

            # record recent use of this key
            queue_append(key)
            refcount[key] += 1

            # get cache entry or compute if not found
            try:
                result = cache[key]
                wrapper.hits += 1
            except KeyError:
                result = user_function(*args, **kwds)
                cache[key] = result
                wrapper.misses += 1

                # purge least recently used cache entry
                if len(cache) > maxsize:
                    key = queue_popleft()
                    refcount[key] -= 1

                    del cache[key], refcount[key]

            # periodically compact the queue by eliminating duplicate keys
            # while preserving order of most recent access
            if len(queue) > maxqueue:
                refcount.clear()
                queue_appendleft(sentinel)
                for key in ifilterfalse(refcount.__contains__,
                                        iter(queue_pop, sentinel)):
                    queue_appendleft(key)
                    refcount[key] = 1

            return result

        def clear():
            cache.clear()
            queue.clear()
            refcount.clear()
            wrapper.hits = wrapper.misses = 0

        wrapper.hits = wrapper.misses = 0
        wrapper.clear = clear
        return wrapper
    return decorating_function

def lfu_cache(maxsize=100):
    '''Least-frequenty-used cache decorator.

    Arguments to the cached function must be hashable.
    Cache performance statistics stored in f.hits and f.misses.
    Clear the cache with f.clear().
    http://example.com

    '''
    def decorating_function(user_function):
        cache = {}                      # mapping of args to results
        use_count = Counter()           # times each key has been accessed
        kwd_mark = object()             # separate positional and keyword args

        @functools.wraps(user_function)
        def wrapper(*args, **kwds):
            key = args
            if kwds:
                key += (kwd_mark,) + tuple(sorted(kwds.items()))
            use_count[key] += 1

            # get cache entry or compute if not found
            try:
                result = cache[key]
                wrapper.hits += 1
            except KeyError:
                result = user_function(*args, **kwds)
                cache[key] = result
                wrapper.misses += 1

                # purge least frequently used cache entry
                if len(cache) > maxsize:
                    for key, _ in nsmallest(maxsize // 10,
                                            use_count.iteritems(),
                                            key=itemgetter(1)):
                        del cache[key], use_count[key]

            return result

        def clear():
            cache.clear()
            use_count.clear()
            wrapper.hits = wrapper.misses = 0

        wrapper.hits = wrapper.misses = 0
        wrapper.clear = clear
        return wrapper
    return decorating_function

if __name__ == '__main__':

    @lru_cache(maxsize=20)
    def f(x, y):
        return 3*x+y

    domain = range(5)
    from random import choice
    for i in range(1000):
        r = f(choice(domain), choice(domain))

    print(f.hits, f.misses)

    @lfu_cache(maxsize=20)
    def f(x, y):
        return 3*x+y

    domain = range(5)
    from random import choice
    for i in range(1000):
        r = f(choice(domain), choice(domain))

    print(f.hits, f.misses)