{
    "file_diffs": [
        {
            "old_file_content": "\"\"\"\nVenn Diagram Widget\n-------------------\n\n\"\"\"\n\nimport math\nimport unicodedata\nfrom collections import namedtuple, defaultdict\nfrom itertools import compress, count\nfrom functools import reduce\nfrom operator import attrgetter\nfrom xml.sax.saxutils import escape\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import (\n    QGraphicsScene, QGraphicsView, QGraphicsWidget,\n    QGraphicsPathItem, QGraphicsTextItem, QStyle, QSizePolicy\n)\nfrom AnyQt.QtGui import (\n    QPainterPath, QPainter, QTransform, QColor, QBrush, QPen, QPalette\n)\nfrom AnyQt.QtCore import Qt, QPointF, QRectF, QLineF\nfrom AnyQt.QtCore import pyqtSignal as Signal\n\nfrom Orange.data import Table, Domain, StringVariable, RowInstance\nfrom Orange.data.util import get_unique_names_duplicates\nfrom Orange.widgets import widget, gui, settings\nfrom Orange.widgets.utils import itemmodels, colorpalettes\nfrom Orange.widgets.utils.annotated_data import (create_annotated_table,\n                                                 ANNOTATED_DATA_SIGNAL_NAME)\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output, Msg\n\n\n_InputData = namedtuple(\"_InputData\", [\"key\", \"name\", \"table\"])\n_ItemSet = namedtuple(\"_ItemSet\", [\"key\", \"name\", \"title\", \"items\"])\n\n\nclass OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables\n\ndef string_attributes(domain):\n    \"\"\"\n    Return all string attributes from the domain.\n    \"\"\"\n    return [attr for attr in domain.variables + domain.metas if attr.is_string]\n\ndef source_attributes(domain):\n    \"\"\"\n    Return all suitable attributes for the venn diagram.\n    \"\"\"\n    return string_attributes(domain)  # + discrete_attributes(domain)\n\n\ndef disjoint_set_label(i, n, simplify=False):\n    \"\"\"\n    Return a html formated label for a disjoint set indexed by `i`.\n    \"\"\"\n    intersection = unicodedata.lookup(\"INTERSECTION\")\n    # comp = unicodedata.lookup(\"COMPLEMENT\")  #\n    # This depends on the font but the unicode complement in\n    # general does not look nice in a super script so we use\n    # plain c instead.\n    comp = \"c\"\n\n    def label_for_index(i):\n        return chr(ord(\"A\") + i)\n\n    if simplify:\n        return \"\".join(label_for_index(i) for i, b in enumerate(setkey(i, n))\n                       if b)\n    else:\n        return intersection.join(label_for_index(i) +\n                                 (\"\" if b else \"<sup>\" + comp + \"</sup>\")\n                                 for i, b in enumerate(setkey(i, n)))\n\n\nclass VennSetItem(QGraphicsPathItem):\n    def __init__(self, parent=None, text=\"\", informativeText=\"\"):\n        super(VennSetItem, self).__init__(parent)\n        # Plain text title (editable by the VennDiagram)\n        self.text = text\n        # Extra informative text (possibly rich text)\n        self.informativeText = informativeText\n\n# TODO: Use palette's selected/highligted text / background colors to\n# indicate selection\n\n\nclass VennIntersectionArea(QGraphicsPathItem):\n    def __init__(self, parent=None, text=\"\"):\n        super().__init__(parent)\n        self.setAcceptHoverEvents(True)\n        self.setPen(QPen(Qt.NoPen))\n\n        self.text = QGraphicsTextItem(self)\n        layout = self.text.document().documentLayout()\n        layout.documentSizeChanged.connect(self._onLayoutChanged)\n\n        self._text = text\n        self._anchor = QPointF()\n\n    def setText(self, text):\n        if self._text != text:\n            self._text = text\n            self.text.setPlainText(text)\n\n    def setTextAnchor(self, pos):\n        if self._anchor != pos:\n            self._anchor = pos\n            self._updateTextAnchor()\n\n    def hoverEnterEvent(self, event):\n        self.setZValue(self.zValue() + 1)\n        return QGraphicsPathItem.hoverEnterEvent(self, event)\n\n    def hoverLeaveEvent(self, event):\n        self.setZValue(self.zValue() - 1)\n        return QGraphicsPathItem.hoverLeaveEvent(self, event)\n\n    def mousePressEvent(self, event):\n        if event.button() == Qt.LeftButton:\n            if event.modifiers() & Qt.AltModifier:\n                self.setSelected(False)\n            elif event.modifiers() & Qt.ControlModifier:\n                self.setSelected(not self.isSelected())\n            elif event.modifiers() & Qt.ShiftModifier:\n                self.setSelected(True)\n            else:\n                for area in self.parentWidget().vennareas():\n                    area.setSelected(False)\n                self.setSelected(True)\n\n    def mouseReleaseEvent(self, event):\n        pass\n\n    def paint(self, painter, option, _widget=None):\n        painter.save()\n        path = self.path()\n        brush = QBrush(self.brush())\n        pen = QPen(self.pen())\n\n        if option.state & QStyle.State_Selected:\n            pen.setColor(Qt.red)\n            brush.setStyle(Qt.DiagCrossPattern)\n            brush.setColor(QColor(40, 40, 40, 100))\n\n        elif option.state & QStyle.State_MouseOver:\n            pen.setColor(Qt.blue)\n\n        if option.state & QStyle.State_MouseOver:\n            brush.setColor(QColor(100, 100, 100, 100))\n            if brush.style() == Qt.NoBrush:\n                # Make sure the highlight is actually visible.\n                brush.setStyle(Qt.SolidPattern)\n\n        painter.setPen(pen)\n        painter.setBrush(brush)\n        painter.drawPath(path)\n        painter.restore()\n\n    def itemChange(self, change, value):\n        if change == QGraphicsPathItem.ItemSelectedHasChanged:\n            self.setZValue(self.zValue() + (1 if value else -1))\n        return QGraphicsPathItem.itemChange(self, change, value)\n\n    def _updateTextAnchor(self):\n        rect = self.text.boundingRect()\n        pos = anchor_rect(rect, self._anchor)\n        self.text.setPos(pos)\n\n    def _onLayoutChanged(self):\n        self._updateTextAnchor()\n\n\nclass GraphicsTextEdit(QGraphicsTextItem):\n    #: Edit triggers\n    NoEditTriggers, DoubleClicked = 0, 1\n\n    editingFinished = Signal()\n    editingStarted = Signal()\n\n    documentSizeChanged = Signal()\n\n    def __init__(self, *args, **kwargs):\n        super(GraphicsTextEdit, self).__init__(*args, **kwargs)\n        self.setCursor(Qt.IBeamCursor)\n        self.setTabChangesFocus(True)\n        self._edittrigger = GraphicsTextEdit.DoubleClicked\n        self._editing = False\n        self.document().documentLayout().documentSizeChanged.connect(\n            self.documentSizeChanged\n        )\n\n    def mouseDoubleClickEvent(self, event):\n        super(GraphicsTextEdit, self).mouseDoubleClickEvent(event)\n        if self._edittrigger == GraphicsTextEdit.DoubleClicked:\n            self._start()\n\n    def focusOutEvent(self, event):\n        super(GraphicsTextEdit, self).focusOutEvent(event)\n\n        if self._editing:\n            self._end()\n\n    def _start(self):\n        self._editing = True\n        self.setTextInteractionFlags(Qt.TextEditorInteraction)\n        self.setFocus(Qt.MouseFocusReason)\n        self.editingStarted.emit()\n\n    def _end(self):\n        self._editing = False\n        self.setTextInteractionFlags(Qt.NoTextInteraction)\n        self.editingFinished.emit()\n\n\nclass VennDiagram(QGraphicsWidget):\n    # rect and petal are for future work\n    Circle, Ellipse, Rect, Petal = 1, 2, 3, 4\n\n    TitleFormat = \"<center><h4>{0}</h4>{1}</center>\"\n\n    selectionChanged = Signal()\n    itemTextEdited = Signal(int, str)\n\n    def __init__(self, parent=None):\n        super(VennDiagram, self).__init__(parent)\n        self.shapeType = VennDiagram.Circle\n        self._items = []\n        self._vennareas = []\n        self._textitems = []\n        self._subsettextitems = []\n        self._textanchors = []\n\n    def item(self, index):\n        return self._items[index]\n\n    def items(self):\n        return list(self._items)\n\n    def count(self):\n        return len(self._items)\n\n    def setItems(self, items):\n        if self._items:\n            self.clear()\n\n        self._items = list(items)\n\n        for item in self._items:\n            item.setParentItem(self)\n            item.setVisible(True)\n\n        fmt = self.TitleFormat.format\n\n        font = self.font()\n        font.setPixelSize(14)\n\n        for item in items:\n            text = GraphicsTextEdit(self)\n            text.setFont(font)\n            text.setDefaultTextColor(QColor(\"#333\"))\n            text.setHtml(fmt(escape(item.text), item.informativeText))\n            text.adjustSize()\n            text.editingStarted.connect(self._on_editingStarted)\n            text.editingFinished.connect(self._on_editingFinished)\n            text.documentSizeChanged.connect(\n                self._on_itemTextSizeChanged\n            )\n\n            self._textitems.append(text)\n\n        self._vennareas = [\n            VennIntersectionArea(parent=self)\n            for i in range(2 ** len(items))\n        ]\n        self._subsettextitems = [\n            QGraphicsTextItem(parent=self)\n            for i in range(2 ** len(items))\n        ]\n\n        self._updateLayout()\n\n    def clear(self):\n        scene = self.scene()\n        items = self.vennareas() + list(self.items()) + self._textitems\n\n        for item in self._textitems:\n            item.editingStarted.disconnect(self._on_editingStarted)\n            item.editingFinished.disconnect(self._on_editingFinished)\n            item.documentSizeChanged.disconnect(\n                self._on_itemTextSizeChanged\n            )\n\n        self._items = []\n        self._vennareas = []\n        self._textitems = []\n        self._subsettextitems = []\n        self._textanchors = []\n\n        for item in items:\n            item.setVisible(False)\n            item.setParentItem(None)\n            if scene is not None:\n                scene.removeItem(item)\n\n    def vennareas(self):\n        return list(self._vennareas)\n\n    def setFont(self, font):\n        if font != self.font():\n            self.prepareGeometryChange()\n            super().setFont(font)\n\n            for item in self.items():\n                item.setFont(font)\n\n    def _updateLayout(self):\n        rect = self.geometry()\n        n = len(self._items)\n        if not n:\n            return\n\n        regions = venn_diagram(n)\n\n        # The y axis in Qt points downward\n        transform = QTransform().scale(1, -1)\n        regions = list(map(transform.map, regions))\n\n        union_brect = reduce(QRectF.united,\n                             (path.boundingRect() for path in regions))\n\n        scalex = rect.width() / union_brect.width()\n        scaley = rect.height() / union_brect.height()\n        scale = min(scalex, scaley)\n\n        transform = QTransform().scale(scale, scale)\n\n        regions = [transform.map(path) for path in regions]\n\n        center = rect.width() / 2, rect.height() / 2\n        for item, path in zip(self.items(), regions):\n            item.setPath(path)\n            item.setPos(*center)\n\n        intersections = venn_intersections(regions)\n        assert len(intersections) == 2 ** n\n        assert len(self.vennareas()) == 2 ** n\n\n        anchors = [(0, 0)] + subset_anchors(self._items)\n\n        anchor_transform = QTransform().scale(rect.width(), -rect.height())\n        for i, area in enumerate(self.vennareas()):\n            area.setPath(intersections[setkey(i, n)])\n            area.setPos(*center)\n            x, y = anchors[i]\n            anchor = anchor_transform.map(QPointF(x, y))\n            area.setTextAnchor(anchor)\n            area.setZValue(30)\n\n        self._updateTextAnchors()\n\n    def _updateTextAnchors(self):\n        n = len(self._items)\n\n        items = self._items\n        dist = 15\n\n        shape = reduce(QPainterPath.united, [item.path() for item in items])\n        brect = shape.boundingRect()\n        bradius = max(brect.width() / 2, brect.height() / 2)\n\n        center = self.boundingRect().center()\n\n        anchors = _category_anchors(items)\n        self._textanchors = []\n        for angle, anchor_h, anchor_v in anchors:\n            line = QLineF.fromPolar(bradius, angle)\n            ext = QLineF.fromPolar(dist, angle)\n            line = QLineF(line.p1(), line.p2() + ext.p2())\n            line = line.translated(center)\n\n            anchor_pos = line.p2()\n            self._textanchors.append((anchor_pos, anchor_h, anchor_v))\n\n        for i in range(n):\n            self._updateTextItemPos(i)\n\n    def _updateTextItemPos(self, i):\n        item = self._textitems[i]\n        anchor_pos, anchor_h, anchor_v = self._textanchors[i]\n        rect = item.boundingRect()\n        pos = anchor_rect(rect, anchor_pos, anchor_h, anchor_v)\n        item.setPos(pos)\n\n    def setGeometry(self, geometry):\n        super(VennDiagram, self).setGeometry(geometry)\n        self._updateLayout()\n\n    def _on_editingStarted(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        text = self._items[index].text\n        item.setTextWidth(-1)\n        item.setHtml(self.TitleFormat.format(escape(text), \"<br/>\"))\n\n    def _on_editingFinished(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        text = item.toPlainText()\n        if text != self._items[index].text:\n            self._items[index].text = text\n\n            self.itemTextEdited.emit(index, text)\n\n        item.setHtml(\n            self.TitleFormat.format(\n                escape(text), self._items[index].informativeText))\n        item.adjustSize()\n\n    def _on_itemTextSizeChanged(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        self._updateTextItemPos(index)\n\n\ndef anchor_rect(rect, anchor_pos,\n                anchor_h=Qt.AnchorHorizontalCenter,\n                anchor_v=Qt.AnchorVerticalCenter):\n\n    if anchor_h == Qt.AnchorLeft:\n        x = anchor_pos.x()\n    elif anchor_h == Qt.AnchorHorizontalCenter:\n        x = anchor_pos.x() - rect.width() / 2\n    elif anchor_h == Qt.AnchorRight:\n        x = anchor_pos.x() - rect.width()\n    else:\n        raise ValueError(anchor_h)\n\n    if anchor_v == Qt.AnchorTop:\n        y = anchor_pos.y()\n    elif anchor_v == Qt.AnchorVerticalCenter:\n        y = anchor_pos.y() - rect.height() / 2\n    elif anchor_v == Qt.AnchorBottom:\n        y = anchor_pos.y() - rect.height()\n    else:\n        raise ValueError(anchor_v)\n\n    return QPointF(x, y)\n\n\ndef radians(angle):\n    return 2 * math.pi * angle / 360\n\n\ndef unit_point(x, r=1.0):\n    x = radians(x)\n    return (r * math.cos(x), r * math.sin(x))\n\n\ndef _category_anchors(shapes):\n    n = len(shapes)\n    return _CATEGORY_ANCHORS[n - 1]\n\n\n# (angle, horizontal anchor, vertical anchor)\n_CATEGORY_ANCHORS = (\n    # n == 1\n    ((90, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),),\n    # n == 2\n    ((180, Qt.AnchorRight, Qt.AnchorVerticalCenter),\n     (0, Qt.AnchorLeft, Qt.AnchorVerticalCenter)),\n    # n == 3\n    ((150, Qt.AnchorRight, Qt.AnchorBottom),\n     (30, Qt.AnchorLeft, Qt.AnchorBottom),\n     (270, Qt.AnchorHorizontalCenter, Qt.AnchorTop)),\n    # n == 4\n    ((270 + 45, Qt.AnchorLeft, Qt.AnchorTop),\n     (270 - 45, Qt.AnchorRight, Qt.AnchorTop),\n     (90 - 15, Qt.AnchorLeft, Qt.AnchorBottom),\n     (90 + 15, Qt.AnchorRight, Qt.AnchorBottom)),\n    # n == 5\n    ((90 - 5, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),\n     (18 - 5, Qt.AnchorLeft, Qt.AnchorVerticalCenter),\n     (306 - 5, Qt.AnchorLeft, Qt.AnchorTop),\n     (234 - 5, Qt.AnchorRight, Qt.AnchorTop),\n     (162 - 5, Qt.AnchorRight, Qt.AnchorVerticalCenter),)\n)\n\n\ndef subset_anchors(shapes):\n    n = len(shapes)\n    if n == 1:\n        return [(0, 0)]\n    elif n == 2:\n        return [unit_point(180, r=1/3),\n                unit_point(0, r=1/3),\n                (0, 0)]\n    elif n == 3:\n        return [unit_point(150, r=0.35),  # A\n                unit_point(30, r=0.35),   # B\n                unit_point(90, r=0.27),   # AB\n                unit_point(270, r=0.35),  # C\n                unit_point(210, r=0.27),  # AC\n                unit_point(330, r=0.27),  # BC\n                unit_point(0, r=0),]      # ABC\n    elif n == 4:\n        anchors = [\n            (0.400, 0.110),    # A\n            (-0.400, 0.110),   # B\n            (0.000, -0.285),   # AB\n            (0.180, 0.330),    # C\n            (0.265, 0.205),    # AC\n            (-0.240, -0.110),  # BC\n            (-0.100, -0.190),  # ABC\n            (-0.180, 0.330),   # D\n            (0.240, -0.110),   # AD\n            (-0.265, 0.205),   # BD\n            (0.100, -0.190),   # ABD\n            (0.000, 0.250),    # CD\n            (0.153, 0.090),    # ACD\n            (-0.153, 0.090),   # BCD\n            (0.000, -0.060),   # ABCD\n        ]\n        return anchors\n\n    elif n == 5:\n        anchors = [None] * 32\n        # Base anchors\n        A = (0.033, 0.385)\n        AD = (0.095, 0.250)\n        AE = (-0.100, 0.265)\n        ACE = (-0.130, 0.220)\n        ADE = (0.010, 0.225)\n        ACDE = (-0.095, 0.175)\n        ABCDE = (0.0, 0.0)\n\n        anchors[-1] = ABCDE\n\n        bases = [(0b00001, A),\n                 (0b01001, AD),\n                 (0b10001, AE),\n                 (0b10101, ACE),\n                 (0b11001, ADE),\n                 (0b11101, ACDE)]\n\n        for i in range(5):\n            for index, anchor in bases:\n                index = bit_rot_left(index, i, bits=5)\n                assert anchors[index] is None\n                anchors[index] = rotate_point(anchor, - 72 * i)\n\n        assert all(anchors[1:])\n        return anchors[1:]\n    return None\n\n\ndef bit_rot_left(x, y, bits=32):\n    mask = 2 ** bits - 1\n    x_masked = x & mask\n    return (x << y) & mask | (x_masked >> bits - y)\n\n\ndef rotate_point(p, angle):\n    r = radians(angle)\n    R = np.array([[math.cos(r), -math.sin(r)],\n                  [math.sin(r), math.cos(r)]])\n    x, y = np.dot(R, p)\n    return (float(x), float(y))\n\n\ndef line_extended(line, distance):\n    \"\"\"\n    Return an QLineF extended by `distance` units in the positive direction.\n    \"\"\"\n    angle = line.angle() / 360 * 2 * math.pi\n    dx, dy = unit_point(angle, r=distance)\n    return QLineF(line.p1(), line.p2() + QPointF(dx, dy))\n\n\ndef circle_path(center, r=1.0):\n    return ellipse_path(center, r, r, rotation=0)\n\n\ndef ellipse_path(center, a, b, rotation=0):\n    if not isinstance(center, QPointF):\n        center = QPointF(*center)\n\n    brect = QRectF(-a, -b, 2 * a, 2 * b)\n\n    path = QPainterPath()\n    path.addEllipse(brect)\n\n    if rotation != 0:\n        transform = QTransform().rotate(rotation)\n        path = transform.map(path)\n\n    path.translate(center)\n    return path\n\n\n# TODO: Should include anchors for text layout (both inside and outside).\n# for each item {path: QPainterPath,\n#                text_anchors: [{center}] * (2 ** n)\n#                mayor_axis: QLineF,\n#                boundingRect QPolygonF (with 4 vertices)}\n#\n# Should be a new class with overloads for ellipse/circle, rect, and petal\n# shapes, should store all constructor parameters, rotation, center,\n# mayor/minor axis.\n\n\ndef venn_diagram(n):\n    if n < 1 or n > 5:\n        raise ValueError()\n\n    paths = []\n\n    if n == 1:\n        paths = [circle_path(center=(0, 0), r=0.5)]\n    elif n == 2:\n        angles = [180, 0]\n        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)\n                 for x in angles]\n    elif n == 3:\n        angles = [150 - 120 * i for i in range(3)]\n        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)\n                 for x in angles]\n    elif n == 4:\n        # Constants shamelessly stolen from VennDiagram R package\n        paths = [\n            ellipse_path((0.65 - 0.5, 0.47 - 0.5), 0.35, 0.20, 45),\n            ellipse_path((0.35 - 0.5, 0.47 - 0.5), 0.35, 0.20, 135),\n            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 45),\n            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 134),\n        ]\n    elif n == 5:\n        # Constants shamelessly stolen from VennDiagram R package\n        d = 0.13\n        a, b = 0.24, 0.48\n        a, b = b, a\n        a, b = 0.48, 0.24\n        paths = [ellipse_path(unit_point((1 - i) * 72, r=d),\n                              a, b, rotation=90 - (i * 72))\n                 for i in range(5)]\n\n    return paths\n\n\ndef setkey(intval, n):\n    return tuple(bool(intval & (2 ** i)) for i in range(n))\n\n\ndef keyrange(n):\n    if n < 0:\n        raise ValueError()\n\n    for i in range(2 ** n):\n        yield setkey(i, n)\n\n\ndef venn_intersections(paths):\n    n = len(paths)\n    return {key: venn_intersection(paths, key) for key in keyrange(n)}\n\n\ndef venn_intersection(paths, key):\n    if not any(key):\n        return QPainterPath()\n\n    # first take the intersection of all included paths\n    path = reduce(QPainterPath.intersected,\n                  (path for path, included in zip(paths, key) if included))\n\n    # subtract all the excluded sets (i.e. take the intersection\n    # with the excluded set complements)\n    path = reduce(QPainterPath.subtracted,\n                  (path for path, included in zip(paths, key) if not included),\n                  path)\n\n    return path\n\n\ndef append_column(data, where, variable, column):\n    X, Y, M = data.X, data.Y, data.metas\n    domain = data.domain\n    attr = domain.attributes\n    class_vars = domain.class_vars\n    metas = domain.metas\n    if where == \"X\":\n        attr = attr + (variable,)\n        X = np.hstack((X, column))\n    elif where == \"Y\":\n        class_vars = class_vars + (variable,)\n        Y = np.hstack((Y, column))\n    elif where == \"M\":\n        metas = metas + (variable,)\n        M = np.hstack((M, column))\n    else:\n        raise ValueError\n    domain = Domain(attr, class_vars, metas)\n    new_data = data.transform(domain)\n    new_data[:, variable] = column\n    return new_data\n\ndef arrays_equal(a, b, type_):\n    \"\"\"\n    checks if arrays have nans in same places and if not-nan elements\n    are equal\n    \"\"\"\n    if a is None and b is None:\n        return True\n    if a is None or b is None:\n        return False\n    if type_ is not StringVariable:\n        nana = np.isnan(a)\n        nanb = np.isnan(b)\n        return np.all(nana == nanb) and np.all(a[~nana] == b[~nanb])\n    else:\n        return np.all(a == b)\n\ndef pad_columns(values, mask, l):\n    #inflates columns with nans\n    a = np.full((l, 1), np.nan, dtype=values.dtype)\n    a[mask] = values.reshape(-1, 1)\n    return a\n\ndef get_perm(ids, all_ids):\n    return [all_ids.index(el) for el in ids if el in all_ids]\n\n\ndef main():  # pragma: no cover\n    # pylint: disable=import-outside-toplevel\n    from Orange.evaluation import ShuffleSplit\n\n    data = Table(\"brown-selected\")\n\n    if not \"test_rows\":  # change to `if not \"test_rows\" to test columns\n        data = append_column(data, \"M\", StringVariable(\"Test\"),\n                             (np.arange(len(data)).reshape(-1, 1) % 30).astype(str))\n        res = ShuffleSplit(n_resamples=5, test_size=0.7, stratified=False)\n        indices = iter(res.get_indices(data))\n        datasets = []\n        for i in range(1, 6):\n            sample, _ = next(indices)\n            data1 = data[sample]\n            data1.name = chr(ord(\"A\") + i)\n            datasets.append((data1, i))\n    else:\n        domain = data.domain\n        data1 = data.transform(Domain(domain.attributes[:15], domain.class_var))\n        data2 = data.transform(Domain(domain.attributes[10:], domain.class_var))\n        datasets = [(data1, 1), (data2, 2)]\n\n    WidgetPreview(OWVennDiagram).run(setData=datasets)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    main()\n",
            "new_file_content": "\"\"\"\nVenn Diagram Widget\n-------------------\n\n\"\"\"\n\nimport math\nimport unicodedata\nfrom collections import namedtuple, defaultdict\nfrom itertools import compress, count\nfrom functools import reduce\nfrom operator import attrgetter\nfrom xml.sax.saxutils import escape\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import (\n    QGraphicsScene, QGraphicsView, QGraphicsWidget,\n    QGraphicsPathItem, QGraphicsTextItem, QStyle, QSizePolicy\n)\nfrom AnyQt.QtGui import (\n    QPainterPath, QPainter, QTransform, QColor, QBrush, QPen, QPalette\n)\nfrom AnyQt.QtCore import Qt, QPointF, QRectF, QLineF\nfrom AnyQt.QtCore import pyqtSignal as Signal\n\nfrom Orange.data import Table, Domain, StringVariable, RowInstance\nfrom Orange.data.util import get_unique_names_duplicates\nfrom Orange.widgets import widget, gui, settings\nfrom Orange.widgets.utils import itemmodels, colorpalettes\nfrom Orange.widgets.utils.annotated_data import (create_annotated_table,\n                                                 ANNOTATED_DATA_SIGNAL_NAME)\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output, Msg\n\n\n_InputData = namedtuple(\"_InputData\", [\"key\", \"name\", \"table\"])\n_ItemSet = namedtuple(\"_ItemSet\", [\"key\", \"name\", \"title\", \"items\"])\n\n\nclass OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n            n = len(metas)\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n            n = len(class_vars)\n        if X is None:\n            X = np.empty((n, 0))\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        domain = {type_ : [] for type_ in self.atr_types}\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = {type_ : [] for type_ in self.atr_types}\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        ids = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, ids)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables\n\ndef string_attributes(domain):\n    \"\"\"\n    Return all string attributes from the domain.\n    \"\"\"\n    return [attr for attr in domain.variables + domain.metas if attr.is_string]\n\ndef source_attributes(domain):\n    \"\"\"\n    Return all suitable attributes for the venn diagram.\n    \"\"\"\n    return string_attributes(domain)  # + discrete_attributes(domain)\n\n\ndef disjoint_set_label(i, n, simplify=False):\n    \"\"\"\n    Return a html formated label for a disjoint set indexed by `i`.\n    \"\"\"\n    intersection = unicodedata.lookup(\"INTERSECTION\")\n    # comp = unicodedata.lookup(\"COMPLEMENT\")  #\n    # This depends on the font but the unicode complement in\n    # general does not look nice in a super script so we use\n    # plain c instead.\n    comp = \"c\"\n\n    def label_for_index(i):\n        return chr(ord(\"A\") + i)\n\n    if simplify:\n        return \"\".join(label_for_index(i) for i, b in enumerate(setkey(i, n))\n                       if b)\n    else:\n        return intersection.join(label_for_index(i) +\n                                 (\"\" if b else \"<sup>\" + comp + \"</sup>\")\n                                 for i, b in enumerate(setkey(i, n)))\n\n\nclass VennSetItem(QGraphicsPathItem):\n    def __init__(self, parent=None, text=\"\", informativeText=\"\"):\n        super(VennSetItem, self).__init__(parent)\n        # Plain text title (editable by the VennDiagram)\n        self.text = text\n        # Extra informative text (possibly rich text)\n        self.informativeText = informativeText\n\n# TODO: Use palette's selected/highligted text / background colors to\n# indicate selection\n\n\nclass VennIntersectionArea(QGraphicsPathItem):\n    def __init__(self, parent=None, text=\"\"):\n        super().__init__(parent)\n        self.setAcceptHoverEvents(True)\n        self.setPen(QPen(Qt.NoPen))\n\n        self.text = QGraphicsTextItem(self)\n        layout = self.text.document().documentLayout()\n        layout.documentSizeChanged.connect(self._onLayoutChanged)\n\n        self._text = text\n        self._anchor = QPointF()\n\n    def setText(self, text):\n        if self._text != text:\n            self._text = text\n            self.text.setPlainText(text)\n\n    def setTextAnchor(self, pos):\n        if self._anchor != pos:\n            self._anchor = pos\n            self._updateTextAnchor()\n\n    def hoverEnterEvent(self, event):\n        self.setZValue(self.zValue() + 1)\n        return QGraphicsPathItem.hoverEnterEvent(self, event)\n\n    def hoverLeaveEvent(self, event):\n        self.setZValue(self.zValue() - 1)\n        return QGraphicsPathItem.hoverLeaveEvent(self, event)\n\n    def mousePressEvent(self, event):\n        if event.button() == Qt.LeftButton:\n            if event.modifiers() & Qt.AltModifier:\n                self.setSelected(False)\n            elif event.modifiers() & Qt.ControlModifier:\n                self.setSelected(not self.isSelected())\n            elif event.modifiers() & Qt.ShiftModifier:\n                self.setSelected(True)\n            else:\n                for area in self.parentWidget().vennareas():\n                    area.setSelected(False)\n                self.setSelected(True)\n\n    def mouseReleaseEvent(self, event):\n        pass\n\n    def paint(self, painter, option, _widget=None):\n        painter.save()\n        path = self.path()\n        brush = QBrush(self.brush())\n        pen = QPen(self.pen())\n\n        if option.state & QStyle.State_Selected:\n            pen.setColor(Qt.red)\n            brush.setStyle(Qt.DiagCrossPattern)\n            brush.setColor(QColor(40, 40, 40, 100))\n\n        elif option.state & QStyle.State_MouseOver:\n            pen.setColor(Qt.blue)\n\n        if option.state & QStyle.State_MouseOver:\n            brush.setColor(QColor(100, 100, 100, 100))\n            if brush.style() == Qt.NoBrush:\n                # Make sure the highlight is actually visible.\n                brush.setStyle(Qt.SolidPattern)\n\n        painter.setPen(pen)\n        painter.setBrush(brush)\n        painter.drawPath(path)\n        painter.restore()\n\n    def itemChange(self, change, value):\n        if change == QGraphicsPathItem.ItemSelectedHasChanged:\n            self.setZValue(self.zValue() + (1 if value else -1))\n        return QGraphicsPathItem.itemChange(self, change, value)\n\n    def _updateTextAnchor(self):\n        rect = self.text.boundingRect()\n        pos = anchor_rect(rect, self._anchor)\n        self.text.setPos(pos)\n\n    def _onLayoutChanged(self):\n        self._updateTextAnchor()\n\n\nclass GraphicsTextEdit(QGraphicsTextItem):\n    #: Edit triggers\n    NoEditTriggers, DoubleClicked = 0, 1\n\n    editingFinished = Signal()\n    editingStarted = Signal()\n\n    documentSizeChanged = Signal()\n\n    def __init__(self, *args, **kwargs):\n        super(GraphicsTextEdit, self).__init__(*args, **kwargs)\n        self.setCursor(Qt.IBeamCursor)\n        self.setTabChangesFocus(True)\n        self._edittrigger = GraphicsTextEdit.DoubleClicked\n        self._editing = False\n        self.document().documentLayout().documentSizeChanged.connect(\n            self.documentSizeChanged\n        )\n\n    def mouseDoubleClickEvent(self, event):\n        super(GraphicsTextEdit, self).mouseDoubleClickEvent(event)\n        if self._edittrigger == GraphicsTextEdit.DoubleClicked:\n            self._start()\n\n    def focusOutEvent(self, event):\n        super(GraphicsTextEdit, self).focusOutEvent(event)\n\n        if self._editing:\n            self._end()\n\n    def _start(self):\n        self._editing = True\n        self.setTextInteractionFlags(Qt.TextEditorInteraction)\n        self.setFocus(Qt.MouseFocusReason)\n        self.editingStarted.emit()\n\n    def _end(self):\n        self._editing = False\n        self.setTextInteractionFlags(Qt.NoTextInteraction)\n        self.editingFinished.emit()\n\n\nclass VennDiagram(QGraphicsWidget):\n    # rect and petal are for future work\n    Circle, Ellipse, Rect, Petal = 1, 2, 3, 4\n\n    TitleFormat = \"<center><h4>{0}</h4>{1}</center>\"\n\n    selectionChanged = Signal()\n    itemTextEdited = Signal(int, str)\n\n    def __init__(self, parent=None):\n        super(VennDiagram, self).__init__(parent)\n        self.shapeType = VennDiagram.Circle\n        self._items = []\n        self._vennareas = []\n        self._textitems = []\n        self._subsettextitems = []\n        self._textanchors = []\n\n    def item(self, index):\n        return self._items[index]\n\n    def items(self):\n        return list(self._items)\n\n    def count(self):\n        return len(self._items)\n\n    def setItems(self, items):\n        if self._items:\n            self.clear()\n\n        self._items = list(items)\n\n        for item in self._items:\n            item.setParentItem(self)\n            item.setVisible(True)\n\n        fmt = self.TitleFormat.format\n\n        font = self.font()\n        font.setPixelSize(14)\n\n        for item in items:\n            text = GraphicsTextEdit(self)\n            text.setFont(font)\n            text.setDefaultTextColor(QColor(\"#333\"))\n            text.setHtml(fmt(escape(item.text), item.informativeText))\n            text.adjustSize()\n            text.editingStarted.connect(self._on_editingStarted)\n            text.editingFinished.connect(self._on_editingFinished)\n            text.documentSizeChanged.connect(\n                self._on_itemTextSizeChanged\n            )\n\n            self._textitems.append(text)\n\n        self._vennareas = [\n            VennIntersectionArea(parent=self)\n            for i in range(2 ** len(items))\n        ]\n        self._subsettextitems = [\n            QGraphicsTextItem(parent=self)\n            for i in range(2 ** len(items))\n        ]\n\n        self._updateLayout()\n\n    def clear(self):\n        scene = self.scene()\n        items = self.vennareas() + list(self.items()) + self._textitems\n\n        for item in self._textitems:\n            item.editingStarted.disconnect(self._on_editingStarted)\n            item.editingFinished.disconnect(self._on_editingFinished)\n            item.documentSizeChanged.disconnect(\n                self._on_itemTextSizeChanged\n            )\n\n        self._items = []\n        self._vennareas = []\n        self._textitems = []\n        self._subsettextitems = []\n        self._textanchors = []\n\n        for item in items:\n            item.setVisible(False)\n            item.setParentItem(None)\n            if scene is not None:\n                scene.removeItem(item)\n\n    def vennareas(self):\n        return list(self._vennareas)\n\n    def setFont(self, font):\n        if font != self.font():\n            self.prepareGeometryChange()\n            super().setFont(font)\n\n            for item in self.items():\n                item.setFont(font)\n\n    def _updateLayout(self):\n        rect = self.geometry()\n        n = len(self._items)\n        if not n:\n            return\n\n        regions = venn_diagram(n)\n\n        # The y axis in Qt points downward\n        transform = QTransform().scale(1, -1)\n        regions = list(map(transform.map, regions))\n\n        union_brect = reduce(QRectF.united,\n                             (path.boundingRect() for path in regions))\n\n        scalex = rect.width() / union_brect.width()\n        scaley = rect.height() / union_brect.height()\n        scale = min(scalex, scaley)\n\n        transform = QTransform().scale(scale, scale)\n\n        regions = [transform.map(path) for path in regions]\n\n        center = rect.width() / 2, rect.height() / 2\n        for item, path in zip(self.items(), regions):\n            item.setPath(path)\n            item.setPos(*center)\n\n        intersections = venn_intersections(regions)\n        assert len(intersections) == 2 ** n\n        assert len(self.vennareas()) == 2 ** n\n\n        anchors = [(0, 0)] + subset_anchors(self._items)\n\n        anchor_transform = QTransform().scale(rect.width(), -rect.height())\n        for i, area in enumerate(self.vennareas()):\n            area.setPath(intersections[setkey(i, n)])\n            area.setPos(*center)\n            x, y = anchors[i]\n            anchor = anchor_transform.map(QPointF(x, y))\n            area.setTextAnchor(anchor)\n            area.setZValue(30)\n\n        self._updateTextAnchors()\n\n    def _updateTextAnchors(self):\n        n = len(self._items)\n\n        items = self._items\n        dist = 15\n\n        shape = reduce(QPainterPath.united, [item.path() for item in items])\n        brect = shape.boundingRect()\n        bradius = max(brect.width() / 2, brect.height() / 2)\n\n        center = self.boundingRect().center()\n\n        anchors = _category_anchors(items)\n        self._textanchors = []\n        for angle, anchor_h, anchor_v in anchors:\n            line = QLineF.fromPolar(bradius, angle)\n            ext = QLineF.fromPolar(dist, angle)\n            line = QLineF(line.p1(), line.p2() + ext.p2())\n            line = line.translated(center)\n\n            anchor_pos = line.p2()\n            self._textanchors.append((anchor_pos, anchor_h, anchor_v))\n\n        for i in range(n):\n            self._updateTextItemPos(i)\n\n    def _updateTextItemPos(self, i):\n        item = self._textitems[i]\n        anchor_pos, anchor_h, anchor_v = self._textanchors[i]\n        rect = item.boundingRect()\n        pos = anchor_rect(rect, anchor_pos, anchor_h, anchor_v)\n        item.setPos(pos)\n\n    def setGeometry(self, geometry):\n        super(VennDiagram, self).setGeometry(geometry)\n        self._updateLayout()\n\n    def _on_editingStarted(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        text = self._items[index].text\n        item.setTextWidth(-1)\n        item.setHtml(self.TitleFormat.format(escape(text), \"<br/>\"))\n\n    def _on_editingFinished(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        text = item.toPlainText()\n        if text != self._items[index].text:\n            self._items[index].text = text\n\n            self.itemTextEdited.emit(index, text)\n\n        item.setHtml(\n            self.TitleFormat.format(\n                escape(text), self._items[index].informativeText))\n        item.adjustSize()\n\n    def _on_itemTextSizeChanged(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        self._updateTextItemPos(index)\n\n\ndef anchor_rect(rect, anchor_pos,\n                anchor_h=Qt.AnchorHorizontalCenter,\n                anchor_v=Qt.AnchorVerticalCenter):\n\n    if anchor_h == Qt.AnchorLeft:\n        x = anchor_pos.x()\n    elif anchor_h == Qt.AnchorHorizontalCenter:\n        x = anchor_pos.x() - rect.width() / 2\n    elif anchor_h == Qt.AnchorRight:\n        x = anchor_pos.x() - rect.width()\n    else:\n        raise ValueError(anchor_h)\n\n    if anchor_v == Qt.AnchorTop:\n        y = anchor_pos.y()\n    elif anchor_v == Qt.AnchorVerticalCenter:\n        y = anchor_pos.y() - rect.height() / 2\n    elif anchor_v == Qt.AnchorBottom:\n        y = anchor_pos.y() - rect.height()\n    else:\n        raise ValueError(anchor_v)\n\n    return QPointF(x, y)\n\n\ndef radians(angle):\n    return 2 * math.pi * angle / 360\n\n\ndef unit_point(x, r=1.0):\n    x = radians(x)\n    return (r * math.cos(x), r * math.sin(x))\n\n\ndef _category_anchors(shapes):\n    n = len(shapes)\n    return _CATEGORY_ANCHORS[n - 1]\n\n\n# (angle, horizontal anchor, vertical anchor)\n_CATEGORY_ANCHORS = (\n    # n == 1\n    ((90, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),),\n    # n == 2\n    ((180, Qt.AnchorRight, Qt.AnchorVerticalCenter),\n     (0, Qt.AnchorLeft, Qt.AnchorVerticalCenter)),\n    # n == 3\n    ((150, Qt.AnchorRight, Qt.AnchorBottom),\n     (30, Qt.AnchorLeft, Qt.AnchorBottom),\n     (270, Qt.AnchorHorizontalCenter, Qt.AnchorTop)),\n    # n == 4\n    ((270 + 45, Qt.AnchorLeft, Qt.AnchorTop),\n     (270 - 45, Qt.AnchorRight, Qt.AnchorTop),\n     (90 - 15, Qt.AnchorLeft, Qt.AnchorBottom),\n     (90 + 15, Qt.AnchorRight, Qt.AnchorBottom)),\n    # n == 5\n    ((90 - 5, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),\n     (18 - 5, Qt.AnchorLeft, Qt.AnchorVerticalCenter),\n     (306 - 5, Qt.AnchorLeft, Qt.AnchorTop),\n     (234 - 5, Qt.AnchorRight, Qt.AnchorTop),\n     (162 - 5, Qt.AnchorRight, Qt.AnchorVerticalCenter),)\n)\n\n\ndef subset_anchors(shapes):\n    n = len(shapes)\n    if n == 1:\n        return [(0, 0)]\n    elif n == 2:\n        return [unit_point(180, r=1/3),\n                unit_point(0, r=1/3),\n                (0, 0)]\n    elif n == 3:\n        return [unit_point(150, r=0.35),  # A\n                unit_point(30, r=0.35),   # B\n                unit_point(90, r=0.27),   # AB\n                unit_point(270, r=0.35),  # C\n                unit_point(210, r=0.27),  # AC\n                unit_point(330, r=0.27),  # BC\n                unit_point(0, r=0),]      # ABC\n    elif n == 4:\n        anchors = [\n            (0.400, 0.110),    # A\n            (-0.400, 0.110),   # B\n            (0.000, -0.285),   # AB\n            (0.180, 0.330),    # C\n            (0.265, 0.205),    # AC\n            (-0.240, -0.110),  # BC\n            (-0.100, -0.190),  # ABC\n            (-0.180, 0.330),   # D\n            (0.240, -0.110),   # AD\n            (-0.265, 0.205),   # BD\n            (0.100, -0.190),   # ABD\n            (0.000, 0.250),    # CD\n            (0.153, 0.090),    # ACD\n            (-0.153, 0.090),   # BCD\n            (0.000, -0.060),   # ABCD\n        ]\n        return anchors\n\n    elif n == 5:\n        anchors = [None] * 32\n        # Base anchors\n        A = (0.033, 0.385)\n        AD = (0.095, 0.250)\n        AE = (-0.100, 0.265)\n        ACE = (-0.130, 0.220)\n        ADE = (0.010, 0.225)\n        ACDE = (-0.095, 0.175)\n        ABCDE = (0.0, 0.0)\n\n        anchors[-1] = ABCDE\n\n        bases = [(0b00001, A),\n                 (0b01001, AD),\n                 (0b10001, AE),\n                 (0b10101, ACE),\n                 (0b11001, ADE),\n                 (0b11101, ACDE)]\n\n        for i in range(5):\n            for index, anchor in bases:\n                index = bit_rot_left(index, i, bits=5)\n                assert anchors[index] is None\n                anchors[index] = rotate_point(anchor, - 72 * i)\n\n        assert all(anchors[1:])\n        return anchors[1:]\n    return None\n\n\ndef bit_rot_left(x, y, bits=32):\n    mask = 2 ** bits - 1\n    x_masked = x & mask\n    return (x << y) & mask | (x_masked >> bits - y)\n\n\ndef rotate_point(p, angle):\n    r = radians(angle)\n    R = np.array([[math.cos(r), -math.sin(r)],\n                  [math.sin(r), math.cos(r)]])\n    x, y = np.dot(R, p)\n    return (float(x), float(y))\n\n\ndef line_extended(line, distance):\n    \"\"\"\n    Return an QLineF extended by `distance` units in the positive direction.\n    \"\"\"\n    angle = line.angle() / 360 * 2 * math.pi\n    dx, dy = unit_point(angle, r=distance)\n    return QLineF(line.p1(), line.p2() + QPointF(dx, dy))\n\n\ndef circle_path(center, r=1.0):\n    return ellipse_path(center, r, r, rotation=0)\n\n\ndef ellipse_path(center, a, b, rotation=0):\n    if not isinstance(center, QPointF):\n        center = QPointF(*center)\n\n    brect = QRectF(-a, -b, 2 * a, 2 * b)\n\n    path = QPainterPath()\n    path.addEllipse(brect)\n\n    if rotation != 0:\n        transform = QTransform().rotate(rotation)\n        path = transform.map(path)\n\n    path.translate(center)\n    return path\n\n\n# TODO: Should include anchors for text layout (both inside and outside).\n# for each item {path: QPainterPath,\n#                text_anchors: [{center}] * (2 ** n)\n#                mayor_axis: QLineF,\n#                boundingRect QPolygonF (with 4 vertices)}\n#\n# Should be a new class with overloads for ellipse/circle, rect, and petal\n# shapes, should store all constructor parameters, rotation, center,\n# mayor/minor axis.\n\n\ndef venn_diagram(n):\n    if n < 1 or n > 5:\n        raise ValueError()\n\n    paths = []\n\n    if n == 1:\n        paths = [circle_path(center=(0, 0), r=0.5)]\n    elif n == 2:\n        angles = [180, 0]\n        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)\n                 for x in angles]\n    elif n == 3:\n        angles = [150 - 120 * i for i in range(3)]\n        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)\n                 for x in angles]\n    elif n == 4:\n        # Constants shamelessly stolen from VennDiagram R package\n        paths = [\n            ellipse_path((0.65 - 0.5, 0.47 - 0.5), 0.35, 0.20, 45),\n            ellipse_path((0.35 - 0.5, 0.47 - 0.5), 0.35, 0.20, 135),\n            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 45),\n            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 134),\n        ]\n    elif n == 5:\n        # Constants shamelessly stolen from VennDiagram R package\n        d = 0.13\n        a, b = 0.24, 0.48\n        a, b = b, a\n        a, b = 0.48, 0.24\n        paths = [ellipse_path(unit_point((1 - i) * 72, r=d),\n                              a, b, rotation=90 - (i * 72))\n                 for i in range(5)]\n\n    return paths\n\n\ndef setkey(intval, n):\n    return tuple(bool(intval & (2 ** i)) for i in range(n))\n\n\ndef keyrange(n):\n    if n < 0:\n        raise ValueError()\n\n    for i in range(2 ** n):\n        yield setkey(i, n)\n\n\ndef venn_intersections(paths):\n    n = len(paths)\n    return {key: venn_intersection(paths, key) for key in keyrange(n)}\n\n\ndef venn_intersection(paths, key):\n    if not any(key):\n        return QPainterPath()\n\n    # first take the intersection of all included paths\n    path = reduce(QPainterPath.intersected,\n                  (path for path, included in zip(paths, key) if included))\n\n    # subtract all the excluded sets (i.e. take the intersection\n    # with the excluded set complements)\n    path = reduce(QPainterPath.subtracted,\n                  (path for path, included in zip(paths, key) if not included),\n                  path)\n\n    return path\n\n\ndef append_column(data, where, variable, column):\n    X, Y, M = data.X, data.Y, data.metas\n    domain = data.domain\n    attr = domain.attributes\n    class_vars = domain.class_vars\n    metas = domain.metas\n    if where == \"X\":\n        attr = attr + (variable,)\n        X = np.hstack((X, column))\n    elif where == \"Y\":\n        class_vars = class_vars + (variable,)\n        Y = np.hstack((Y, column))\n    elif where == \"M\":\n        metas = metas + (variable,)\n        M = np.hstack((M, column))\n    else:\n        raise ValueError\n    domain = Domain(attr, class_vars, metas)\n    new_data = data.transform(domain)\n    new_data[:, variable] = column\n    return new_data\n\ndef arrays_equal(a, b, type_):\n    \"\"\"\n    checks if arrays have nans in same places and if not-nan elements\n    are equal\n    \"\"\"\n    if a is None and b is None:\n        return True\n    if a is None or b is None:\n        return False\n    if type_ is not StringVariable:\n        nana = np.isnan(a)\n        nanb = np.isnan(b)\n        return np.all(nana == nanb) and np.all(a[~nana] == b[~nanb])\n    else:\n        return np.all(a == b)\n\ndef pad_columns(values, mask, l):\n    #inflates columns with nans\n    a = np.full((l, 1), np.nan, dtype=values.dtype)\n    a[mask] = values.reshape(-1, 1)\n    return a\n\ndef get_perm(ids, all_ids):\n    return [all_ids.index(el) for el in ids if el in all_ids]\n\n\ndef main():  # pragma: no cover\n    # pylint: disable=import-outside-toplevel\n    from Orange.evaluation import ShuffleSplit\n\n    data = Table(\"brown-selected\")\n\n    if not \"test_rows\":  # change to `if not \"test_rows\" to test columns\n        data = append_column(data, \"M\", StringVariable(\"Test\"),\n                             (np.arange(len(data)).reshape(-1, 1) % 30).astype(str))\n        res = ShuffleSplit(n_resamples=5, test_size=0.7, stratified=False)\n        indices = iter(res.get_indices(data))\n        datasets = []\n        for i in range(1, 6):\n            sample, _ = next(indices)\n            data1 = data[sample]\n            data1.name = chr(ord(\"A\") + i)\n            datasets.append((data1, i))\n    else:\n        domain = data.domain\n        data1 = data.transform(Domain(domain.attributes[:15], domain.class_var))\n        data2 = data.transform(Domain(domain.attributes[10:], domain.class_var))\n        datasets = [(data1, 1), (data2, 2)]\n\n    WidgetPreview(OWVennDiagram).run(setData=datasets)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    main()\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/visualize/owvenndiagram.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "aa229a473",
                "new_commit_hash": "0076a8280",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/visualize/owvenndiagram.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/visualize/owvenndiagram.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 379,
                            "length": 16
                        },
                        "new_range": {
                            "start": 379,
                            "length": 19
                        },
                        "section": "class OWVennDiagram(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            X = np.hstack(values['attributes'])",
                                "type": "context"
                            },
                            {
                                "content": "        if 'metas' in values:",
                                "type": "context"
                            },
                            {
                                "content": "            metas = np.hstack(values['metas'])",
                                "type": "context"
                            },
                            {
                                "content": "            n = len(metas)",
                                "type": "added"
                            },
                            {
                                "content": "        if 'class_vars' in values:",
                                "type": "context"
                            },
                            {
                                "content": "            class_vars = np.hstack(values['class_vars'])",
                                "type": "context"
                            },
                            {
                                "content": "            n = len(class_vars)",
                                "type": "added"
                            },
                            {
                                "content": "        if X is None:",
                                "type": "added"
                            },
                            {
                                "content": "            X = np.empty((n, 0))",
                                "type": "added"
                            },
                            {
                                "content": "        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)",
                                "type": "context"
                            },
                            {
                                "content": "        if ids is not None:",
                                "type": "context"
                            },
                            {
                                "content": "            table.ids = ids",
                                "type": "context"
                            },
                            {
                                "content": "        return table",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def extract_columnwise(self, var_dict, columns=None):",
                                "type": "context"
                            },
                            {
                                "content": "        #for columns",
                                "type": "deleted"
                            },
                            {
                                "content": "        domain = defaultdict(list)",
                                "type": "deleted"
                            },
                            {
                                "content": "        domain = {type_ : [] for type_ in self.atr_types}",
                                "type": "added"
                            },
                            {
                                "content": "        values = defaultdict(list)",
                                "type": "context"
                            },
                            {
                                "content": "        renamed = []",
                                "type": "context"
                            },
                            {
                                "content": "        for atr_type, vars_dict in var_dict.items():",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "method",
                            "name": "OWVennDiagram.extract_columnwise",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 389,
                            "end_lineno": 419,
                            "parent": {
                                "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                                "type": "class",
                                "name": "OWVennDiagram",
                                "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 42,
                                "end_lineno": 703,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "method",
                            "name": "OWVennDiagram.merge_data",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n            n = len(metas)\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n            n = len(class_vars)\n        if X is None:\n            X = np.empty((n, 0))\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        domain = {type_ : [] for type_ in self.atr_types}\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = {type_ : [] for type_ in self.atr_types}\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        ids = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, ids)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 366,
                            "end_lineno": 391,
                            "parent": {
                                "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                                "type": "class",
                                "name": "OWVennDiagram",
                                "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n            n = len(metas)\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n            n = len(class_vars)\n        if X is None:\n            X = np.empty((n, 0))\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        domain = {type_ : [] for type_ in self.atr_types}\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = {type_ : [] for type_ in self.atr_types}\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        ids = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, ids)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 42,
                                "end_lineno": 706,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "class",
                            "name": "OWVennDiagram",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 42,
                            "end_lineno": 703,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 514,
                            "length": 7
                        },
                        "new_range": {
                            "start": 517,
                            "length": 7
                        },
                        "section": "class OWVennDiagram(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        for table_key, dict_ in ids.items():",
                                "type": "context"
                            },
                            {
                                "content": "            permutations[table_key] = get_perm(list(dict_), all_ids)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        domain = defaultdict(list)",
                                "type": "deleted"
                            },
                            {
                                "content": "        domain = {type_ : [] for type_ in self.atr_types}",
                                "type": "added"
                            },
                            {
                                "content": "        values = defaultdict(list)",
                                "type": "context"
                            },
                            {
                                "content": "        renamed = []",
                                "type": "context"
                            },
                            {
                                "content": "        for atr_type, vars_dict in var_dict.items():",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "class",
                            "name": "OWVennDiagram",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 42,
                            "end_lineno": 703,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "method",
                            "name": "OWVennDiagram.extract_rowwise",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 504,
                            "end_lineno": 559,
                            "parent": {
                                "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                                "type": "class",
                                "name": "OWVennDiagram",
                                "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 42,
                                "end_lineno": 703,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 551,
                            "length": 8
                        },
                        "new_range": {
                            "start": 554,
                            "length": 8
                        },
                        "section": "class OWVennDiagram(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        if renamed:",
                                "type": "context"
                            },
                            {
                                "content": "            self.Warning.renamed_vars(', '.join(renamed))",
                                "type": "context"
                            },
                            {
                                "content": "        idas = None if self.selected_feature else np.array(all_ids)",
                                "type": "deleted"
                            },
                            {
                                "content": "        table = self.merge_data(domain, values, idas)",
                                "type": "deleted"
                            },
                            {
                                "content": "        ids = None if self.selected_feature else np.array(all_ids)",
                                "type": "added"
                            },
                            {
                                "content": "        table = self.merge_data(domain, values, ids)",
                                "type": "added"
                            },
                            {
                                "content": "        if selection:",
                                "type": "context"
                            },
                            {
                                "content": "            mask = [idx in self.selected_items for idx in all_ids]",
                                "type": "context"
                            },
                            {
                                "content": "            return create_annotated_table(table, mask)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "class",
                            "name": "OWVennDiagram",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 42,
                            "end_lineno": 703,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                            "type": "method",
                            "name": "OWVennDiagram.extract_rowwise",
                            "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 504,
                            "end_lineno": 559,
                            "parent": {
                                "file_name": "Orange/widgets/visualize/owvenndiagram.py",
                                "type": "class",
                                "name": "OWVennDiagram",
                                "content": "class OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 42,
                                "end_lineno": 703,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring\n\nimport unittest\nfrom unittest.mock import patch\nfrom copy import deepcopy\n\nimport numpy as np\n\nfrom Orange.data import (Table, Domain, StringVariable,\n                         ContinuousVariable)\nfrom Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin\nfrom Orange.widgets.utils.annotated_data import (ANNOTATED_DATA_FEATURE_NAME)\nfrom Orange.widgets.visualize.owvenndiagram import (\n                                                    OWVennDiagram,\n                                                    arrays_equal,\n                                                    pad_columns,\n                                                    get_perm)\n\n\nclass TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        WidgetOutputsTestMixin.init(cls, False)\n\n        cls.signal_data = cls.data[:25]\n\n    def setUp(self):\n        self.widget = self.create_widget(OWVennDiagram)\n        self.signal_name = self.widget.Inputs.data\n\n    def _select_data(self):\n        self.widget.vennwidget.vennareas()[1].setSelected(True)\n        return list(range(len(self.signal_data)))\n\n    def test_rows_id(self):\n        data = Table('zoo')\n        data1 = deepcopy(data)\n        data1[:, 1] = 1\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, data1[:10], 1)\n        self.widget.selected_feature = None\n        self.send_signal(self.signal_name, data[5:10], 2)\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        sel_atrs = [atr.name for atr in selected.domain.attributes]\n        true_atrs = ['hair', 'feathers (1)', 'feathers (2)', 'eggs', 'milk', 'airborne', 'aquatic',\n                     'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs',\n                     'tail', 'domestic', 'catsize']\n        self.assertEqual(sel_atrs, true_atrs)\n        self.assertEqual(selected.domain.metas, data.domain.metas)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n    def test_output_duplicates(self):\n        self.widget.rowwise = True\n        self.widget.output_duplicates = True\n        self.send_signal(self.signal_name, self.data[:2], 1)\n        self.send_signal(self.signal_name, self.data[:4], 2)\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        in_ids = self.data[:2].ids\n        true_ids = np.array([in_ids[0], in_ids[0], in_ids[1], in_ids[1]])\n        np.testing.assert_array_equal(selected.ids.flatten(), true_ids)\n\n    def test_multiple_input_rows_id(self):\n        \"\"\"Over rows\"\"\"\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n\n        # check selected data output\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n\n        # check annotated data output\n        feature_name = ANNOTATED_DATA_FEATURE_NAME\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected_indices = list(range(50, 100))\n\n        # check selected data output\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        n_sel, n_attr = len(selected), len(self.data.domain.attributes)\n        self.assertGreater(n_sel, 0)\n        self.assertEqual(selected.domain == self.data.domain,\n                         self.same_input_output_domain)\n        np.testing.assert_array_equal(selected.X[:, :n_attr],\n                                      self.data.X[selected_indices])\n\n        # check annotated data output\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(n_sel, np.sum([i[feature_name] for i in annotated]))\n\n        # compare selected and annotated data domains\n        self._compare_selected_annotated_domains(selected, annotated)\n\n        # check output when data is removed\n        self.send_signal(self.signal_name, None, 1)\n        self.send_signal(self.signal_name, None, 2)\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))\n\n    def test_multiple_input_over_cols(self):\n        self.widget.rowwise = False\n        selected_atr_name = 'Selected'\n        input2 = self.data.transform(Domain([self.data.domain.attributes[0]],\n                                            self.data.domain.class_vars,\n                                            self.data.domain.metas))\n        self.send_signal(self.signal_name, self.data, (1, 'Data', None))\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNone(selected)\n        self.assertEqual(len(annotated), len(self.data))\n        self.assertEqual(annotated.domain, self.data.domain)\n        for atr in annotated.domain.attributes:\n            self.assertFalse(atr.attributes)\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        np.testing.assert_array_equal(selected.X,\n                                      input2.X)\n        np.testing.assert_array_equal(selected.Y,\n                                      input2.Y)\n        np.testing.assert_array_equal(selected.metas,\n                                      input2.metas)\n\n        #domain matches but the values do not\n        input2.X = input2.X - 1\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        atrs = {atr.name for atr in selected.domain.attributes}\n        true_atrs = {'sepal length (2)', 'sepal length (1)'}\n        self.assertTrue(atrs == true_atrs)\n\n        out_domain = annotated.domain.attributes\n        self.assertTrue(out_domain[0].attributes[selected_atr_name])\n        self.assertTrue(out_domain[1].attributes[selected_atr_name])\n        self.assertFalse(out_domain[2].attributes[selected_atr_name])\n        self.assertFalse(out_domain[3].attributes[selected_atr_name])\n        self.assertFalse(out_domain[4].attributes[selected_atr_name])\n\n    def test_no_data(self):\n        \"\"\"Check that the widget doesn't crash on empty data\"\"\"\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[:100], 2)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n        self.send_signal(self.signal_name, self.data[:0], 3)\n\n    def test_unconditional_commit_on_new_signal(self):\n        with patch.object(self.widget, 'unconditional_commit') as commit:\n            self.widget.autocommit = False\n            commit.reset_mock()\n            self.send_signal(self.signal_name, self.data[:100], 1)\n            commit.assert_called()\n\n    def test_input_compatibility(self):\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name,\n                         self.data.transform(Domain([self.data.domain.attributes[0]],\n                                                    self.data.domain.class_vars,\n                                                    self.data.domain.metas)), 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n        self.widget.rowwise = False\n        self.send_signal(self.signal_name, self.data[:100, :], 2)\n        self.assertTrue(self.widget.Error.instances_mismatch.is_shown())\n\n        self.send_signal(self.signal_name, None, 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n    def test_rows_identifiers(self):\n        self.widget.rowwise = True\n        data = Table('zoo')\n        self.send_signal(self.signal_name, data, (1, 'Data', None))\n        self.widget.selected_feature = data.domain.metas[0]\n        self.send_signal(self.signal_name, data[:5], (2, 'Data', None))\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        self.assertEqual(len(selected), 5)\n        self.assertEqual(selected.domain.attributes, data.domain.attributes)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 100)\n\n    def test_too_many_inputs(self):\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name, self.data, 2)\n        self.send_signal(self.signal_name, self.data, 3)\n        self.send_signal(self.signal_name, self.data, 4)\n        self.send_signal(self.signal_name, self.data, 5)\n        self.send_signal(self.signal_name, self.data, 6)\n        self.assertTrue(self.widget.Error.too_many_inputs.is_shown())\n\n        self.send_signal(self.signal_name, None, 6)\n        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())\n\n\nclass TestVennUtilities(unittest.TestCase):\n\n    def test_array_equals_cols(self):\n        a = np.array([1, 2], dtype=np.float64)\n        b = np.array([1, np.nan], dtype=np.float64)\n        self.assertTrue(arrays_equal(None, None, None))\n        self.assertFalse(arrays_equal(None, a, None))\n        self.assertFalse(arrays_equal(a, None, None))\n        self.assertFalse(arrays_equal(a, b, ContinuousVariable))\n        a[1] = np.nan\n        self.assertTrue(arrays_equal(a, b, ContinuousVariable))\n        self.assertTrue(arrays_equal(a.astype(str), a.astype(str), StringVariable))\n        a[1] = 2\n        b[1] = 3\n        self.assertFalse(arrays_equal(a, b, ContinuousVariable))\n        self.assertFalse(arrays_equal(a.astype(str), b.astype(str), StringVariable))\n\n    def test_pad_columns(self):\n        l = 5\n        mask = [2, 3]\n        values = np.array([7.2, 77.3]).reshape(-1, 1)\n        res = pad_columns(values, mask, l)\n        true_res = np.array([np.nan, np.nan, 7.2, 77.3, np.nan]).reshape(-1, 1)\n        np.testing.assert_array_equal(res, true_res)\n\n    def test_get_perm(self):\n        all_ids = [1, 7, 22]\n        res = get_perm([7, 33], all_ids)\n        true_res = [1]\n        self.assertEqual(res, true_res)\n\n        res = get_perm([22, 1, 7], all_ids)\n        true_res = [2, 0, 1]\n        self.assertEqual(res, true_res)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "new_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring\n\nimport unittest\nfrom unittest.mock import patch\nfrom copy import deepcopy\n\nimport numpy as np\n\nfrom Orange.data import (Table, Domain, StringVariable,\n                         ContinuousVariable)\nfrom Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin\nfrom Orange.widgets.utils.annotated_data import (ANNOTATED_DATA_FEATURE_NAME)\nfrom Orange.widgets.visualize.owvenndiagram import (\n                        OWVennDiagram,\n                        arrays_equal,\n                        pad_columns,\n                        get_perm)\n\n\nclass TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        WidgetOutputsTestMixin.init(cls, False)\n\n        cls.signal_data = cls.data[:25]\n\n    def setUp(self):\n        self.widget = self.create_widget(OWVennDiagram)\n        self.signal_name = self.widget.Inputs.data\n\n    def _select_data(self):\n        self.widget.vennwidget.vennareas()[1].setSelected(True)\n        return list(range(len(self.signal_data)))\n\n    def test_rows_id(self):\n        data = Table('zoo')\n        data1 = deepcopy(data)\n        data1[:, 1] = 1\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, data1[:10], 1)\n        self.widget.selected_feature = None\n        self.send_signal(self.signal_name, data[5:10], 2)\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        sel_atrs = [atr.name for atr in selected.domain.attributes]\n        true_atrs = ['hair', 'feathers (1)', 'feathers (2)', 'eggs', 'milk', 'airborne', 'aquatic',\n                     'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs',\n                     'tail', 'domestic', 'catsize']\n        self.assertEqual(sel_atrs, true_atrs)\n        self.assertEqual(selected.domain.metas, data.domain.metas)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n    def test_output_duplicates(self):\n        self.widget.rowwise = True\n        self.widget.output_duplicates = True\n        self.send_signal(self.signal_name, self.data[:2], 1)\n        self.send_signal(self.signal_name, self.data[:4], 2)\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        in_ids = self.data[:2].ids\n        true_ids = np.array([in_ids[0], in_ids[0], in_ids[1], in_ids[1]])\n        np.testing.assert_array_equal(selected.ids.flatten(), true_ids)\n\n    def test_multiple_input_rows_id(self):\n        \"\"\"Over rows\"\"\"\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n\n        # check selected data output\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n\n        # check annotated data output\n        feature_name = ANNOTATED_DATA_FEATURE_NAME\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected_indices = list(range(50, 100))\n\n        # check selected data output\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        n_sel, n_attr = len(selected), len(self.data.domain.attributes)\n        self.assertGreater(n_sel, 0)\n        self.assertEqual(selected.domain == self.data.domain,\n                         self.same_input_output_domain)\n        np.testing.assert_array_equal(selected.X[:, :n_attr],\n                                      self.data.X[selected_indices])\n\n        # check annotated data output\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(n_sel, np.sum([i[feature_name] for i in annotated]))\n\n        # compare selected and annotated data domains\n        self._compare_selected_annotated_domains(selected, annotated)\n\n        # check output when data is removed\n        self.send_signal(self.signal_name, None, 1)\n        self.send_signal(self.signal_name, None, 2)\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))\n\n    def test_multiple_input_over_cols(self):\n        self.widget.rowwise = False\n        selected_atr_name = 'Selected'\n        input2 = self.data.transform(Domain([self.data.domain.attributes[0]],\n                                            self.data.domain.class_vars,\n                                            self.data.domain.metas))\n        self.send_signal(self.signal_name, self.data, (1, 'Data', None))\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNone(selected)\n        self.assertEqual(len(annotated), len(self.data))\n        self.assertEqual(annotated.domain, self.data.domain)\n        for atr in annotated.domain.attributes:\n            self.assertFalse(atr.attributes)\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        np.testing.assert_array_equal(selected.X,\n                                      input2.X)\n        np.testing.assert_array_equal(selected.Y,\n                                      input2.Y)\n        np.testing.assert_array_equal(selected.metas,\n                                      input2.metas)\n\n        #domain matches but the values do not\n        input2.X = input2.X - 1\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        atrs = {atr.name for atr in selected.domain.attributes}\n        true_atrs = {'sepal length (2)', 'sepal length (1)'}\n        self.assertTrue(atrs == true_atrs)\n\n        out_domain = annotated.domain.attributes\n        self.assertTrue(out_domain[0].attributes[selected_atr_name])\n        self.assertTrue(out_domain[1].attributes[selected_atr_name])\n        self.assertFalse(out_domain[2].attributes[selected_atr_name])\n        self.assertFalse(out_domain[3].attributes[selected_atr_name])\n        self.assertFalse(out_domain[4].attributes[selected_atr_name])\n\n    def test_no_data(self):\n        \"\"\"Check that the widget doesn't crash on empty data\"\"\"\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[:100], 2)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n        self.send_signal(self.signal_name, self.data[:0], 3)\n\n    def test_unconditional_commit_on_new_signal(self):\n        with patch.object(self.widget, 'unconditional_commit') as commit:\n            self.widget.autocommit = False\n            commit.reset_mock()\n            self.send_signal(self.signal_name, self.data[:100], 1)\n            commit.assert_called()\n\n    def test_input_compatibility(self):\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name,\n                         self.data.transform(Domain([self.data.domain.attributes[0]],\n                                                    self.data.domain.class_vars,\n                                                    self.data.domain.metas)), 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n        self.widget.rowwise = False\n        self.send_signal(self.signal_name, self.data[:100, :], 2)\n        self.assertTrue(self.widget.Error.instances_mismatch.is_shown())\n\n        self.send_signal(self.signal_name, None, 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n    def test_rows_identifiers(self):\n        self.widget.rowwise = True\n        data = Table('zoo')\n        self.send_signal(self.signal_name, data, (1, 'Data', None))\n        self.widget.selected_feature = data.domain.metas[0]\n        self.send_signal(self.signal_name, data[:5], (2, 'Data', None))\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        self.assertEqual(len(selected), 5)\n        self.assertEqual(selected.domain.attributes, data.domain.attributes)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 100)\n\n    def test_too_many_inputs(self):\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name, self.data, 2)\n        self.send_signal(self.signal_name, self.data, 3)\n        self.send_signal(self.signal_name, self.data, 4)\n        self.send_signal(self.signal_name, self.data, 5)\n        self.send_signal(self.signal_name, self.data, 6)\n        self.assertTrue(self.widget.Error.too_many_inputs.is_shown())\n\n        self.send_signal(self.signal_name, None, 6)\n        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())\n\n    def test_no_attributes(self):\n        domain = Domain([], class_vars=self.data.domain.attributes)\n        n = len(self.data)\n        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)\n\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, table, 1)\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(out), len(table))\n\nclass TestVennUtilities(unittest.TestCase):\n\n    def test_array_equals_cols(self):\n        a = np.array([1, 2], dtype=np.float64)\n        b = np.array([1, np.nan], dtype=np.float64)\n        self.assertTrue(arrays_equal(None, None, None))\n        self.assertFalse(arrays_equal(None, a, None))\n        self.assertFalse(arrays_equal(a, None, None))\n        self.assertFalse(arrays_equal(a, b, ContinuousVariable))\n        a[1] = np.nan\n        self.assertTrue(arrays_equal(a, b, ContinuousVariable))\n        self.assertTrue(arrays_equal(a.astype(str), a.astype(str), StringVariable))\n        a[1] = 2\n        b[1] = 3\n        self.assertFalse(arrays_equal(a, b, ContinuousVariable))\n        self.assertFalse(arrays_equal(a.astype(str), b.astype(str), StringVariable))\n\n    def test_pad_columns(self):\n        l = 5\n        mask = [2, 3]\n        values = np.array([7.2, 77.3]).reshape(-1, 1)\n        res = pad_columns(values, mask, l)\n        true_res = np.array([np.nan, np.nan, 7.2, 77.3, np.nan]).reshape(-1, 1)\n        np.testing.assert_array_equal(res, true_res)\n\n    def test_get_perm(self):\n        all_ids = [1, 7, 22]\n        res = get_perm([7, 33], all_ids)\n        true_res = [1]\n        self.assertEqual(res, true_res)\n\n        res = get_perm([22, 1, 7], all_ids)\n        true_res = [2, 0, 1]\n        self.assertEqual(res, true_res)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/visualize/tests/test_owvenndiagram.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "7f892dd00",
                "new_commit_hash": "cf1d40664",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/visualize/tests/test_owvenndiagram.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/visualize/tests/test_owvenndiagram.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 12,
                            "length": 10
                        },
                        "new_range": {
                            "start": 12,
                            "length": 10
                        },
                        "section": "from Orange.data import (Table, Domain, StringVariable,"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.utils.annotated_data import (ANNOTATED_DATA_FEATURE_NAME)",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.visualize.owvenndiagram import (",
                                "type": "context"
                            },
                            {
                                "content": "                                                    OWVennDiagram,",
                                "type": "deleted"
                            },
                            {
                                "content": "                                                    arrays_equal,",
                                "type": "deleted"
                            },
                            {
                                "content": "                                                    pad_columns,",
                                "type": "deleted"
                            },
                            {
                                "content": "                                                    get_perm)",
                                "type": "deleted"
                            },
                            {
                                "content": "                        OWVennDiagram,",
                                "type": "added"
                            },
                            {
                                "content": "                        arrays_equal,",
                                "type": "added"
                            },
                            {
                                "content": "                        pad_columns,",
                                "type": "added"
                            },
                            {
                                "content": "                        get_perm)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/tests/test_owvenndiagram.py",
                            "type": "statement",
                            "name": "",
                            "content": "from Orange.widgets.visualize.owvenndiagram import (\n                        OWVennDiagram,\n                        arrays_equal,\n                        pad_columns,\n                        get_perm)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 14,
                            "end_lineno": 18,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/tests/test_owvenndiagram.py",
                            "type": "statement",
                            "name": "",
                            "content": "from Orange.widgets.visualize.owvenndiagram import (\n                                                    OWVennDiagram,\n                                                    arrays_equal,\n                                                    pad_columns,\n                                                    get_perm)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 14,
                            "end_lineno": 18,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 224,
                            "length": 6
                        },
                        "new_range": {
                            "start": 224,
                            "length": 15
                        },
                        "section": "class TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.send_signal(self.signal_name, None, 6)",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_no_attributes(self):",
                                "type": "added"
                            },
                            {
                                "content": "        domain = Domain([], class_vars=self.data.domain.attributes)",
                                "type": "added"
                            },
                            {
                                "content": "        n = len(self.data)",
                                "type": "added"
                            },
                            {
                                "content": "        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.widget.rowwise = True",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.signal_name, table, 1)",
                                "type": "added"
                            },
                            {
                                "content": "        out = self.get_output(self.widget.Outputs.annotated_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(len(out), len(table))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class TestVennUtilities(unittest.TestCase):",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/tests/test_owvenndiagram.py",
                            "type": "class",
                            "name": "TestOWVennDiagram",
                            "content": "class TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        WidgetOutputsTestMixin.init(cls, False)\n\n        cls.signal_data = cls.data[:25]\n\n    def setUp(self):\n        self.widget = self.create_widget(OWVennDiagram)\n        self.signal_name = self.widget.Inputs.data\n\n    def _select_data(self):\n        self.widget.vennwidget.vennareas()[1].setSelected(True)\n        return list(range(len(self.signal_data)))\n\n    def test_rows_id(self):\n        data = Table('zoo')\n        data1 = deepcopy(data)\n        data1[:, 1] = 1\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, data1[:10], 1)\n        self.widget.selected_feature = None\n        self.send_signal(self.signal_name, data[5:10], 2)\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        sel_atrs = [atr.name for atr in selected.domain.attributes]\n        true_atrs = ['hair', 'feathers (1)', 'feathers (2)', 'eggs', 'milk', 'airborne', 'aquatic',\n                     'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs',\n                     'tail', 'domestic', 'catsize']\n        self.assertEqual(sel_atrs, true_atrs)\n        self.assertEqual(selected.domain.metas, data.domain.metas)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n    def test_output_duplicates(self):\n        self.widget.rowwise = True\n        self.widget.output_duplicates = True\n        self.send_signal(self.signal_name, self.data[:2], 1)\n        self.send_signal(self.signal_name, self.data[:4], 2)\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        in_ids = self.data[:2].ids\n        true_ids = np.array([in_ids[0], in_ids[0], in_ids[1], in_ids[1]])\n        np.testing.assert_array_equal(selected.ids.flatten(), true_ids)\n\n    def test_multiple_input_rows_id(self):\n        \"\"\"Over rows\"\"\"\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n\n        # check selected data output\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n\n        # check annotated data output\n        feature_name = ANNOTATED_DATA_FEATURE_NAME\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected_indices = list(range(50, 100))\n\n        # check selected data output\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        n_sel, n_attr = len(selected), len(self.data.domain.attributes)\n        self.assertGreater(n_sel, 0)\n        self.assertEqual(selected.domain == self.data.domain,\n                         self.same_input_output_domain)\n        np.testing.assert_array_equal(selected.X[:, :n_attr],\n                                      self.data.X[selected_indices])\n\n        # check annotated data output\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(n_sel, np.sum([i[feature_name] for i in annotated]))\n\n        # compare selected and annotated data domains\n        self._compare_selected_annotated_domains(selected, annotated)\n\n        # check output when data is removed\n        self.send_signal(self.signal_name, None, 1)\n        self.send_signal(self.signal_name, None, 2)\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))\n\n    def test_multiple_input_over_cols(self):\n        self.widget.rowwise = False\n        selected_atr_name = 'Selected'\n        input2 = self.data.transform(Domain([self.data.domain.attributes[0]],\n                                            self.data.domain.class_vars,\n                                            self.data.domain.metas))\n        self.send_signal(self.signal_name, self.data, (1, 'Data', None))\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNone(selected)\n        self.assertEqual(len(annotated), len(self.data))\n        self.assertEqual(annotated.domain, self.data.domain)\n        for atr in annotated.domain.attributes:\n            self.assertFalse(atr.attributes)\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        np.testing.assert_array_equal(selected.X,\n                                      input2.X)\n        np.testing.assert_array_equal(selected.Y,\n                                      input2.Y)\n        np.testing.assert_array_equal(selected.metas,\n                                      input2.metas)\n\n        #domain matches but the values do not\n        input2.X = input2.X - 1\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        atrs = {atr.name for atr in selected.domain.attributes}\n        true_atrs = {'sepal length (2)', 'sepal length (1)'}\n        self.assertTrue(atrs == true_atrs)\n\n        out_domain = annotated.domain.attributes\n        self.assertTrue(out_domain[0].attributes[selected_atr_name])\n        self.assertTrue(out_domain[1].attributes[selected_atr_name])\n        self.assertFalse(out_domain[2].attributes[selected_atr_name])\n        self.assertFalse(out_domain[3].attributes[selected_atr_name])\n        self.assertFalse(out_domain[4].attributes[selected_atr_name])\n\n    def test_no_data(self):\n        \"\"\"Check that the widget doesn't crash on empty data\"\"\"\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[:100], 2)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n        self.send_signal(self.signal_name, self.data[:0], 3)\n\n    def test_unconditional_commit_on_new_signal(self):\n        with patch.object(self.widget, 'unconditional_commit') as commit:\n            self.widget.autocommit = False\n            commit.reset_mock()\n            self.send_signal(self.signal_name, self.data[:100], 1)\n            commit.assert_called()\n\n    def test_input_compatibility(self):\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name,\n                         self.data.transform(Domain([self.data.domain.attributes[0]],\n                                                    self.data.domain.class_vars,\n                                                    self.data.domain.metas)), 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n        self.widget.rowwise = False\n        self.send_signal(self.signal_name, self.data[:100, :], 2)\n        self.assertTrue(self.widget.Error.instances_mismatch.is_shown())\n\n        self.send_signal(self.signal_name, None, 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n    def test_rows_identifiers(self):\n        self.widget.rowwise = True\n        data = Table('zoo')\n        self.send_signal(self.signal_name, data, (1, 'Data', None))\n        self.widget.selected_feature = data.domain.metas[0]\n        self.send_signal(self.signal_name, data[:5], (2, 'Data', None))\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        self.assertEqual(len(selected), 5)\n        self.assertEqual(selected.domain.attributes, data.domain.attributes)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 100)\n\n    def test_too_many_inputs(self):\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name, self.data, 2)\n        self.send_signal(self.signal_name, self.data, 3)\n        self.send_signal(self.signal_name, self.data, 4)\n        self.send_signal(self.signal_name, self.data, 5)\n        self.send_signal(self.signal_name, self.data, 6)\n        self.assertTrue(self.widget.Error.too_many_inputs.is_shown())\n\n        self.send_signal(self.signal_name, None, 6)\n        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())\n\n    def test_no_attributes(self):\n        domain = Domain([], class_vars=self.data.domain.attributes)\n        n = len(self.data)\n        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)\n\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, table, 1)\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(out), len(table))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 21,
                            "end_lineno": 235,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/visualize/tests/test_owvenndiagram.py",
                            "type": "method",
                            "name": "TestOWVennDiagram.test_no_attributes",
                            "content": "class TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        WidgetOutputsTestMixin.init(cls, False)\n\n        cls.signal_data = cls.data[:25]\n\n    def setUp(self):\n        self.widget = self.create_widget(OWVennDiagram)\n        self.signal_name = self.widget.Inputs.data\n\n    def _select_data(self):\n        self.widget.vennwidget.vennareas()[1].setSelected(True)\n        return list(range(len(self.signal_data)))\n\n    def test_rows_id(self):\n        data = Table('zoo')\n        data1 = deepcopy(data)\n        data1[:, 1] = 1\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, data1[:10], 1)\n        self.widget.selected_feature = None\n        self.send_signal(self.signal_name, data[5:10], 2)\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        sel_atrs = [atr.name for atr in selected.domain.attributes]\n        true_atrs = ['hair', 'feathers (1)', 'feathers (2)', 'eggs', 'milk', 'airborne', 'aquatic',\n                     'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs',\n                     'tail', 'domestic', 'catsize']\n        self.assertEqual(sel_atrs, true_atrs)\n        self.assertEqual(selected.domain.metas, data.domain.metas)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n    def test_output_duplicates(self):\n        self.widget.rowwise = True\n        self.widget.output_duplicates = True\n        self.send_signal(self.signal_name, self.data[:2], 1)\n        self.send_signal(self.signal_name, self.data[:4], 2)\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        in_ids = self.data[:2].ids\n        true_ids = np.array([in_ids[0], in_ids[0], in_ids[1], in_ids[1]])\n        np.testing.assert_array_equal(selected.ids.flatten(), true_ids)\n\n    def test_multiple_input_rows_id(self):\n        \"\"\"Over rows\"\"\"\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n\n        # check selected data output\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n\n        # check annotated data output\n        feature_name = ANNOTATED_DATA_FEATURE_NAME\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected_indices = list(range(50, 100))\n\n        # check selected data output\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        n_sel, n_attr = len(selected), len(self.data.domain.attributes)\n        self.assertGreater(n_sel, 0)\n        self.assertEqual(selected.domain == self.data.domain,\n                         self.same_input_output_domain)\n        np.testing.assert_array_equal(selected.X[:, :n_attr],\n                                      self.data.X[selected_indices])\n\n        # check annotated data output\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(n_sel, np.sum([i[feature_name] for i in annotated]))\n\n        # compare selected and annotated data domains\n        self._compare_selected_annotated_domains(selected, annotated)\n\n        # check output when data is removed\n        self.send_signal(self.signal_name, None, 1)\n        self.send_signal(self.signal_name, None, 2)\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))\n\n    def test_multiple_input_over_cols(self):\n        self.widget.rowwise = False\n        selected_atr_name = 'Selected'\n        input2 = self.data.transform(Domain([self.data.domain.attributes[0]],\n                                            self.data.domain.class_vars,\n                                            self.data.domain.metas))\n        self.send_signal(self.signal_name, self.data, (1, 'Data', None))\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNone(selected)\n        self.assertEqual(len(annotated), len(self.data))\n        self.assertEqual(annotated.domain, self.data.domain)\n        for atr in annotated.domain.attributes:\n            self.assertFalse(atr.attributes)\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        np.testing.assert_array_equal(selected.X,\n                                      input2.X)\n        np.testing.assert_array_equal(selected.Y,\n                                      input2.Y)\n        np.testing.assert_array_equal(selected.metas,\n                                      input2.metas)\n\n        #domain matches but the values do not\n        input2.X = input2.X - 1\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        atrs = {atr.name for atr in selected.domain.attributes}\n        true_atrs = {'sepal length (2)', 'sepal length (1)'}\n        self.assertTrue(atrs == true_atrs)\n\n        out_domain = annotated.domain.attributes\n        self.assertTrue(out_domain[0].attributes[selected_atr_name])\n        self.assertTrue(out_domain[1].attributes[selected_atr_name])\n        self.assertFalse(out_domain[2].attributes[selected_atr_name])\n        self.assertFalse(out_domain[3].attributes[selected_atr_name])\n        self.assertFalse(out_domain[4].attributes[selected_atr_name])\n\n    def test_no_data(self):\n        \"\"\"Check that the widget doesn't crash on empty data\"\"\"\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[:100], 2)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n        self.send_signal(self.signal_name, self.data[:0], 3)\n\n    def test_unconditional_commit_on_new_signal(self):\n        with patch.object(self.widget, 'unconditional_commit') as commit:\n            self.widget.autocommit = False\n            commit.reset_mock()\n            self.send_signal(self.signal_name, self.data[:100], 1)\n            commit.assert_called()\n\n    def test_input_compatibility(self):\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name,\n                         self.data.transform(Domain([self.data.domain.attributes[0]],\n                                                    self.data.domain.class_vars,\n                                                    self.data.domain.metas)), 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n        self.widget.rowwise = False\n        self.send_signal(self.signal_name, self.data[:100, :], 2)\n        self.assertTrue(self.widget.Error.instances_mismatch.is_shown())\n\n        self.send_signal(self.signal_name, None, 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n    def test_rows_identifiers(self):\n        self.widget.rowwise = True\n        data = Table('zoo')\n        self.send_signal(self.signal_name, data, (1, 'Data', None))\n        self.widget.selected_feature = data.domain.metas[0]\n        self.send_signal(self.signal_name, data[:5], (2, 'Data', None))\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        self.assertEqual(len(selected), 5)\n        self.assertEqual(selected.domain.attributes, data.domain.attributes)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 100)\n\n    def test_too_many_inputs(self):\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name, self.data, 2)\n        self.send_signal(self.signal_name, self.data, 3)\n        self.send_signal(self.signal_name, self.data, 4)\n        self.send_signal(self.signal_name, self.data, 5)\n        self.send_signal(self.signal_name, self.data, 6)\n        self.assertTrue(self.widget.Error.too_many_inputs.is_shown())\n\n        self.send_signal(self.signal_name, None, 6)\n        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())\n\n    def test_no_attributes(self):\n        domain = Domain([], class_vars=self.data.domain.attributes)\n        n = len(self.data)\n        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)\n\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, table, 1)\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(out), len(table))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 227,
                            "end_lineno": 235,
                            "parent": {
                                "file_name": "Orange/widgets/visualize/tests/test_owvenndiagram.py",
                                "type": "class",
                                "name": "TestOWVennDiagram",
                                "content": "class TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        WidgetOutputsTestMixin.init(cls, False)\n\n        cls.signal_data = cls.data[:25]\n\n    def setUp(self):\n        self.widget = self.create_widget(OWVennDiagram)\n        self.signal_name = self.widget.Inputs.data\n\n    def _select_data(self):\n        self.widget.vennwidget.vennareas()[1].setSelected(True)\n        return list(range(len(self.signal_data)))\n\n    def test_rows_id(self):\n        data = Table('zoo')\n        data1 = deepcopy(data)\n        data1[:, 1] = 1\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, data1[:10], 1)\n        self.widget.selected_feature = None\n        self.send_signal(self.signal_name, data[5:10], 2)\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        sel_atrs = [atr.name for atr in selected.domain.attributes]\n        true_atrs = ['hair', 'feathers (1)', 'feathers (2)', 'eggs', 'milk', 'airborne', 'aquatic',\n                     'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs',\n                     'tail', 'domestic', 'catsize']\n        self.assertEqual(sel_atrs, true_atrs)\n        self.assertEqual(selected.domain.metas, data.domain.metas)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n    def test_output_duplicates(self):\n        self.widget.rowwise = True\n        self.widget.output_duplicates = True\n        self.send_signal(self.signal_name, self.data[:2], 1)\n        self.send_signal(self.signal_name, self.data[:4], 2)\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        in_ids = self.data[:2].ids\n        true_ids = np.array([in_ids[0], in_ids[0], in_ids[1], in_ids[1]])\n        np.testing.assert_array_equal(selected.ids.flatten(), true_ids)\n\n    def test_multiple_input_rows_id(self):\n        \"\"\"Over rows\"\"\"\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n\n        # check selected data output\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n\n        # check annotated data output\n        feature_name = ANNOTATED_DATA_FEATURE_NAME\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected_indices = list(range(50, 100))\n\n        # check selected data output\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        n_sel, n_attr = len(selected), len(self.data.domain.attributes)\n        self.assertGreater(n_sel, 0)\n        self.assertEqual(selected.domain == self.data.domain,\n                         self.same_input_output_domain)\n        np.testing.assert_array_equal(selected.X[:, :n_attr],\n                                      self.data.X[selected_indices])\n\n        # check annotated data output\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(n_sel, np.sum([i[feature_name] for i in annotated]))\n\n        # compare selected and annotated data domains\n        self._compare_selected_annotated_domains(selected, annotated)\n\n        # check output when data is removed\n        self.send_signal(self.signal_name, None, 1)\n        self.send_signal(self.signal_name, None, 2)\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))\n\n    def test_multiple_input_over_cols(self):\n        self.widget.rowwise = False\n        selected_atr_name = 'Selected'\n        input2 = self.data.transform(Domain([self.data.domain.attributes[0]],\n                                            self.data.domain.class_vars,\n                                            self.data.domain.metas))\n        self.send_signal(self.signal_name, self.data, (1, 'Data', None))\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNone(selected)\n        self.assertEqual(len(annotated), len(self.data))\n        self.assertEqual(annotated.domain, self.data.domain)\n        for atr in annotated.domain.attributes:\n            self.assertFalse(atr.attributes)\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        np.testing.assert_array_equal(selected.X,\n                                      input2.X)\n        np.testing.assert_array_equal(selected.Y,\n                                      input2.Y)\n        np.testing.assert_array_equal(selected.metas,\n                                      input2.metas)\n\n        #domain matches but the values do not\n        input2.X = input2.X - 1\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        atrs = {atr.name for atr in selected.domain.attributes}\n        true_atrs = {'sepal length (2)', 'sepal length (1)'}\n        self.assertTrue(atrs == true_atrs)\n\n        out_domain = annotated.domain.attributes\n        self.assertTrue(out_domain[0].attributes[selected_atr_name])\n        self.assertTrue(out_domain[1].attributes[selected_atr_name])\n        self.assertFalse(out_domain[2].attributes[selected_atr_name])\n        self.assertFalse(out_domain[3].attributes[selected_atr_name])\n        self.assertFalse(out_domain[4].attributes[selected_atr_name])\n\n    def test_no_data(self):\n        \"\"\"Check that the widget doesn't crash on empty data\"\"\"\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[:100], 2)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n        self.send_signal(self.signal_name, self.data[:0], 3)\n\n    def test_unconditional_commit_on_new_signal(self):\n        with patch.object(self.widget, 'unconditional_commit') as commit:\n            self.widget.autocommit = False\n            commit.reset_mock()\n            self.send_signal(self.signal_name, self.data[:100], 1)\n            commit.assert_called()\n\n    def test_input_compatibility(self):\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name,\n                         self.data.transform(Domain([self.data.domain.attributes[0]],\n                                                    self.data.domain.class_vars,\n                                                    self.data.domain.metas)), 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n        self.widget.rowwise = False\n        self.send_signal(self.signal_name, self.data[:100, :], 2)\n        self.assertTrue(self.widget.Error.instances_mismatch.is_shown())\n\n        self.send_signal(self.signal_name, None, 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n    def test_rows_identifiers(self):\n        self.widget.rowwise = True\n        data = Table('zoo')\n        self.send_signal(self.signal_name, data, (1, 'Data', None))\n        self.widget.selected_feature = data.domain.metas[0]\n        self.send_signal(self.signal_name, data[:5], (2, 'Data', None))\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        self.assertEqual(len(selected), 5)\n        self.assertEqual(selected.domain.attributes, data.domain.attributes)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 100)\n\n    def test_too_many_inputs(self):\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name, self.data, 2)\n        self.send_signal(self.signal_name, self.data, 3)\n        self.send_signal(self.signal_name, self.data, 4)\n        self.send_signal(self.signal_name, self.data, 5)\n        self.send_signal(self.signal_name, self.data, 6)\n        self.assertTrue(self.widget.Error.too_many_inputs.is_shown())\n\n        self.send_signal(self.signal_name, None, 6)\n        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())\n\n    def test_no_attributes(self):\n        domain = Domain([], class_vars=self.data.domain.attributes)\n        n = len(self.data)\n        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)\n\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, table, 1)\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(out), len(table))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 21,
                                "end_lineno": 235,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "b7b003b2e84e9d7f6b454b6a95e9f40571075e06^",
    "new_commit_hash": "b7b003b2e84e9d7f6b454b6a95e9f40571075e06",
    "commit_message": "Merge pull request #4510 from AndrejaKovacic/venn\n\n[FiX] Venn diagram crashes on tables with no X",
    "commit_date": "2020-03-12T10:53:50+01:00",
    "metadata": {}
}
{
    "repo_name": "orange3",
    "new_commit_hash": "b7b003b2e84e9d7f6b454b6a95e9f40571075e06",
    "test_file_codes": [
        "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring\n\nimport unittest\nfrom unittest.mock import patch\nfrom copy import deepcopy\n\nimport numpy as np\n\nfrom Orange.data import (Table, Domain, StringVariable,\n                         ContinuousVariable)\nfrom Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin\nfrom Orange.widgets.utils.annotated_data import (ANNOTATED_DATA_FEATURE_NAME)\nfrom Orange.widgets.visualize.owvenndiagram import (\n                        OWVennDiagram,\n                        arrays_equal,\n                        pad_columns,\n                        get_perm)\n\n\nclass TestOWVennDiagram(WidgetTest, WidgetOutputsTestMixin):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        WidgetOutputsTestMixin.init(cls, False)\n\n        cls.signal_data = cls.data[:25]\n\n    def setUp(self):\n        self.widget = self.create_widget(OWVennDiagram)\n        self.signal_name = self.widget.Inputs.data\n\n    def _select_data(self):\n        self.widget.vennwidget.vennareas()[1].setSelected(True)\n        return list(range(len(self.signal_data)))\n\n    def test_rows_id(self):\n        data = Table('zoo')\n        data1 = deepcopy(data)\n        data1[:, 1] = 1\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, data1[:10], 1)\n        self.widget.selected_feature = None\n        self.send_signal(self.signal_name, data[5:10], 2)\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertTrue(self.widget.Warning.renamed_vars.is_shown())\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        sel_atrs = [atr.name for atr in selected.domain.attributes]\n        true_atrs = ['hair', 'feathers (1)', 'feathers (2)', 'eggs', 'milk', 'airborne', 'aquatic',\n                     'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs',\n                     'tail', 'domestic', 'catsize']\n        self.assertEqual(sel_atrs, true_atrs)\n        self.assertEqual(selected.domain.metas, data.domain.metas)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n    def test_output_duplicates(self):\n        self.widget.rowwise = True\n        self.widget.output_duplicates = True\n        self.send_signal(self.signal_name, self.data[:2], 1)\n        self.send_signal(self.signal_name, self.data[:4], 2)\n\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        in_ids = self.data[:2].ids\n        true_ids = np.array([in_ids[0], in_ids[0], in_ids[1], in_ids[1]])\n        np.testing.assert_array_equal(selected.ids.flatten(), true_ids)\n\n    def test_multiple_input_rows_id(self):\n        \"\"\"Over rows\"\"\"\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n\n        # check selected data output\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n\n        # check annotated data output\n        feature_name = ANNOTATED_DATA_FEATURE_NAME\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected_indices = list(range(50, 100))\n\n        # check selected data output\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        n_sel, n_attr = len(selected), len(self.data.domain.attributes)\n        self.assertGreater(n_sel, 0)\n        self.assertEqual(selected.domain == self.data.domain,\n                         self.same_input_output_domain)\n        np.testing.assert_array_equal(selected.X[:, :n_attr],\n                                      self.data.X[selected_indices])\n\n        # check annotated data output\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(n_sel, np.sum([i[feature_name] for i in annotated]))\n\n        # compare selected and annotated data domains\n        self._compare_selected_annotated_domains(selected, annotated)\n\n        # check output when data is removed\n        self.send_signal(self.signal_name, None, 1)\n        self.send_signal(self.signal_name, None, 2)\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))\n\n    def test_multiple_input_over_cols(self):\n        self.widget.rowwise = False\n        selected_atr_name = 'Selected'\n        input2 = self.data.transform(Domain([self.data.domain.attributes[0]],\n                                            self.data.domain.class_vars,\n                                            self.data.domain.metas))\n        self.send_signal(self.signal_name, self.data, (1, 'Data', None))\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertIsNone(selected)\n        self.assertEqual(len(annotated), len(self.data))\n        self.assertEqual(annotated.domain, self.data.domain)\n        for atr in annotated.domain.attributes:\n            self.assertFalse(atr.attributes)\n\n        # select data instances\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        np.testing.assert_array_equal(selected.X,\n                                      input2.X)\n        np.testing.assert_array_equal(selected.Y,\n                                      input2.Y)\n        np.testing.assert_array_equal(selected.metas,\n                                      input2.metas)\n\n        #domain matches but the values do not\n        input2.X = input2.X - 1\n        self.send_signal(self.signal_name, input2, (2, 'Data', None))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        atrs = {atr.name for atr in selected.domain.attributes}\n        true_atrs = {'sepal length (2)', 'sepal length (1)'}\n        self.assertTrue(atrs == true_atrs)\n\n        out_domain = annotated.domain.attributes\n        self.assertTrue(out_domain[0].attributes[selected_atr_name])\n        self.assertTrue(out_domain[1].attributes[selected_atr_name])\n        self.assertFalse(out_domain[2].attributes[selected_atr_name])\n        self.assertFalse(out_domain[3].attributes[selected_atr_name])\n        self.assertFalse(out_domain[4].attributes[selected_atr_name])\n\n    def test_no_data(self):\n        \"\"\"Check that the widget doesn't crash on empty data\"\"\"\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[:100], 2)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[:0], 1)\n        self.send_signal(self.signal_name, self.data[50:], 3)\n\n        for i in range(1, 4):\n            self.send_signal(self.signal_name, None, i)\n\n        self.send_signal(self.signal_name, self.data[:100], 1)\n        self.send_signal(self.signal_name, self.data[50:], 2)\n        self.send_signal(self.signal_name, self.data[:0], 3)\n\n    def test_unconditional_commit_on_new_signal(self):\n        with patch.object(self.widget, 'unconditional_commit') as commit:\n            self.widget.autocommit = False\n            commit.reset_mock()\n            self.send_signal(self.signal_name, self.data[:100], 1)\n            commit.assert_called()\n\n    def test_input_compatibility(self):\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name,\n                         self.data.transform(Domain([self.data.domain.attributes[0]],\n                                                    self.data.domain.class_vars,\n                                                    self.data.domain.metas)), 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n        self.widget.rowwise = False\n        self.send_signal(self.signal_name, self.data[:100, :], 2)\n        self.assertTrue(self.widget.Error.instances_mismatch.is_shown())\n\n        self.send_signal(self.signal_name, None, 2)\n        self.assertFalse(self.widget.Error.instances_mismatch.is_shown())\n\n    def test_rows_identifiers(self):\n        self.widget.rowwise = True\n        data = Table('zoo')\n        self.send_signal(self.signal_name, data, (1, 'Data', None))\n        self.widget.selected_feature = data.domain.metas[0]\n        self.send_signal(self.signal_name, data[:5], (2, 'Data', None))\n\n        self.assertIsNone(self.get_output(self.widget.Outputs.selected_data))\n        self.widget.vennwidget.vennareas()[3].setSelected(True)\n        selected = self.get_output(self.widget.Outputs.selected_data)\n        self.assertEqual(len(selected), 5)\n        self.assertEqual(selected.domain.attributes, data.domain.attributes)\n        self.assertEqual(selected.domain.class_vars, data.domain.class_vars)\n\n        annotated = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(annotated), 100)\n\n    def test_too_many_inputs(self):\n        self.send_signal(self.signal_name, self.data, 1)\n        self.send_signal(self.signal_name, self.data, 2)\n        self.send_signal(self.signal_name, self.data, 3)\n        self.send_signal(self.signal_name, self.data, 4)\n        self.send_signal(self.signal_name, self.data, 5)\n        self.send_signal(self.signal_name, self.data, 6)\n        self.assertTrue(self.widget.Error.too_many_inputs.is_shown())\n\n        self.send_signal(self.signal_name, None, 6)\n        self.assertFalse(self.widget.Error.too_many_inputs.is_shown())\n\n    def test_no_attributes(self):\n        domain = Domain([], class_vars=self.data.domain.attributes)\n        n = len(self.data)\n        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)\n\n        self.widget.rowwise = True\n        self.send_signal(self.signal_name, table, 1)\n        out = self.get_output(self.widget.Outputs.annotated_data)\n        self.assertEqual(len(out), len(table))\n\nclass TestVennUtilities(unittest.TestCase):\n\n    def test_array_equals_cols(self):\n        a = np.array([1, 2], dtype=np.float64)\n        b = np.array([1, np.nan], dtype=np.float64)\n        self.assertTrue(arrays_equal(None, None, None))\n        self.assertFalse(arrays_equal(None, a, None))\n        self.assertFalse(arrays_equal(a, None, None))\n        self.assertFalse(arrays_equal(a, b, ContinuousVariable))\n        a[1] = np.nan\n        self.assertTrue(arrays_equal(a, b, ContinuousVariable))\n        self.assertTrue(arrays_equal(a.astype(str), a.astype(str), StringVariable))\n        a[1] = 2\n        b[1] = 3\n        self.assertFalse(arrays_equal(a, b, ContinuousVariable))\n        self.assertFalse(arrays_equal(a.astype(str), b.astype(str), StringVariable))\n\n    def test_pad_columns(self):\n        l = 5\n        mask = [2, 3]\n        values = np.array([7.2, 77.3]).reshape(-1, 1)\n        res = pad_columns(values, mask, l)\n        true_res = np.array([np.nan, np.nan, 7.2, 77.3, np.nan]).reshape(-1, 1)\n        np.testing.assert_array_equal(res, true_res)\n\n    def test_get_perm(self):\n        all_ids = [1, 7, 22]\n        res = get_perm([7, 33], all_ids)\n        true_res = [1]\n        self.assertEqual(res, true_res)\n\n        res = get_perm([22, 1, 7], all_ids)\n        true_res = [2, 0, 1]\n        self.assertEqual(res, true_res)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Orange installation attempts...\nAttempting installation with Python 3.7...\nrunning build_ext\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpusspizwz/tmp\ncreating /tmp/tmpusspizwz/tmp/tmpusspizwz\ncompile options: '-MMD -MF /tmp/tmpusspizwz/file.c.d -c'\ncc: /tmp/tmpusspizwz/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpgjt5t05l/tmp\ncreating /tmp/tmpgjt5t05l/tmp/tmpgjt5t05l\ncompile options: '-MMD -MF /tmp/tmpgjt5t05l/file.c.d -c'\ncc: /tmp/tmpgjt5t05l/file.c\ncustomize UnixCCompiler using build_ext\nbuilding 'Orange.classification._simple_tree' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange\ncreating build/temp.linux-x86_64-3.7/Orange/classification\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_simple_tree.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_simple_tree.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.classification._tree_scorers' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_tree_scorers.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_tree_scorers.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._valuecount' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/data\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_valuecount.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_valuecount.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._contingency' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_contingency.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_contingency.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._io' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_io.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_io.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_io.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._variable' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_variable.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_variable.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.distance._distance' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/distance\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/distance/_distance.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/distance/_distance.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._discretize' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/preprocess\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/preprocess/_discretize.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_discretize.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._relieff' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/preprocess/_relieff.cpp\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_relieff.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.projection._som' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/projection\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/projection/_som.c\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/projection/_som.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.widgets.utils._grid_density' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/widgets\ncreating build/temp.linux-x86_64-3.7/Orange/widgets/utils\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/widgets/utils/_grid_density.cpp\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so\nrunning develop\nrunning build_scripts\nrunning egg_info\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\ncreating Orange3.egg-info\nwriting Orange3.egg-info/PKG-INFO\nwriting dependency_links to Orange3.egg-info/dependency_links.txt\nwriting entry points to Orange3.egg-info/entry_points.txt\nwriting requirements to Orange3.egg-info/requires.txt\nwriting top-level names to Orange3.egg-info/top_level.txt\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest template 'MANIFEST.in'\nwarning: no files found matching 'README*' under directory 'Orange'\nwarning: no files found matching 'LICENSE' under directory 'Orange'\nwarning: no files found matching '*.basket' under directory 'Orange/datasets'\nwarning: no files found matching '*.js' under directory 'Orange/widgets'\nwarning: no files found matching '*.css' under directory 'Orange/widgets'\nwarning: no files found matching '*.html' under directory 'Orange/widgets'\nwarning: no files found matching 'COPYING'\nadding license file 'LICENSE'\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nrunning build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpt8fp9x3v/tmp\ncreating /tmp/tmpt8fp9x3v/tmp/tmpt8fp9x3v\ncompile options: '-MMD -MF /tmp/tmpt8fp9x3v/file.c.d -c'\ncc: /tmp/tmpt8fp9x3v/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpyvbhid1s/tmp\ncreating /tmp/tmpyvbhid1s/tmp/tmpyvbhid1s\ncompile options: '-MMD -MF /tmp/tmpyvbhid1s/file.c.d -c'\ncc: /tmp/tmpyvbhid1s/file.c\ncustomize UnixCCompiler using build_ext\nCreating /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/Orange3.egg-link (link to .)\nAdding Orange3 3.25.0.dev0+b7b003b to easy-install.pth file\nInstalling orange-canvas script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nInstalled /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06\nProcessing dependencies for Orange3==3.25.0.dev0+b7b003b\nSearching for XlsxWriter==3.2.0\nBest match: XlsxWriter 3.2.0\nAdding XlsxWriter 3.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for xlrd==2.0.1\nBest match: xlrd 2.0.1\nAdding xlrd 2.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for setuptools==59.8.0\nBest match: setuptools 59.8.0\nAdding setuptools 59.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for serverfiles==0.3.1\nBest match: serverfiles 0.3.1\nAdding serverfiles 0.3.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for scipy==1.7.3\nBest match: scipy 1.7.3\nAdding scipy 1.7.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for scikit-learn==1.0.2\nBest match: scikit-learn 1.0.2\nAdding scikit-learn 1.0.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for requests==2.31.0\nBest match: requests 2.31.0\nAdding requests 2.31.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for PyYAML==6.0.1\nBest match: PyYAML 6.0.1\nAdding PyYAML 6.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for python-louvain==0.16\nBest match: python-louvain 0.16\nAdding python-louvain 0.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for pyqtgraph==0.10.0\nBest match: pyqtgraph 0.10.0\nAdding pyqtgraph 0.10.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for pip==24.0\nBest match: pip 24.0\nAdding pip 24.0 to easy-install.pth file\nInstalling pip script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling pip3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling pip3.10 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for pandas==1.3.5\nBest match: pandas 1.3.5\nAdding pandas 1.3.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for orange-widget-base==4.22.0\nBest match: orange-widget-base 4.22.0\nAdding orange-widget-base 4.22.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for orange-canvas-core==0.1.35\nBest match: orange-canvas-core 0.1.35\nAdding orange-canvas-core 0.1.35 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for openpyxl==3.1.3\nBest match: openpyxl 3.1.3\nAdding openpyxl 3.1.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for openTSNE==1.0.1\nBest match: openTSNE 1.0.1\nAdding openTSNE 1.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for numpy==1.17.5\nBest match: numpy 1.17.5\nAdding numpy 1.17.5 to easy-install.pth file\nInstalling f2py script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling f2py3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling f2py3.7 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for networkx==2.6.3\nBest match: networkx 2.6.3\nAdding networkx 2.6.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for matplotlib==3.5.3\nBest match: matplotlib 3.5.3\nAdding matplotlib 3.5.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for keyrings.alt==4.2.0\nBest match: keyrings.alt 4.2.0\nAdding keyrings.alt 4.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for keyring==24.1.1\nBest match: keyring 24.1.1\nAdding keyring 24.1.1 to easy-install.pth file\nInstalling keyring script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for joblib==1.3.2\nBest match: joblib 1.3.2\nAdding joblib 1.3.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for docutils==0.20.1\nBest match: docutils 0.20.1\nAdding docutils 0.20.1 to easy-install.pth file\nInstalling docutils script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for chardet==5.2.0\nBest match: chardet 5.2.0\nAdding chardet 5.2.0 to easy-install.pth file\nInstalling chardetect script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for Bottleneck==1.4.0\nBest match: Bottleneck 1.4.0\nAdding Bottleneck 1.4.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for baycomp==1.0.3\nBest match: baycomp 1.0.3\nAdding baycomp 1.0.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for AnyQt==0.2.0\nBest match: AnyQt 0.2.0\nAdding AnyQt 0.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for threadpoolctl==3.1.0\nBest match: threadpoolctl 3.1.0\nAdding threadpoolctl 3.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for urllib3==2.0.7\nBest match: urllib3 2.0.7\nAdding urllib3 2.0.7 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for certifi==2024.12.14\nBest match: certifi 2024.12.14\nAdding certifi 2024.12.14 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for charset-normalizer==3.4.1\nBest match: charset-normalizer 3.4.1\nAdding charset-normalizer 3.4.1 to easy-install.pth file\nInstalling normalizer script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for idna==3.10\nBest match: idna 3.10\nAdding idna 3.10 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for pytz==2024.2\nBest match: pytz 2024.2\nAdding pytz 2024.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for python-dateutil==2.9.0.post0\nBest match: python-dateutil 2.9.0.post0\nAdding python-dateutil 2.9.0.post0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for typing-extensions==4.7.1\nBest match: typing-extensions 4.7.1\nAdding typing-extensions 4.7.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for packaging==24.0\nBest match: packaging 24.0\nAdding packaging 24.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for importlib-metadata==6.7.0\nBest match: importlib-metadata 6.7.0\nAdding importlib-metadata 6.7.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for requests-cache==1.1.1\nBest match: requests-cache 1.1.1\nAdding requests-cache 1.1.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for commonmark==0.9.1\nBest match: commonmark 0.9.1\nAdding commonmark 0.9.1 to easy-install.pth file\nInstalling cmark script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for dictdiffer==0.9.0\nBest match: dictdiffer 0.9.0\nAdding dictdiffer 0.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for qasync==0.25.0\nBest match: qasync 0.25.0\nAdding qasync 0.25.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for et-xmlfile==1.1.0\nBest match: et-xmlfile 1.1.0\nAdding et-xmlfile 1.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for kiwisolver==1.4.5\nBest match: kiwisolver 1.4.5\nAdding kiwisolver 1.4.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for fonttools==4.38.0\nBest match: fonttools 4.38.0\nAdding fonttools 4.38.0 to easy-install.pth file\nInstalling fonttools script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling pyftmerge script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling pyftsubset script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\nInstalling ttx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for cycler==0.11.0\nBest match: cycler 0.11.0\nAdding cycler 0.11.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for pyparsing==3.1.4\nBest match: pyparsing 3.1.4\nAdding pyparsing 3.1.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for Pillow==9.5.0\nBest match: Pillow 9.5.0\nAdding Pillow 9.5.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for jaraco.classes==3.2.3\nBest match: jaraco.classes 3.2.3\nAdding jaraco.classes 3.2.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for importlib-resources==5.12.0\nBest match: importlib-resources 5.12.0\nAdding importlib-resources 5.12.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for SecretStorage==3.3.3\nBest match: SecretStorage 3.3.3\nAdding SecretStorage 3.3.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for jeepney==0.8.0\nBest match: jeepney 0.8.0\nAdding jeepney 0.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for six==1.17.0\nBest match: six 1.17.0\nAdding six 1.17.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for zipp==3.15.0\nBest match: zipp 3.15.0\nAdding zipp 3.15.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for url-normalize==1.4.3\nBest match: url-normalize 1.4.3\nAdding url-normalize 1.4.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for platformdirs==4.0.0\nBest match: platformdirs 4.0.0\nAdding platformdirs 4.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for cattrs==23.1.2\nBest match: cattrs 23.1.2\nAdding cattrs 23.1.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for attrs==24.2.0\nBest match: attrs 24.2.0\nAdding attrs 24.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for more-itertools==9.1.0\nBest match: more-itertools 9.1.0\nAdding more-itertools 9.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for cryptography==44.0.0\nBest match: cryptography 44.0.0\nAdding cryptography 44.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for exceptiongroup==1.2.2\nBest match: exceptiongroup 1.2.2\nAdding exceptiongroup 1.2.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for cffi==1.15.1\nBest match: cffi 1.15.1\nAdding cffi 1.15.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nSearching for pycparser==2.21\nBest match: pycparser 2.21\nAdding pycparser 2.21 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages\nFinished processing dependencies for Orange3==3.25.0.dev0+b7b003b\nVerifying Orange installation...\n✅ Orange installation successful!\nSuccessfully installed orange using Python 3.7\n",
    "setup_res_stderr": "Using CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 18 packages in 116ms\nPrepared 18 packages in 65ms\nInstalled 18 packages in 476ms\n + cython==0.29.37\n + exceptiongroup==1.2.2\n + importlib-metadata==6.7.0\n + iniconfig==2.0.0\n + numpy==1.17.5\n + packaging==24.0\n + pluggy==1.2.0\n + pyqt5==5.15.10\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.13.0\n + pyqtwebengine==5.15.6\n + pyqtwebengine-qt5==5.15.16\n + pytest==7.4.4\n + setuptools==59.8.0\n + tomli==2.0.1\n + typing-extensions==4.7.1\n + wheel==0.42.0\n + zipp==3.15.0\nResolved 48 packages in 364ms\nInstalled 42 packages in 425ms\n + baycomp==1.0.3\n + bottleneck==1.4.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + cryptography==44.0.0\n + cycler==0.11.0\n + et-xmlfile==1.1.0\n + fonttools==4.38.0\n + idna==3.10\n + importlib-resources==5.12.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + joblib==1.3.2\n + keyring==24.1.1\n + keyrings-alt==4.2.0\n + kiwisolver==1.4.5\n + matplotlib==3.5.3\n + more-itertools==9.1.0\n + networkx==2.6.3\n + openpyxl==3.1.3\n + opentsne==1.0.1\n + pandas==1.3.5\n + pillow==9.5.0\n + pip==24.0\n + pycparser==2.21\n + pyparsing==3.1.4\n + python-dateutil==2.9.0.post0\n + python-louvain==0.16\n + pytz==2024.2\n + pyyaml==6.0.1\n + requests==2.31.0\n + scikit-learn==1.0.2\n + scipy==1.7.3\n + secretstorage==3.3.3\n + serverfiles==0.3.1\n + six==1.17.0\n + threadpoolctl==3.1.0\n + urllib3==2.0.7\n + xlrd==2.0.1\n + xlsxwriter==3.2.0\nResolved 34 packages in 173ms\nInstalled 13 packages in 23ms\n + anyqt==0.2.0\n + attrs==24.2.0\n + cattrs==23.1.2\n + commonmark==0.9.1\n + dictdiffer==0.9.0\n + docutils==0.20.1\n + orange-canvas-core==0.1.35\n + orange-widget-base==4.22.0\n + platformdirs==4.0.0\n + pyqtgraph==0.10.0\n + qasync==0.25.0\n + requests-cache==1.1.1\n + url-normalize==1.4.3\nResolved 2 packages in 23ms\nInstalled 2 packages in 41ms\n + psycopg2==2.9.9\n + pymssql==2.3.2\nAudited 1 package in 1ms\nAudited 2 packages in 1ms\nOrange/classification/_simple_tree.c: In function ‘gain_ratio_c’:\nOrange/classification/_simple_tree.c:63:9: warning: implicit declaration of function ‘qsort_r’; did you mean ‘qsort’? [-Wimplicit-function-declaration]\n   63 |         qsort_r(base, nel, size, compar, thunk)\n      |         ^~~~~~~\nOrange/classification/_simple_tree.c:147:9: note: in expansion of macro ‘QSORT_R_FUNC’\n  147 |         QSORT_R_FUNC(examples, size, sizeof(struct Example), (void*) &compar_attr, compar_examples);\n      |         ^~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/classification/_tree_scorers.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_2find_threshold_entropy’:\nOrange/classification/_tree_scorers.c:3115:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 3115 |   __pyx_t_7 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_6find_threshold_MSE’:\nOrange/classification/_tree_scorers.c:4619:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 4619 |   __pyx_t_1 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_8find_binarization_MSE’:\nOrange/classification/_tree_scorers.c:5396:43: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5396 |           for (__pyx_t_22 = 0; __pyx_t_22 < __pyx_t_21; __pyx_t_22+=1) {\n      |                                           ^\nOrange/classification/_tree_scorers.c:5517:39: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5517 |           __pyx_t_27 = ((__pyx_v_left >= __pyx_v_min_leaf) != 0);\n      |                                       ^~\nOrange/classification/_tree_scorers.c:5523:53: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5523 |           __pyx_t_27 = (((__pyx_v_N - __pyx_v_left) >= __pyx_v_min_leaf) != 0);\n      |                                                     ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_14compute_predictions_csr’:\nOrange/classification/_tree_scorers.c:7051:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7051 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_16compute_predictions_csc’:\nOrange/classification/_tree_scorers.c:7741:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7741 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c:7801:143: warning: comparison of integer expressions of different signedness: ‘__pyx_t_5numpy_int32_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]\n 7801 |               __pyx_t_17 = (((*((__pyx_t_5numpy_int32_t *) ( /* dim=0 */ (__pyx_v_indices.data + __pyx_t_18 * __pyx_v_indices.strides[0]) ))) != __pyx_v_i) != 0);\n      |                                                                                                                                               ^~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_valuecount.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_contingency.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/data/_io.c: In function ‘__pyx_pf_6Orange_4data_3_io_sparse_read_float.constprop’:\nOrange/data/_io.c:3881:24: warning: ‘__pyx_v_decs’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3881 |           __pyx_v_decs = (__pyx_v_decs / 10.0);\n      |           ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:4070:24: warning: ‘__pyx_v_value’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4070 |           __pyx_t_10 = PyFloat_FromDouble(__pyx_v_value); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 221, __pyx_L5_error)\n      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:480:40: warning: ‘__pyx_v_attr_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n  480 |   #define PyInt_FromLong               PyLong_FromLong\n      |                                        ^~~~~~~~~~~~~~~\nOrange/data/_io.c:1301:7: note: ‘__pyx_v_attr_index’ was declared here\n 1301 |   int __pyx_v_attr_index;\n      |       ^~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:2935:25: warning: ‘__pyx_v_atomp’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2935 |           __pyx_v_atomp = (__pyx_v_atomp + 1);\n      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/distance/_distance.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_discretize.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_relieff.cpp:581:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/projection/_som.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_5update_hex’:\nOrange/projection/_som.c:3655:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3655 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3654:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3654 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_7get_winners_sparse’:\nOrange/projection/_som.c:4547:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4547 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_39, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4114:7: note: ‘__pyx_v_win_y’ was declared here\n 4114 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4536:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4536 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_36, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_37, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4113:7: note: ‘__pyx_v_win_x’ was declared here\n 4113 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_8update_sparse.constprop’:\nOrange/projection/_som.c:4746:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4746 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4745:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4745 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_3update’:\nOrange/projection/_som.c:3141:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3141 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_1get_winners’:\nOrange/projection/_som.c:2964:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2964 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2665:7: note: ‘__pyx_v_win_y’ was declared here\n 2665 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:2953:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2953 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2664:7: note: ‘__pyx_v_win_x’ was declared here\n 2664 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_10update_sparse_hex.constprop’:\nOrange/projection/_som.c:5515:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5515 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:5514:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5514 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n  setuptools.SetuptoolsDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/setuptools/command/easy_install.py:159: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n  EasyInstallDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n  setuptools.SetuptoolsDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06/.venv/lib/python3.7/site-packages/setuptools/command/egg_info.py:628: SetuptoolsDeprecationWarning: Custom 'build_py' does not implement 'get_data_files_without_manifest'.\nPlease extend command classes from setuptools instead of distutils.\n  SetuptoolsDeprecationWarning\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06\ncollected 20 items\n\nr2e_tests/test_1.py sss..F..............                                 [100%]\n\n=================================== FAILURES ===================================\n_____________________ TestOWVennDiagram.test_minimum_size ______________________\n\nself = <r2e_tests.test_1.TestOWVennDiagram testMethod=test_minimum_size>\n\n    def test_minimum_size(self):\n        widget = getattr(self, \"widget\", None)\n        if widget is None:\n            self.skipTest(\"minimum size not tested as .widget was not set\")\n>       self.check_minimum_size(widget)\n\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:242: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:615: in check_minimum_size\n    self.assertLess(min_size.width(), 800)\nE   AssertionError: 898 not less than 800\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_image_export\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_input_compatibility\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_multiple_input_over_cols\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_multiple_input_rows_id\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_no_attributes\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_no_data\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_output_duplicates\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_outputs\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_rows_id\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_rows_identifiers\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_too_many_inputs\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_unconditional_commit_on_new_signal\nPASSED r2e_tests/test_1.py::TestVennUtilities::test_array_equals_cols\nPASSED r2e_tests/test_1.py::TestVennUtilities::test_get_perm\nPASSED r2e_tests/test_1.py::TestVennUtilities::test_pad_columns\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\nFAILED r2e_tests/test_1.py::TestOWVennDiagram::test_minimum_size - AssertionE...\n=================== 1 failed, 16 passed, 3 skipped in 5.28s ====================\nWidgets left: 0    Max widgets: 868 \n\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_b7b003b2e84e9d7f6b454b6a95e9f40571075e06\ncollected 20 items\n\nr2e_tests/test_1.py sss..F...F..........                                 [100%]\n\n=================================== FAILURES ===================================\n_____________________ TestOWVennDiagram.test_minimum_size ______________________\n\nself = <r2e_tests.test_1.TestOWVennDiagram testMethod=test_minimum_size>\n\n    def test_minimum_size(self):\n        widget = getattr(self, \"widget\", None)\n        if widget is None:\n            self.skipTest(\"minimum size not tested as .widget was not set\")\n>       self.check_minimum_size(widget)\n\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:242: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:615: in check_minimum_size\n    self.assertLess(min_size.width(), 800)\nE   AssertionError: 898 not less than 800\n_____________________ TestOWVennDiagram.test_no_attributes _____________________\n\nself = <r2e_tests.test_1.TestOWVennDiagram testMethod=test_no_attributes>\n\n    def test_no_attributes(self):\n        domain = Domain([], class_vars=self.data.domain.attributes)\n        n = len(self.data)\n        table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)\n    \n        self.widget.rowwise = True\n>       self.send_signal(self.signal_name, table, 1)\n\nr2e_tests/test_1.py:233: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:446: in send_signal\n    widget=widget, wait=wait)\n.venv/lib/python3.7/site-packages/orangewidget/tests/base.py:472: in send_signals\n    widget.handleNewSignals()\nOrange/widgets/visualize/owvenndiagram.py:207: in handleNewSignals\n    self._createDiagram()\nOrange/widgets/visualize/owvenndiagram.py:320: in _createDiagram\n    self._on_selectionChanged()\nOrange/widgets/visualize/owvenndiagram.py:343: in _on_selectionChanged\n    self.invalidateOutput()\nOrange/widgets/visualize/owvenndiagram.py:364: in invalidateOutput\n    self.commit()\n.venv/lib/python3.7/site-packages/orangewidget/gui.py:1999: in conditional_commit\n    do_commit()\n.venv/lib/python3.7/site-packages/orangewidget/gui.py:2009: in do_commit\n    commit()\nOrange/widgets/visualize/owvenndiagram.py:671: in commit\n    annotated = self.create_from_rows(annotated_ids, True)\nOrange/widgets/visualize/owvenndiagram.py:600: in create_from_rows\n    return self.extract_rowwise(var_dict, relevant_ids, selection)\nOrange/widgets/visualize/owvenndiagram.py:555: in extract_rowwise\n    table = self.merge_data(domain, values, idas)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <Orange.widgets.visualize.owvenndiagram.OWVennDiagram object at 0x76af1c1a39b0>\ndomain = defaultdict(<class 'list'>, {'class_vars': [ContinuousVariable(name='sepal length', number_of_decimals=1), ContinuousV...ousVariable(name='petal length', number_of_decimals=1), ContinuousVariable(name='petal width', number_of_decimals=1)]})\nvalues = defaultdict(<class 'list'>, {'class_vars': [array([[5.1],\n       [4.9],\n       [4.7],\n       [4.6],\n       [5. ],\n    ...3],\n       [1.9],\n       [2.3],\n       [2.5],\n       [2.3],\n       [1.9],\n       [2. ],\n       [2.3],\n       [1.8]])]})\nids = array([2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210,\n       2211, 2212, 2213, 2214, 2215, 2216, 22...    2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342,\n       2343, 2344, 2345, 2346, 2347, 2348, 2349])\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n>       table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\nE       TypeError: __init__() missing 1 required positional argument: 'attributes'\n\nOrange/widgets/visualize/owvenndiagram.py:384: TypeError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_image_export\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_input_compatibility\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_multiple_input_over_cols\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_multiple_input_rows_id\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_no_data\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_output_duplicates\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_outputs\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_rows_id\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_rows_identifiers\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_too_many_inputs\nPASSED r2e_tests/test_1.py::TestOWVennDiagram::test_unconditional_commit_on_new_signal\nPASSED r2e_tests/test_1.py::TestVennUtilities::test_array_equals_cols\nPASSED r2e_tests/test_1.py::TestVennUtilities::test_get_perm\nPASSED r2e_tests/test_1.py::TestVennUtilities::test_pad_columns\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\nFAILED r2e_tests/test_1.py::TestOWVennDiagram::test_minimum_size - AssertionE...\nFAILED r2e_tests/test_1.py::TestOWVennDiagram::test_no_attributes - TypeError...\n=================== 2 failed, 15 passed, 3 skipped in 3.85s ====================\nWidgets left: 0    Max widgets: 868 \n\n",
    "old_commit_res_stderr": ""
}
Orange/widgets/visualize/owvenndiagram.py Orange/widgets/visualize/tests/test_owvenndiagram.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** Venn Diagram Crashes When Handling Tables Without Attributes

**Description:**
When using the Venn Diagram widget with a table that has no attributes, the application crashes unexpectedly.

**Example Buggy Code:**
```python
def test_no_attributes(self):
    domain = Domain([], class_vars=self.data.domain.attributes)
    n = len(self.data)
    table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)
    self.widget.rowwise = True
    self.send_signal(self.signal_name, table, 1)
```

In this test scenario, a table is created with no attribute columns. When this table is sent to the Venn Diagram widget, it fails to handle the absence of attributes properly.

**Expected Behavior:**
The Venn Diagram widget should handle tables without attributes gracefully without crashing. It should either display an empty diagram or provide a meaningful message indicating that there are no attributes to display.

**Actual Behavior:**
The application crashes with the following error:
```
TypeError: __init__() missing 1 required positional argument: 'attributes'
```
This indicates that the widget does not properly initialize when there are no attributes in the input table.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/visualize/owvenndiagram.py
"""
Venn Diagram Widget
-------------------

"""

import math
import unicodedata
from collections import namedtuple, defaultdict
from itertools import compress, count
from functools import reduce
from operator import attrgetter
from xml.sax.saxutils import escape

import numpy as np

from AnyQt.QtWidgets import (
    QGraphicsScene, QGraphicsView, QGraphicsWidget,
    QGraphicsPathItem, QGraphicsTextItem, QStyle, QSizePolicy
)
from AnyQt.QtGui import (
    QPainterPath, QPainter, QTransform, QColor, QBrush, QPen, QPalette
)
from AnyQt.QtCore import Qt, QPointF, QRectF, QLineF
from AnyQt.QtCore import pyqtSignal as Signal

from Orange.data import Table, Domain, StringVariable, RowInstance
from Orange.data.util import get_unique_names_duplicates
from Orange.widgets import widget, gui, settings
from Orange.widgets.utils import itemmodels, colorpalettes
from Orange.widgets.utils.annotated_data import (create_annotated_table,
                                                 ANNOTATED_DATA_SIGNAL_NAME)
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output, Msg


_InputData = namedtuple("_InputData", ["key", "name", "table"])
_ItemSet = namedtuple("_ItemSet", ["key", "name", "title", "items"])


class OWVennDiagram(widget.OWWidget):
    name = "Venn Diagram"
    description = "A graphical visualization of the overlap of data instances " \
                  "from a collection of input datasets."
    icon = "icons/VennDiagram.svg"
    priority = 280
    keywords = []
    settings_version = 2

    class Inputs:
        data = Input("Data", Table, multiple=True)

    class Outputs:
        selected_data = Output("Selected Data", Table, default=True)
        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)

    class Error(widget.OWWidget.Error):
        instances_mismatch = Msg("Data sets do not contain the same instances.")
        too_many_inputs = Msg("Venn diagram accepts at most five datasets.")

    class Warning(widget.OWWidget.Warning):
        renamed_vars = Msg("Some variables have been renamed "
                           "to avoid duplicates.\n{}")

    selection: list

    settingsHandler = settings.DomainContextHandler()
    # Indices of selected disjoint areas
    selection = settings.Setting([], schema_only=True)
    #: Output unique items (one output row for every unique instance `key`)
    #: or preserve all duplicates in the output.
    output_duplicates = settings.Setting(False)
    autocommit = settings.Setting(True)
    rowwise = settings.Setting(True)
    selected_feature = settings.ContextSetting(None)

    want_control_area = False
    graph_name = "scene"
    atr_types = ['attributes', 'metas', 'class_vars']
    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}
    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}

    def __init__(self):
        super().__init__()

        # Diagram update is in progress
        self._updating = False
        # Input update is in progress
        self._inputUpdate = False
        # Input datasets in the order they were 'connected'.
        self.data = {}
        # Extracted input item sets in the order they were 'connected'
        self.itemsets = {}
        # A list with 2 ** len(self.data) elements that store item sets
        # belonging to each area
        self.disjoint = []
        # A list with  2 ** len(self.data) elements that store keys of tables
        # intersected in each area
        self.area_keys = []

        # Main area view
        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)
        self.view.setRenderHint(QPainter.Antialiasing)
        self.view.setBackgroundRole(QPalette.Window)
        self.view.setFrameStyle(QGraphicsView.StyledPanel)

        self.mainArea.layout().addWidget(self.view)
        self.vennwidget = VennDiagram()
        self._resize()
        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)
        self.scene.selectionChanged.connect(self._on_selectionChanged)

        self.scene.addItem(self.vennwidget)

        controls = gui.hBox(self.mainArea)
        box = gui.radioButtonsInBox(
            controls, self, 'rowwise',
            ["Columns (features)", "Rows (instances), matched by", ],
            box="Elements", callback=self._on_matching_changed
        )
        gui.comboBox(
            gui.indentedBox(box), self, "selected_feature",
            model=itemmodels.VariableListModel(placeholder="Instance identity"),
            callback=self._on_inputAttrActivated
            )
        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)

        self.outputs_box = box = gui.vBox(controls, "Output")
        self.output_duplicates_cb = gui.checkBox(
            box, self, "output_duplicates", "Output duplicates",
            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda
        gui.auto_send(box, self, "autocommit", box=False)
        self.output_duplicates_cb.setEnabled(bool(self.rowwise))
        self._queue = []

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self._resize()

    def showEvent(self, event):
        super().showEvent(event)
        self._resize()

    def _resize(self):
        # vennwidget draws so that the diagram fits into its geometry,
        # while labels take further 120 pixels, hence -120 in below formula
        size = max(200, min(self.view.width(), self.view.height()) - 120)
        self.vennwidget.resize(size, size)
        self.scene.setSceneRect(self.scene.itemsBoundingRect())

    @Inputs.data
    @check_sql_input
    def setData(self, data, key=None):
        self.Error.too_many_inputs.clear()
        if not self._inputUpdate:
            self._inputUpdate = True
        if key in self.data:
            if data is None:
                # Remove the input
                # Clear possible warnings.
                self.Warning.clear()
                del self.data[key]
            else:
                # Update existing item
                self.data[key] = self.data[key]._replace(name=data.name, table=data)

        elif data is not None:
            # TODO: Allow setting more them 5 inputs and let the user
            # select the 5 to display.
            if len(self.data) == 5:
                self.Error.too_many_inputs()
                return
            # Add a new input
            self.data[key] = _InputData(key, data.name, data)
        self._setInterAttributes()

    def data_equality(self):
        """ Checks if all input datasets have same ids. """
        if not self.data.values():
            return True
        sets = []
        for val in self.data.values():
            sets.append(set(val.table.ids))
        inter = reduce(set.intersection, sets)
        return len(inter) == max(map(len, sets))

    def settings_compatible(self):
        self.Error.instances_mismatch.clear()
        if not self.rowwise:
            if not self.data_equality():
                self.vennwidget.clear()
                self.Error.instances_mismatch()
                self.itemsets = {}
                return False
        return True

    def handleNewSignals(self):
        self._inputUpdate = False
        self.vennwidget.clear()
        if not self.settings_compatible():
            self.invalidateOutput()
            return

        self._createItemsets()
        self._createDiagram()
        # If autocommit is enabled, _createDiagram already outputs data
        # If not, call unconditional_commit from here
        if not self.autocommit:
            self.unconditional_commit()

        self._updateInfo()
        super().handleNewSignals()

    def intersectionStringAttrs(self):
        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]
        if sets:
            return reduce(set.intersection, sets)
        return set()

    def _setInterAttributes(self):
        model = self.controls.selected_feature.model()
        model[:] = [None] + list(self.intersectionStringAttrs())
        if self.selected_feature:
            names = (var.name for var in model if var)
            if self.selected_feature.name not in names:
                self.selected_feature = model[0]

    def _itemsForInput(self, key):
        """
        Calculates input for venn diagram, according to user's settings.
        """
        table = self.data[key].table
        attr = self.selected_feature
        if attr:
            return [str(inst[attr]) for inst in table
                    if not np.isnan(inst[attr])]
        else:
            return list(table.ids)

    def _createItemsets(self):
        """
        Create itemsets over rows or columns (domains) of input tables.
        """
        olditemsets = dict(self.itemsets)
        self.itemsets.clear()

        for key, input_ in self.data.items():
            if self.rowwise:
                items = self._itemsForInput(key)
            else:
                items = [el.name for el in input_.table.domain.attributes]
            name = input_.name
            if key in olditemsets and olditemsets[key].name == name:
                # Reuse the title (which might have been changed by the user)
                title = olditemsets[key].title
            else:
                title = name

            itemset = _ItemSet(key=key, name=name, title=title, items=items)
            self.itemsets[key] = itemset

    def _createDiagram(self):
        self._updating = True

        oldselection = list(self.selection)

        n = len(self.itemsets)
        self.disjoint, self.area_keys = \
            self.get_disjoint(set(s.items) for s in self.itemsets.values())

        vennitems = []
        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)

        for i, item in enumerate(self.itemsets.values()):
            cnt = len(set(item.items))
            cnt_all = len(item.items)
            if cnt != cnt_all:
                fmt = '{} <i>(all: {})</i>'
            else:
                fmt = '{}'
            counts = fmt.format(cnt, cnt_all)
            gr = VennSetItem(text=item.title, informativeText=counts)
            color = colors[i]
            color.setAlpha(100)
            gr.setBrush(QBrush(color))
            gr.setPen(QPen(Qt.NoPen))
            vennitems.append(gr)

        self.vennwidget.setItems(vennitems)

        for i, area in enumerate(self.vennwidget.vennareas()):
            area_items = list(map(str, list(self.disjoint[i])))
            if i:
                area.setText("{0}".format(len(area_items)))

            label = disjoint_set_label(i, n, simplify=False)
            head = "<h4>|{}| = {}</h4>".format(label, len(area_items))
            if len(area_items) > 32:
                items_str = ", ".join(map(escape, area_items[:32]))
                hidden = len(area_items) - 32
                tooltip = ("{}<span>{}, ...</br>({} items not shown)<span>"
                           .format(head, items_str, hidden))
            elif area_items:
                tooltip = "{}<span>{}</span>".format(
                    head,
                    ", ".join(map(escape, area_items))
                )
            else:
                tooltip = head

            area.setToolTip(tooltip)

            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))
            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)
            area.setSelected(i in oldselection)

        self._updating = False
        self._on_selectionChanged()

    def _updateInfo(self):
        # Clear all warnings
        self.warning()

        if self.selected_feature is None:
            no_idx = ["#{}".format(i + 1)
                      for i, key in enumerate(self.data)
                      if not source_attributes(self.data[key].table.domain)]
            if len(no_idx) == 1:
                self.warning("Dataset {} has no suitable identifiers."
                             .format(no_idx[0]))
            elif len(no_idx) > 1:
                self.warning("Datasets {} and {} have no suitable identifiers."
                             .format(", ".join(no_idx[:-1]), no_idx[-1]))

    def _on_selectionChanged(self):
        if self._updating:
            return

        areas = self.vennwidget.vennareas()
        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]
        self.invalidateOutput()

    def _on_matching_changed(self):
        self.output_duplicates_cb.setEnabled(bool(self.rowwise))
        if not self.settings_compatible():
            self.invalidateOutput()
            return
        self._createItemsets()
        self._createDiagram()
        self._updateInfo()

    def _on_inputAttrActivated(self):
        self.rowwise = 1
        self._on_matching_changed()

    def _on_itemTextEdited(self, index, text):
        text = str(text)
        key = list(self.itemsets)[index]
        self.itemsets[key] = self.itemsets[key]._replace(title=text)

    def invalidateOutput(self):
        self.commit()

    def merge_data(self, domain, values, ids=None):
        X, metas, class_vars = None, None, None
        renamed = []
        for val in domain.values():
            names = [var.name for var in val]
            unique_names = get_unique_names_duplicates(names)
            for n, u, idx, var in zip(names, unique_names, count(), val):
                if n != u:
                    val[idx] = var.copy(name=u)
                    renamed.append(n)
        if renamed:
            self.Warning.renamed_vars(', '.join(renamed))
        if 'attributes' in values:
            X = np.hstack(values['attributes'])
        if 'metas' in values:
            metas = np.hstack(values['metas'])
        if 'class_vars' in values:
            class_vars = np.hstack(values['class_vars'])
        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)
        if ids is not None:
            table.ids = ids
        return table

    def extract_columnwise(self, var_dict, columns=None):
        #for columns
        domain = defaultdict(list)
        values = defaultdict(list)
        renamed = []
        for atr_type, vars_dict in var_dict.items():
            for var_name, var_data in vars_dict.items():
                is_selected = bool(columns) and var_name.name in columns
                if var_data[0]:
                    #columns are different, copy all, rename them
                    for var, table_key in var_data[1]:
                        idx = list(self.data).index(table_key) + 1
                        new_atr = var.copy(name=f'{var_name.name} ({idx})')
                        if columns and atr_type == 'attributes':
                            new_atr.attributes['Selected'] = is_selected
                        domain[atr_type].append(new_atr)
                        renamed.append(var_name.name)
                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],
                                                        self.atr_vals[atr_type])
                                                .reshape(-1, 1))
                else:
                    new_atr = var_data[1][0][0].copy()
                    if columns and atr_type == 'attributes':
                        new_atr.attributes['Selected'] = is_selected
                    domain[atr_type].append(new_atr)
                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],
                                                    self.atr_vals[atr_type])
                                            .reshape(-1, 1))
        if renamed:
            self.Warning.renamed_vars(', '.join(renamed))
        return self.merge_data(domain, values)

    def curry_merge(self, table_key, atr_type, ids=None, selection=False):
        if self.rowwise:
            check_equality = self.arrays_equal_rows
        else:
            check_equality = self.arrays_equal_cols

        def inner(new_atrs, atr):
            """
            Atrs - list of variables we wish to merge
            new_atrs - dictionary where key is old var, val
                is [is_different:bool, table_keys:list]), is_different is set to True,
                if we are outputing duplicates, but the value is arbitrary
            """
            if atr in new_atrs:
                if not selection and self.output_duplicates:
                    #if output_duplicates, we just check if compute value is the same
                    new_atrs[atr][0] = True
                elif not new_atrs[atr][0]:
                    for var, key in new_atrs[atr][1]:
                        if not check_equality(table_key,
                                              key,
                                              atr.name,
                                              self.atr_vals[atr_type],
                                              type(var), ids):
                            new_atrs[atr][0] = True
                            break
                new_atrs[atr][1].append((atr, table_key))
            else:
                new_atrs[atr] = [False, [(atr, table_key)]]
            return new_atrs
        return inner

    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):
        #gets masks, compares same as cols
        t1 = self.data[key1].table
        t2 = self.data[key2].table
        inter_val = set(ids[key1]) & set(ids[key2])
        t1_inter = [ids[key1][val] for val in inter_val]
        t2_inter = [ids[key2][val] for val in inter_val]
        return arrays_equal(
            getattr(t1[t1_inter, name],
                    data_type).reshape(-1, 1),
            getattr(t2[t2_inter, name],
                    data_type).reshape(-1, 1),
            type_)

    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):
        return arrays_equal(
            getattr(self.data[key1].table[:, name],
                    data_type),
            getattr(self.data[key2].table[:, name],
                    data_type),
            type_)

    def create_from_columns(self, columns, relevant_keys, get_selected):
        """
        Columns are duplicated only if values differ (even
        if only in order of values), origin table name and input slot is added to column name.
        """
        var_dict = {}
        for atr_type in self.atr_types:
            container = {}
            for table_key in relevant_keys:
                table = self.data[table_key].table
                if atr_type == 'attributes':
                    if get_selected:
                        atrs = list(compress(table.domain.attributes,
                                             [c.name in columns for c in table.domain.attributes]))
                    else:
                        atrs = getattr(table.domain, atr_type)
                else:
                    atrs = getattr(table.domain, atr_type)
                merge_vars = self.curry_merge(table_key, atr_type)
                container = reduce(merge_vars, atrs, container)
            var_dict[atr_type] = container

        if get_selected:
            annotated = self.extract_columnwise(var_dict, None)
        else:
            annotated = self.extract_columnwise(var_dict, columns)

        return annotated

    def extract_rowwise(self, var_dict, ids=None, selection=False):
        """
        keys : ['attributes', 'metas', 'class_vars']
        vals: new_atrs - dictionary where key is old name, val
            is [is_different:bool, table_keys:list])
        ids: dict with ids for each table
        """
        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))

        permutations = {}
        for table_key, dict_ in ids.items():
            permutations[table_key] = get_perm(list(dict_), all_ids)

        domain = defaultdict(list)
        values = defaultdict(list)
        renamed = []
        for atr_type, vars_dict in var_dict.items():
            for var_name, var_data in vars_dict.items():
                different = var_data[0]
                if different:
                    # Columns are different, copy and rename them.
                    # Renaming is done here to mark appropriately the source table.
                    # Additional strange clashes are checked later in merge_data
                    for var, table_key in var_data[1]:
                        temp = self.data[table_key].table
                        idx = list(self.data).index(table_key) + 1
                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))
                        renamed.append(var_name.name)
                        v = getattr(temp[list(ids[table_key].values()), var_name],
                                    self.atr_vals[atr_type])
                        perm = permutations[table_key]
                        if len(v) < len(all_ids):
                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))
                        else:
                            values[atr_type].append(v[perm].reshape(-1, 1))
                else:
                    value = np.full((len(all_ids), 1), np.nan)
                    domain[atr_type].append(var_data[1][0][0].copy())
                    for _, table_key in var_data[1]:
                        #different tables have different part of the same attribute vector
                        perm = permutations[table_key]
                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),
                                                               var_name],
                                    self.atr_vals[atr_type]).reshape(-1, 1)
                        value = value.astype(v.dtype, copy=False)
                        value[perm] = v
                    values[atr_type].append(value)

        if renamed:
            self.Warning.renamed_vars(', '.join(renamed))
        idas = None if self.selected_feature else np.array(all_ids)
        table = self.merge_data(domain, values, idas)
        if selection:
            mask = [idx in self.selected_items for idx in all_ids]
            return create_annotated_table(table, mask)
        return table

    def get_indices(self, table, selection):
        """Returns mappings of ids (be it row id or string) to indices in tables"""
        if self.selected_feature:
            if self.output_duplicates and selection:
                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),
                                           return_inverse=True)
                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]
            else:
                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),
                                       return_index=True)

        else:
            items = table.ids
            ids = range(len(table))

        if selection:
            return {item: idx for item, idx in zip(items, ids)
                    if item in self.selected_items}

        return dict(zip(items, ids))

    def get_indices_to_match_by(self, relevant_keys, selection=False):
        dict_ = {}
        for key in relevant_keys:
            table = self.data[key].table
            dict_[key] = self.get_indices(table, selection)
        return dict_

    def create_from_rows(self, relevant_ids, selection=False):
        var_dict = {}
        for atr_type in self.atr_types:
            container = {}
            for table_key in relevant_ids:
                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)
                atrs = getattr(self.data[table_key].table.domain, atr_type)
                container = reduce(merge_vars, atrs, container)
            var_dict[atr_type] = container
        if self.output_duplicates and not selection:
            return self.extract_rowwise_duplicates(var_dict, relevant_ids)
        return self.extract_rowwise(var_dict, relevant_ids, selection)

    def expand_table(self, table, atrs, metas, cv):
        exp = []
        n = 1 if isinstance(table, RowInstance) else len(table)
        if isinstance(table, RowInstance):
            ids = table.id.reshape(-1, 1)
            atr_vals = self.row_vals
        else:
            ids = table.ids.reshape(-1, 1)
            atr_vals = self.atr_vals
        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):
            cur_el = getattr(table.domain, atr_type)
            array = np.full((n, len(all_el)), np.nan)
            if cur_el:
                perm = get_perm(cur_el, all_el)
                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))
                array = array.astype(b.dtype, copy=False)
                array[:, perm] = b
            exp.append(array)
        return (*exp, ids)

    def extract_rowwise_duplicates(self, var_dict, ids):
        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))
        sort_key = attrgetter("name")
        all_atrs = sorted(var_dict['attributes'], key=sort_key)
        all_metas = sorted(var_dict['metas'], key=sort_key)
        all_cv = sorted(var_dict['class_vars'], key=sort_key)

        all_x, all_y, all_m = [], [], []
        new_table_ids = []
        for idx in all_ids:
            #iterate trough tables with same idx
            for table_key, t_indices in ids.items():
                if idx not in t_indices:
                    continue
                map_ = t_indices[idx]
                extracted = self.data[table_key].table[map_]
                # pylint: disable=unbalanced-tuple-unpacking
                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)
                all_x.append(x)
                all_y.append(y)
                all_m.append(m)
                new_table_ids.append(t_ids)
        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}
        values = {'attributes': [np.vstack(all_x)],
                  'metas': [np.vstack(all_m)],
                  'class_vars': [np.vstack(all_y)]}
        return self.merge_data(domain, values, np.vstack(new_table_ids))

    def commit(self):
        if not self.vennwidget.vennareas() or not self.data:
            self.Outputs.selected_data.send(None)
            self.Outputs.annotated_data.send(None)
            return

        self.selected_items = reduce(
            set.union, [self.disjoint[index] for index in self.selection],
            set()
        )
        selected_keys = reduce(
            set.union, [set(self.area_keys[area]) for area in self.selection],
            set())
        selected = None

        if self.rowwise:
            if self.selected_items:
                selected_ids = self.get_indices_to_match_by(
                    selected_keys, bool(self.selection))
                selected = self.create_from_rows(selected_ids, False)
            annotated_ids = self.get_indices_to_match_by(self.data)
            annotated = self.create_from_rows(annotated_ids, True)
        else:
            annotated = self.create_from_columns(self.selected_items, self.data, False)
            if self.selected_items:
                selected = self.create_from_columns(self.selected_items, selected_keys, True)

        self.Outputs.selected_data.send(selected)
        self.Outputs.annotated_data.send(annotated)

    def send_report(self):
        self.report_plot()

    def get_disjoint(self, sets):
        """
        Return all disjoint subsets.
        """
        sets = list(sets)
        n = len(sets)
        disjoint_sets = [None] * (2 ** n)
        included_tables = [None] * (2 ** n)
        for i in range(2 ** n):
            key = setkey(i, n)
            included = [s for s, inc in zip(sets, key) if inc]
            if included:
                excluded = [s for s, inc in zip(sets, key) if not inc]
                s = reduce(set.intersection, included)
                s = reduce(set.difference, excluded, s)
            else:
                s = set()
            disjoint_sets[i] = s
            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]

        return disjoint_sets, included_tables

def string_attributes(domain):
    """
    Return all string attributes from the domain.
    """
    return [attr for attr in domain.variables + domain.metas if attr.is_string]

def source_attributes(domain):
    """
    Return all suitable attributes for the venn diagram.
    """
    return string_attributes(domain)  # + discrete_attributes(domain)


def disjoint_set_label(i, n, simplify=False):
    """
    Return a html formated label for a disjoint set indexed by `i`.
    """
    intersection = unicodedata.lookup("INTERSECTION")
    # comp = unicodedata.lookup("COMPLEMENT")  #
    # This depends on the font but the unicode complement in
    # general does not look nice in a super script so we use
    # plain c instead.
    comp = "c"

    def label_for_index(i):
        return chr(ord("A") + i)

    if simplify:
        return "".join(label_for_index(i) for i, b in enumerate(setkey(i, n))
                       if b)
    else:
        return intersection.join(label_for_index(i) +
                                 ("" if b else "<sup>" + comp + "</sup>")
                                 for i, b in enumerate(setkey(i, n)))


class VennSetItem(QGraphicsPathItem):
    def __init__(self, parent=None, text="", informativeText=""):
        super(VennSetItem, self).__init__(parent)
        # Plain text title (editable by the VennDiagram)
        self.text = text
        # Extra informative text (possibly rich text)
        self.informativeText = informativeText

# TODO: Use palette's selected/highligted text / background colors to
# indicate selection


class VennIntersectionArea(QGraphicsPathItem):
    def __init__(self, parent=None, text=""):
        super().__init__(parent)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))

        self.text = QGraphicsTextItem(self)
        layout = self.text.document().documentLayout()
        layout.documentSizeChanged.connect(self._onLayoutChanged)

        self._text = text
        self._anchor = QPointF()

    def setText(self, text):
        if self._text != text:
            self._text = text
            self.text.setPlainText(text)

    def setTextAnchor(self, pos):
        if self._anchor != pos:
            self._anchor = pos
            self._updateTextAnchor()

    def hoverEnterEvent(self, event):
        self.setZValue(self.zValue() + 1)
        return QGraphicsPathItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.setZValue(self.zValue() - 1)
        return QGraphicsPathItem.hoverLeaveEvent(self, event)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            if event.modifiers() & Qt.AltModifier:
                self.setSelected(False)
            elif event.modifiers() & Qt.ControlModifier:
                self.setSelected(not self.isSelected())
            elif event.modifiers() & Qt.ShiftModifier:
                self.setSelected(True)
            else:
                for area in self.parentWidget().vennareas():
                    area.setSelected(False)
                self.setSelected(True)

    def mouseReleaseEvent(self, event):
        pass

    def paint(self, painter, option, _widget=None):
        painter.save()
        path = self.path()
        brush = QBrush(self.brush())
        pen = QPen(self.pen())

        if option.state & QStyle.State_Selected:
            pen.setColor(Qt.red)
            brush.setStyle(Qt.DiagCrossPattern)
            brush.setColor(QColor(40, 40, 40, 100))

        elif option.state & QStyle.State_MouseOver:
            pen.setColor(Qt.blue)

        if option.state & QStyle.State_MouseOver:
            brush.setColor(QColor(100, 100, 100, 100))
            if brush.style() == Qt.NoBrush:
                # Make sure the highlight is actually visible.
                brush.setStyle(Qt.SolidPattern)

        painter.setPen(pen)
        painter.setBrush(brush)
        painter.drawPath(path)
        painter.restore()

    def itemChange(self, change, value):
        if change == QGraphicsPathItem.ItemSelectedHasChanged:
            self.setZValue(self.zValue() + (1 if value else -1))
        return QGraphicsPathItem.itemChange(self, change, value)

    def _updateTextAnchor(self):
        rect = self.text.boundingRect()
        pos = anchor_rect(rect, self._anchor)
        self.text.setPos(pos)

    def _onLayoutChanged(self):
        self._updateTextAnchor()


class GraphicsTextEdit(QGraphicsTextItem):
    #: Edit triggers
    NoEditTriggers, DoubleClicked = 0, 1

    editingFinished = Signal()
    editingStarted = Signal()

    documentSizeChanged = Signal()

    def __init__(self, *args, **kwargs):
        super(GraphicsTextEdit, self).__init__(*args, **kwargs)
        self.setCursor(Qt.IBeamCursor)
        self.setTabChangesFocus(True)
        self._edittrigger = GraphicsTextEdit.DoubleClicked
        self._editing = False
        self.document().documentLayout().documentSizeChanged.connect(
            self.documentSizeChanged
        )

    def mouseDoubleClickEvent(self, event):
        super(GraphicsTextEdit, self).mouseDoubleClickEvent(event)
        if self._edittrigger == GraphicsTextEdit.DoubleClicked:
            self._start()

    def focusOutEvent(self, event):
        super(GraphicsTextEdit, self).focusOutEvent(event)

        if self._editing:
            self._end()

    def _start(self):
        self._editing = True
        self.setTextInteractionFlags(Qt.TextEditorInteraction)
        self.setFocus(Qt.MouseFocusReason)
        self.editingStarted.emit()

    def _end(self):
        self._editing = False
        self.setTextInteractionFlags(Qt.NoTextInteraction)
        self.editingFinished.emit()


class VennDiagram(QGraphicsWidget):
    # rect and petal are for future work
    Circle, Ellipse, Rect, Petal = 1, 2, 3, 4

    TitleFormat = "<center><h4>{0}</h4>{1}</center>"

    selectionChanged = Signal()
    itemTextEdited = Signal(int, str)

    def __init__(self, parent=None):
        super(VennDiagram, self).__init__(parent)
        self.shapeType = VennDiagram.Circle
        self._items = []
        self._vennareas = []
        self._textitems = []
        self._subsettextitems = []
        self._textanchors = []

    def item(self, index):
        return self._items[index]

    def items(self):
        return list(self._items)

    def count(self):
        return len(self._items)

    def setItems(self, items):
        if self._items:
            self.clear()

        self._items = list(items)

        for item in self._items:
            item.setParentItem(self)
            item.setVisible(True)

        fmt = self.TitleFormat.format

        font = self.font()
        font.setPixelSize(14)

        for item in items:
            text = GraphicsTextEdit(self)
            text.setFont(font)
            text.setDefaultTextColor(QColor("#333"))
            text.setHtml(fmt(escape(item.text), item.informativeText))
            text.adjustSize()
            text.editingStarted.connect(self._on_editingStarted)
            text.editingFinished.connect(self._on_editingFinished)
            text.documentSizeChanged.connect(
                self._on_itemTextSizeChanged
            )

            self._textitems.append(text)

        self._vennareas = [
            VennIntersectionArea(parent=self)
            for i in range(2 ** len(items))
        ]
        self._subsettextitems = [
            QGraphicsTextItem(parent=self)
            for i in range(2 ** len(items))
        ]

        self._updateLayout()

    def clear(self):
        scene = self.scene()
        items = self.vennareas() + list(self.items()) + self._textitems

        for item in self._textitems:
            item.editingStarted.disconnect(self._on_editingStarted)
            item.editingFinished.disconnect(self._on_editingFinished)
            item.documentSizeChanged.disconnect(
                self._on_itemTextSizeChanged
            )

        self._items = []
        self._vennareas = []
        self._textitems = []
        self._subsettextitems = []
        self._textanchors = []

        for item in items:
            item.setVisible(False)
            item.setParentItem(None)
            if scene is not None:
                scene.removeItem(item)

    def vennareas(self):
        return list(self._vennareas)

    def setFont(self, font):
        if font != self.font():
            self.prepareGeometryChange()
            super().setFont(font)

            for item in self.items():
                item.setFont(font)

    def _updateLayout(self):
        rect = self.geometry()
        n = len(self._items)
        if not n:
            return

        regions = venn_diagram(n)

        # The y axis in Qt points downward
        transform = QTransform().scale(1, -1)
        regions = list(map(transform.map, regions))

        union_brect = reduce(QRectF.united,
                             (path.boundingRect() for path in regions))

        scalex = rect.width() / union_brect.width()
        scaley = rect.height() / union_brect.height()
        scale = min(scalex, scaley)

        transform = QTransform().scale(scale, scale)

        regions = [transform.map(path) for path in regions]

        center = rect.width() / 2, rect.height() / 2
        for item, path in zip(self.items(), regions):
            item.setPath(path)
            item.setPos(*center)

        intersections = venn_intersections(regions)
        assert len(intersections) == 2 ** n
        assert len(self.vennareas()) == 2 ** n

        anchors = [(0, 0)] + subset_anchors(self._items)

        anchor_transform = QTransform().scale(rect.width(), -rect.height())
        for i, area in enumerate(self.vennareas()):
            area.setPath(intersections[setkey(i, n)])
            area.setPos(*center)
            x, y = anchors[i]
            anchor = anchor_transform.map(QPointF(x, y))
            area.setTextAnchor(anchor)
            area.setZValue(30)

        self._updateTextAnchors()

    def _updateTextAnchors(self):
        n = len(self._items)

        items = self._items
        dist = 15

        shape = reduce(QPainterPath.united, [item.path() for item in items])
        brect = shape.boundingRect()
        bradius = max(brect.width() / 2, brect.height() / 2)

        center = self.boundingRect().center()

        anchors = _category_anchors(items)
        self._textanchors = []
        for angle, anchor_h, anchor_v in anchors:
            line = QLineF.fromPolar(bradius, angle)
            ext = QLineF.fromPolar(dist, angle)
            line = QLineF(line.p1(), line.p2() + ext.p2())
            line = line.translated(center)

            anchor_pos = line.p2()
            self._textanchors.append((anchor_pos, anchor_h, anchor_v))

        for i in range(n):
            self._updateTextItemPos(i)

    def _updateTextItemPos(self, i):
        item = self._textitems[i]
        anchor_pos, anchor_h, anchor_v = self._textanchors[i]
        rect = item.boundingRect()
        pos = anchor_rect(rect, anchor_pos, anchor_h, anchor_v)
        item.setPos(pos)

    def setGeometry(self, geometry):
        super(VennDiagram, self).setGeometry(geometry)
        self._updateLayout()

    def _on_editingStarted(self):
        item = self.sender()
        index = self._textitems.index(item)
        text = self._items[index].text
        item.setTextWidth(-1)
        item.setHtml(self.TitleFormat.format(escape(text), "<br/>"))

    def _on_editingFinished(self):
        item = self.sender()
        index = self._textitems.index(item)
        text = item.toPlainText()
        if text != self._items[index].text:
            self._items[index].text = text

            self.itemTextEdited.emit(index, text)

        item.setHtml(
            self.TitleFormat.format(
                escape(text), self._items[index].informativeText))
        item.adjustSize()

    def _on_itemTextSizeChanged(self):
        item = self.sender()
        index = self._textitems.index(item)
        self._updateTextItemPos(index)


def anchor_rect(rect, anchor_pos,
                anchor_h=Qt.AnchorHorizontalCenter,
                anchor_v=Qt.AnchorVerticalCenter):

    if anchor_h == Qt.AnchorLeft:
        x = anchor_pos.x()
    elif anchor_h == Qt.AnchorHorizontalCenter:
        x = anchor_pos.x() - rect.width() / 2
    elif anchor_h == Qt.AnchorRight:
        x = anchor_pos.x() - rect.width()
    else:
        raise ValueError(anchor_h)

    if anchor_v == Qt.AnchorTop:
        y = anchor_pos.y()
    elif anchor_v == Qt.AnchorVerticalCenter:
        y = anchor_pos.y() - rect.height() / 2
    elif anchor_v == Qt.AnchorBottom:
        y = anchor_pos.y() - rect.height()
    else:
        raise ValueError(anchor_v)

    return QPointF(x, y)


def radians(angle):
    return 2 * math.pi * angle / 360


def unit_point(x, r=1.0):
    x = radians(x)
    return (r * math.cos(x), r * math.sin(x))


def _category_anchors(shapes):
    n = len(shapes)
    return _CATEGORY_ANCHORS[n - 1]


# (angle, horizontal anchor, vertical anchor)
_CATEGORY_ANCHORS = (
    # n == 1
    ((90, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),),
    # n == 2
    ((180, Qt.AnchorRight, Qt.AnchorVerticalCenter),
     (0, Qt.AnchorLeft, Qt.AnchorVerticalCenter)),
    # n == 3
    ((150, Qt.AnchorRight, Qt.AnchorBottom),
     (30, Qt.AnchorLeft, Qt.AnchorBottom),
     (270, Qt.AnchorHorizontalCenter, Qt.AnchorTop)),
    # n == 4
    ((270 + 45, Qt.AnchorLeft, Qt.AnchorTop),
     (270 - 45, Qt.AnchorRight, Qt.AnchorTop),
     (90 - 15, Qt.AnchorLeft, Qt.AnchorBottom),
     (90 + 15, Qt.AnchorRight, Qt.AnchorBottom)),
    # n == 5
    ((90 - 5, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),
     (18 - 5, Qt.AnchorLeft, Qt.AnchorVerticalCenter),
     (306 - 5, Qt.AnchorLeft, Qt.AnchorTop),
     (234 - 5, Qt.AnchorRight, Qt.AnchorTop),
     (162 - 5, Qt.AnchorRight, Qt.AnchorVerticalCenter),)
)


def subset_anchors(shapes):
    n = len(shapes)
    if n == 1:
        return [(0, 0)]
    elif n == 2:
        return [unit_point(180, r=1/3),
                unit_point(0, r=1/3),
                (0, 0)]
    elif n == 3:
        return [unit_point(150, r=0.35),  # A
                unit_point(30, r=0.35),   # B
                unit_point(90, r=0.27),   # AB
                unit_point(270, r=0.35),  # C
                unit_point(210, r=0.27),  # AC
                unit_point(330, r=0.27),  # BC
                unit_point(0, r=0),]      # ABC
    elif n == 4:
        anchors = [
            (0.400, 0.110),    # A
            (-0.400, 0.110),   # B
            (0.000, -0.285),   # AB
            (0.180, 0.330),    # C
            (0.265, 0.205),    # AC
            (-0.240, -0.110),  # BC
            (-0.100, -0.190),  # ABC
            (-0.180, 0.330),   # D
            (0.240, -0.110),   # AD
            (-0.265, 0.205),   # BD
            (0.100, -0.190),   # ABD
            (0.000, 0.250),    # CD
            (0.153, 0.090),    # ACD
            (-0.153, 0.090),   # BCD
            (0.000, -0.060),   # ABCD
        ]
        return anchors

    elif n == 5:
        anchors = [None] * 32
        # Base anchors
        A = (0.033, 0.385)
        AD = (0.095, 0.250)
        AE = (-0.100, 0.265)
        ACE = (-0.130, 0.220)
        ADE = (0.010, 0.225)
        ACDE = (-0.095, 0.175)
        ABCDE = (0.0, 0.0)

        anchors[-1] = ABCDE

        bases = [(0b00001, A),
                 (0b01001, AD),
                 (0b10001, AE),
                 (0b10101, ACE),
                 (0b11001, ADE),
                 (0b11101, ACDE)]

        for i in range(5):
            for index, anchor in bases:
                index = bit_rot_left(index, i, bits=5)
                assert anchors[index] is None
                anchors[index] = rotate_point(anchor, - 72 * i)

        assert all(anchors[1:])
        return anchors[1:]
    return None


def bit_rot_left(x, y, bits=32):
    mask = 2 ** bits - 1
    x_masked = x & mask
    return (x << y) & mask | (x_masked >> bits - y)


def rotate_point(p, angle):
    r = radians(angle)
    R = np.array([[math.cos(r), -math.sin(r)],
                  [math.sin(r), math.cos(r)]])
    x, y = np.dot(R, p)
    return (float(x), float(y))


def line_extended(line, distance):
    """
    Return an QLineF extended by `distance` units in the positive direction.
    """
    angle = line.angle() / 360 * 2 * math.pi
    dx, dy = unit_point(angle, r=distance)
    return QLineF(line.p1(), line.p2() + QPointF(dx, dy))


def circle_path(center, r=1.0):
    return ellipse_path(center, r, r, rotation=0)


def ellipse_path(center, a, b, rotation=0):
    if not isinstance(center, QPointF):
        center = QPointF(*center)

    brect = QRectF(-a, -b, 2 * a, 2 * b)

    path = QPainterPath()
    path.addEllipse(brect)

    if rotation != 0:
        transform = QTransform().rotate(rotation)
        path = transform.map(path)

    path.translate(center)
    return path


# TODO: Should include anchors for text layout (both inside and outside).
# for each item {path: QPainterPath,
#                text_anchors: [{center}] * (2 ** n)
#                mayor_axis: QLineF,
#                boundingRect QPolygonF (with 4 vertices)}
#
# Should be a new class with overloads for ellipse/circle, rect, and petal
# shapes, should store all constructor parameters, rotation, center,
# mayor/minor axis.


def venn_diagram(n):
    if n < 1 or n > 5:
        raise ValueError()

    paths = []

    if n == 1:
        paths = [circle_path(center=(0, 0), r=0.5)]
    elif n == 2:
        angles = [180, 0]
        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)
                 for x in angles]
    elif n == 3:
        angles = [150 - 120 * i for i in range(3)]
        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)
                 for x in angles]
    elif n == 4:
        # Constants shamelessly stolen from VennDiagram R package
        paths = [
            ellipse_path((0.65 - 0.5, 0.47 - 0.5), 0.35, 0.20, 45),
            ellipse_path((0.35 - 0.5, 0.47 - 0.5), 0.35, 0.20, 135),
            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 45),
            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 134),
        ]
    elif n == 5:
        # Constants shamelessly stolen from VennDiagram R package
        d = 0.13
        a, b = 0.24, 0.48
        a, b = b, a
        a, b = 0.48, 0.24
        paths = [ellipse_path(unit_point((1 - i) * 72, r=d),
                              a, b, rotation=90 - (i * 72))
                 for i in range(5)]

    return paths


def setkey(intval, n):
    return tuple(bool(intval & (2 ** i)) for i in range(n))


def keyrange(n):
    if n < 0:
        raise ValueError()

    for i in range(2 ** n):
        yield setkey(i, n)


def venn_intersections(paths):
    n = len(paths)
    return {key: venn_intersection(paths, key) for key in keyrange(n)}


def venn_intersection(paths, key):
    if not any(key):
        return QPainterPath()

    # first take the intersection of all included paths
    path = reduce(QPainterPath.intersected,
                  (path for path, included in zip(paths, key) if included))

    # subtract all the excluded sets (i.e. take the intersection
    # with the excluded set complements)
    path = reduce(QPainterPath.subtracted,
                  (path for path, included in zip(paths, key) if not included),
                  path)

    return path


def append_column(data, where, variable, column):
    X, Y, M = data.X, data.Y, data.metas
    domain = data.domain
    attr = domain.attributes
    class_vars = domain.class_vars
    metas = domain.metas
    if where == "X":
        attr = attr + (variable,)
        X = np.hstack((X, column))
    elif where == "Y":
        class_vars = class_vars + (variable,)
        Y = np.hstack((Y, column))
    elif where == "M":
        metas = metas + (variable,)
        M = np.hstack((M, column))
    else:
        raise ValueError
    domain = Domain(attr, class_vars, metas)
    new_data = data.transform(domain)
    new_data[:, variable] = column
    return new_data

def arrays_equal(a, b, type_):
    """
    checks if arrays have nans in same places and if not-nan elements
    are equal
    """
    if a is None and b is None:
        return True
    if a is None or b is None:
        return False
    if type_ is not StringVariable:
        nana = np.isnan(a)
        nanb = np.isnan(b)
        return np.all(nana == nanb) and np.all(a[~nana] == b[~nanb])
    else:
        return np.all(a == b)

def pad_columns(values, mask, l):
    #inflates columns with nans
    a = np.full((l, 1), np.nan, dtype=values.dtype)
    a[mask] = values.reshape(-1, 1)
    return a

def get_perm(ids, all_ids):
    return [all_ids.index(el) for el in ids if el in all_ids]


def main():  # pragma: no cover
    # pylint: disable=import-outside-toplevel
    from Orange.evaluation import ShuffleSplit

    data = Table("brown-selected")

    if not "test_rows":  # change to `if not "test_rows" to test columns
        data = append_column(data, "M", StringVariable("Test"),
                             (np.arange(len(data)).reshape(-1, 1) % 30).astype(str))
        res = ShuffleSplit(n_resamples=5, test_size=0.7, stratified=False)
        indices = iter(res.get_indices(data))
        datasets = []
        for i in range(1, 6):
            sample, _ = next(indices)
            data1 = data[sample]
            data1.name = chr(ord("A") + i)
            datasets.append((data1, i))
    else:
        domain = data.domain
        data1 = data.transform(Domain(domain.attributes[:15], domain.class_var))
        data2 = data.transform(Domain(domain.attributes[10:], domain.class_var))
        datasets = [(data1, 1), (data2, 2)]

    WidgetPreview(OWVennDiagram).run(setData=datasets)


if __name__ == "__main__":  # pragma: no cover
    main()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]

**Title:** Venn Diagram Crashes When Handling Tables Without Attributes

**Description:**
When using the Venn Diagram widget with a table that has no attributes, the application crashes unexpectedly.

**Example Buggy Code:**
```python
def test_no_attributes(self):
    domain = Domain([], class_vars=self.data.domain.attributes)
    n = len(self.data)
    table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)
    self.widget.rowwise = True
    self.send_signal(self.signal_name, table, 1)
```

In this test scenario, a table is created with no attribute columns. When this table is sent to the Venn Diagram widget, it fails to handle the absence of attributes properly.

**Expected Behavior:**
The Venn Diagram widget should handle tables without attributes gracefully without crashing. It should either display an empty diagram or provide a meaningful message indicating that there are no attributes to display.

**Actual Behavior:**
The application crashes with the following error:
```
TypeError: __init__() missing 1 required positional argument: 'attributes'
```
This indicates that the widget does not properly initialize when there are no attributes in the input table.

[/ISSUE]
{
    "TestOWVennDiagram.test_image_export": "PASSED",
    "TestOWVennDiagram.test_input_compatibility": "PASSED",
    "TestOWVennDiagram.test_msg_base_class": "PASSED",
    "TestOWVennDiagram.test_multiple_input_over_cols": "PASSED",
    "TestOWVennDiagram.test_multiple_input_rows_id": "PASSED",
    "TestOWVennDiagram.test_no_attributes": "PASSED",
    "TestOWVennDiagram.test_no_data": "PASSED",
    "TestOWVennDiagram.test_output_duplicates": "PASSED",
    "TestOWVennDiagram.test_outputs": "PASSED",
    "TestOWVennDiagram.test_rows_id": "PASSED",
    "TestOWVennDiagram.test_rows_identifiers": "PASSED",
    "TestOWVennDiagram.test_too_many_inputs": "PASSED",
    "TestOWVennDiagram.test_unconditional_commit_on_new_signal": "PASSED",
    "TestVennUtilities.test_array_equals_cols": "PASSED",
    "TestVennUtilities.test_get_perm": "PASSED",
    "TestVennUtilities.test_pad_columns": "PASSED",
    "TestOWVennDiagram.test_minimum_size": "FAILED"
}
diff --git a/Orange/widgets/visualize/owvenndiagram.py b/Orange/widgets/visualize/owvenndiagram.py
index aa229a473..0076a8280 100644
--- a/Orange/widgets/visualize/owvenndiagram.py
+++ b/Orange/widgets/visualize/owvenndiagram.py
@@ -379,16 +379,19 @@ class OWVennDiagram(widget.OWWidget):
             X = np.hstack(values['attributes'])
         if 'metas' in values:
             metas = np.hstack(values['metas'])
+            n = len(metas)
         if 'class_vars' in values:
             class_vars = np.hstack(values['class_vars'])
+            n = len(class_vars)
+        if X is None:
+            X = np.empty((n, 0))
         table = Table.from_numpy(Domain(**domain), X, class_vars, metas)
         if ids is not None:
             table.ids = ids
         return table
 
     def extract_columnwise(self, var_dict, columns=None):
-        #for columns
-        domain = defaultdict(list)
+        domain = {type_ : [] for type_ in self.atr_types}
         values = defaultdict(list)
         renamed = []
         for atr_type, vars_dict in var_dict.items():
@@ -514,7 +517,7 @@ class OWVennDiagram(widget.OWWidget):
         for table_key, dict_ in ids.items():
             permutations[table_key] = get_perm(list(dict_), all_ids)
 
-        domain = defaultdict(list)
+        domain = {type_ : [] for type_ in self.atr_types}
         values = defaultdict(list)
         renamed = []
         for atr_type, vars_dict in var_dict.items():
@@ -551,8 +554,8 @@ class OWVennDiagram(widget.OWWidget):
 
         if renamed:
             self.Warning.renamed_vars(', '.join(renamed))
-        idas = None if self.selected_feature else np.array(all_ids)
-        table = self.merge_data(domain, values, idas)
+        ids = None if self.selected_feature else np.array(all_ids)
+        table = self.merge_data(domain, values, ids)
         if selection:
             mask = [idx in self.selected_items for idx in all_ids]
             return create_annotated_table(table, mask)

{"Orange/widgets/visualize/owvenndiagram.py": "\"\"\"\nVenn Diagram Widget\n-------------------\n\n\"\"\"\n\nimport math\nimport unicodedata\nfrom collections import namedtuple, defaultdict\nfrom itertools import compress, count\nfrom functools import reduce\nfrom operator import attrgetter\nfrom xml.sax.saxutils import escape\n\nimport numpy as np\n\nfrom AnyQt.QtWidgets import (\n    QGraphicsScene, QGraphicsView, QGraphicsWidget,\n    QGraphicsPathItem, QGraphicsTextItem, QStyle, QSizePolicy\n)\nfrom AnyQt.QtGui import (\n    QPainterPath, QPainter, QTransform, QColor, QBrush, QPen, QPalette\n)\nfrom AnyQt.QtCore import Qt, QPointF, QRectF, QLineF\nfrom AnyQt.QtCore import pyqtSignal as Signal\n\nfrom Orange.data import Table, Domain, StringVariable, RowInstance\nfrom Orange.data.util import get_unique_names_duplicates\nfrom Orange.widgets import widget, gui, settings\nfrom Orange.widgets.utils import itemmodels, colorpalettes\nfrom Orange.widgets.utils.annotated_data import (create_annotated_table,\n                                                 ANNOTATED_DATA_SIGNAL_NAME)\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output, Msg\n\n\n_InputData = namedtuple(\"_InputData\", [\"key\", \"name\", \"table\"])\n_ItemSet = namedtuple(\"_ItemSet\", [\"key\", \"name\", \"title\", \"items\"])\n\n\nclass OWVennDiagram(widget.OWWidget):\n    name = \"Venn Diagram\"\n    description = \"A graphical visualization of the overlap of data instances \" \\\n                  \"from a collection of input datasets.\"\n    icon = \"icons/VennDiagram.svg\"\n    priority = 280\n    keywords = []\n    settings_version = 2\n\n    class Inputs:\n        data = Input(\"Data\", Table, multiple=True)\n\n    class Outputs:\n        selected_data = Output(\"Selected Data\", Table, default=True)\n        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)\n\n    class Error(widget.OWWidget.Error):\n        instances_mismatch = Msg(\"Data sets do not contain the same instances.\")\n        too_many_inputs = Msg(\"Venn diagram accepts at most five datasets.\")\n\n    class Warning(widget.OWWidget.Warning):\n        renamed_vars = Msg(\"Some variables have been renamed \"\n                           \"to avoid duplicates.\\n{}\")\n\n    selection: list\n\n    settingsHandler = settings.DomainContextHandler()\n    # Indices of selected disjoint areas\n    selection = settings.Setting([], schema_only=True)\n    #: Output unique items (one output row for every unique instance `key`)\n    #: or preserve all duplicates in the output.\n    output_duplicates = settings.Setting(False)\n    autocommit = settings.Setting(True)\n    rowwise = settings.Setting(True)\n    selected_feature = settings.ContextSetting(None)\n\n    want_control_area = False\n    graph_name = \"scene\"\n    atr_types = ['attributes', 'metas', 'class_vars']\n    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}\n    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}\n\n    def __init__(self):\n        super().__init__()\n\n        # Diagram update is in progress\n        self._updating = False\n        # Input update is in progress\n        self._inputUpdate = False\n        # Input datasets in the order they were 'connected'.\n        self.data = {}\n        # Extracted input item sets in the order they were 'connected'\n        self.itemsets = {}\n        # A list with 2 ** len(self.data) elements that store item sets\n        # belonging to each area\n        self.disjoint = []\n        # A list with  2 ** len(self.data) elements that store keys of tables\n        # intersected in each area\n        self.area_keys = []\n\n        # Main area view\n        self.scene = QGraphicsScene()\n        self.view = QGraphicsView(self.scene)\n        self.view.setRenderHint(QPainter.Antialiasing)\n        self.view.setBackgroundRole(QPalette.Window)\n        self.view.setFrameStyle(QGraphicsView.StyledPanel)\n\n        self.mainArea.layout().addWidget(self.view)\n        self.vennwidget = VennDiagram()\n        self._resize()\n        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)\n        self.scene.selectionChanged.connect(self._on_selectionChanged)\n\n        self.scene.addItem(self.vennwidget)\n\n        controls = gui.hBox(self.mainArea)\n        box = gui.radioButtonsInBox(\n            controls, self, 'rowwise',\n            [\"Columns (features)\", \"Rows (instances), matched by\", ],\n            box=\"Elements\", callback=self._on_matching_changed\n        )\n        gui.comboBox(\n            gui.indentedBox(box), self, \"selected_feature\",\n            model=itemmodels.VariableListModel(placeholder=\"Instance identity\"),\n            callback=self._on_inputAttrActivated\n            )\n        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)\n\n        self.outputs_box = box = gui.vBox(controls, \"Output\")\n        self.output_duplicates_cb = gui.checkBox(\n            box, self, \"output_duplicates\", \"Output duplicates\",\n            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda\n        gui.auto_send(box, self, \"autocommit\", box=False)\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        self._queue = []\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._resize()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        self._resize()\n\n    def _resize(self):\n        # vennwidget draws so that the diagram fits into its geometry,\n        # while labels take further 120 pixels, hence -120 in below formula\n        size = max(200, min(self.view.width(), self.view.height()) - 120)\n        self.vennwidget.resize(size, size)\n        self.scene.setSceneRect(self.scene.itemsBoundingRect())\n\n    @Inputs.data\n    @check_sql_input\n    def setData(self, data, key=None):\n        self.Error.too_many_inputs.clear()\n        if not self._inputUpdate:\n            self._inputUpdate = True\n        if key in self.data:\n            if data is None:\n                # Remove the input\n                # Clear possible warnings.\n                self.Warning.clear()\n                del self.data[key]\n            else:\n                # Update existing item\n                self.data[key] = self.data[key]._replace(name=data.name, table=data)\n\n        elif data is not None:\n            # TODO: Allow setting more them 5 inputs and let the user\n            # select the 5 to display.\n            if len(self.data) == 5:\n                self.Error.too_many_inputs()\n                return\n            # Add a new input\n            self.data[key] = _InputData(key, data.name, data)\n        self._setInterAttributes()\n\n    def data_equality(self):\n        \"\"\" Checks if all input datasets have same ids. \"\"\"\n        if not self.data.values():\n            return True\n        sets = []\n        for val in self.data.values():\n            sets.append(set(val.table.ids))\n        inter = reduce(set.intersection, sets)\n        return len(inter) == max(map(len, sets))\n\n    def settings_compatible(self):\n        self.Error.instances_mismatch.clear()\n        if not self.rowwise:\n            if not self.data_equality():\n                self.vennwidget.clear()\n                self.Error.instances_mismatch()\n                self.itemsets = {}\n                return False\n        return True\n\n    def handleNewSignals(self):\n        self._inputUpdate = False\n        self.vennwidget.clear()\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n\n        self._createItemsets()\n        self._createDiagram()\n        # If autocommit is enabled, _createDiagram already outputs data\n        # If not, call unconditional_commit from here\n        if not self.autocommit:\n            self.unconditional_commit()\n\n        self._updateInfo()\n        super().handleNewSignals()\n\n    def intersectionStringAttrs(self):\n        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]\n        if sets:\n            return reduce(set.intersection, sets)\n        return set()\n\n    def _setInterAttributes(self):\n        model = self.controls.selected_feature.model()\n        model[:] = [None] + list(self.intersectionStringAttrs())\n        if self.selected_feature:\n            names = (var.name for var in model if var)\n            if self.selected_feature.name not in names:\n                self.selected_feature = model[0]\n\n    def _itemsForInput(self, key):\n        \"\"\"\n        Calculates input for venn diagram, according to user's settings.\n        \"\"\"\n        table = self.data[key].table\n        attr = self.selected_feature\n        if attr:\n            return [str(inst[attr]) for inst in table\n                    if not np.isnan(inst[attr])]\n        else:\n            return list(table.ids)\n\n    def _createItemsets(self):\n        \"\"\"\n        Create itemsets over rows or columns (domains) of input tables.\n        \"\"\"\n        olditemsets = dict(self.itemsets)\n        self.itemsets.clear()\n\n        for key, input_ in self.data.items():\n            if self.rowwise:\n                items = self._itemsForInput(key)\n            else:\n                items = [el.name for el in input_.table.domain.attributes]\n            name = input_.name\n            if key in olditemsets and olditemsets[key].name == name:\n                # Reuse the title (which might have been changed by the user)\n                title = olditemsets[key].title\n            else:\n                title = name\n\n            itemset = _ItemSet(key=key, name=name, title=title, items=items)\n            self.itemsets[key] = itemset\n\n    def _createDiagram(self):\n        self._updating = True\n\n        oldselection = list(self.selection)\n\n        n = len(self.itemsets)\n        self.disjoint, self.area_keys = \\\n            self.get_disjoint(set(s.items) for s in self.itemsets.values())\n\n        vennitems = []\n        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)\n\n        for i, item in enumerate(self.itemsets.values()):\n            cnt = len(set(item.items))\n            cnt_all = len(item.items)\n            if cnt != cnt_all:\n                fmt = '{} <i>(all: {})</i>'\n            else:\n                fmt = '{}'\n            counts = fmt.format(cnt, cnt_all)\n            gr = VennSetItem(text=item.title, informativeText=counts)\n            color = colors[i]\n            color.setAlpha(100)\n            gr.setBrush(QBrush(color))\n            gr.setPen(QPen(Qt.NoPen))\n            vennitems.append(gr)\n\n        self.vennwidget.setItems(vennitems)\n\n        for i, area in enumerate(self.vennwidget.vennareas()):\n            area_items = list(map(str, list(self.disjoint[i])))\n            if i:\n                area.setText(\"{0}\".format(len(area_items)))\n\n            label = disjoint_set_label(i, n, simplify=False)\n            head = \"<h4>|{}| = {}</h4>\".format(label, len(area_items))\n            if len(area_items) > 32:\n                items_str = \", \".join(map(escape, area_items[:32]))\n                hidden = len(area_items) - 32\n                tooltip = (\"{}<span>{}, ...</br>({} items not shown)<span>\"\n                           .format(head, items_str, hidden))\n            elif area_items:\n                tooltip = \"{}<span>{}</span>\".format(\n                    head,\n                    \", \".join(map(escape, area_items))\n                )\n            else:\n                tooltip = head\n\n            area.setToolTip(tooltip)\n\n            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))\n            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)\n            area.setSelected(i in oldselection)\n\n        self._updating = False\n        self._on_selectionChanged()\n\n    def _updateInfo(self):\n        # Clear all warnings\n        self.warning()\n\n        if self.selected_feature is None:\n            no_idx = [\"#{}\".format(i + 1)\n                      for i, key in enumerate(self.data)\n                      if not source_attributes(self.data[key].table.domain)]\n            if len(no_idx) == 1:\n                self.warning(\"Dataset {} has no suitable identifiers.\"\n                             .format(no_idx[0]))\n            elif len(no_idx) > 1:\n                self.warning(\"Datasets {} and {} have no suitable identifiers.\"\n                             .format(\", \".join(no_idx[:-1]), no_idx[-1]))\n\n    def _on_selectionChanged(self):\n        if self._updating:\n            return\n\n        areas = self.vennwidget.vennareas()\n        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]\n        self.invalidateOutput()\n\n    def _on_matching_changed(self):\n        self.output_duplicates_cb.setEnabled(bool(self.rowwise))\n        if not self.settings_compatible():\n            self.invalidateOutput()\n            return\n        self._createItemsets()\n        self._createDiagram()\n        self._updateInfo()\n\n    def _on_inputAttrActivated(self):\n        self.rowwise = 1\n        self._on_matching_changed()\n\n    def _on_itemTextEdited(self, index, text):\n        text = str(text)\n        key = list(self.itemsets)[index]\n        self.itemsets[key] = self.itemsets[key]._replace(title=text)\n\n    def invalidateOutput(self):\n        self.commit()\n\n    def merge_data(self, domain, values, ids=None):\n        X, metas, class_vars = None, None, None\n        renamed = []\n        for val in domain.values():\n            names = [var.name for var in val]\n            unique_names = get_unique_names_duplicates(names)\n            for n, u, idx, var in zip(names, unique_names, count(), val):\n                if n != u:\n                    val[idx] = var.copy(name=u)\n                    renamed.append(n)\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        if 'attributes' in values:\n            X = np.hstack(values['attributes'])\n        if 'metas' in values:\n            metas = np.hstack(values['metas'])\n        if 'class_vars' in values:\n            class_vars = np.hstack(values['class_vars'])\n        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)\n        if ids is not None:\n            table.ids = ids\n        return table\n\n    def extract_columnwise(self, var_dict, columns=None):\n        #for columns\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                is_selected = bool(columns) and var_name.name in columns\n                if var_data[0]:\n                    #columns are different, copy all, rename them\n                    for var, table_key in var_data[1]:\n                        idx = list(self.data).index(table_key) + 1\n                        new_atr = var.copy(name=f'{var_name.name} ({idx})')\n                        if columns and atr_type == 'attributes':\n                            new_atr.attributes['Selected'] = is_selected\n                        domain[atr_type].append(new_atr)\n                        renamed.append(var_name.name)\n                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],\n                                                        self.atr_vals[atr_type])\n                                                .reshape(-1, 1))\n                else:\n                    new_atr = var_data[1][0][0].copy()\n                    if columns and atr_type == 'attributes':\n                        new_atr.attributes['Selected'] = is_selected\n                    domain[atr_type].append(new_atr)\n                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],\n                                                    self.atr_vals[atr_type])\n                                            .reshape(-1, 1))\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        return self.merge_data(domain, values)\n\n    def curry_merge(self, table_key, atr_type, ids=None, selection=False):\n        if self.rowwise:\n            check_equality = self.arrays_equal_rows\n        else:\n            check_equality = self.arrays_equal_cols\n\n        def inner(new_atrs, atr):\n            \"\"\"\n            Atrs - list of variables we wish to merge\n            new_atrs - dictionary where key is old var, val\n                is [is_different:bool, table_keys:list]), is_different is set to True,\n                if we are outputing duplicates, but the value is arbitrary\n            \"\"\"\n            if atr in new_atrs:\n                if not selection and self.output_duplicates:\n                    #if output_duplicates, we just check if compute value is the same\n                    new_atrs[atr][0] = True\n                elif not new_atrs[atr][0]:\n                    for var, key in new_atrs[atr][1]:\n                        if not check_equality(table_key,\n                                              key,\n                                              atr.name,\n                                              self.atr_vals[atr_type],\n                                              type(var), ids):\n                            new_atrs[atr][0] = True\n                            break\n                new_atrs[atr][1].append((atr, table_key))\n            else:\n                new_atrs[atr] = [False, [(atr, table_key)]]\n            return new_atrs\n        return inner\n\n    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):\n        #gets masks, compares same as cols\n        t1 = self.data[key1].table\n        t2 = self.data[key2].table\n        inter_val = set(ids[key1]) & set(ids[key2])\n        t1_inter = [ids[key1][val] for val in inter_val]\n        t2_inter = [ids[key2][val] for val in inter_val]\n        return arrays_equal(\n            getattr(t1[t1_inter, name],\n                    data_type).reshape(-1, 1),\n            getattr(t2[t2_inter, name],\n                    data_type).reshape(-1, 1),\n            type_)\n\n    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):\n        return arrays_equal(\n            getattr(self.data[key1].table[:, name],\n                    data_type),\n            getattr(self.data[key2].table[:, name],\n                    data_type),\n            type_)\n\n    def create_from_columns(self, columns, relevant_keys, get_selected):\n        \"\"\"\n        Columns are duplicated only if values differ (even\n        if only in order of values), origin table name and input slot is added to column name.\n        \"\"\"\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_keys:\n                table = self.data[table_key].table\n                if atr_type == 'attributes':\n                    if get_selected:\n                        atrs = list(compress(table.domain.attributes,\n                                             [c.name in columns for c in table.domain.attributes]))\n                    else:\n                        atrs = getattr(table.domain, atr_type)\n                else:\n                    atrs = getattr(table.domain, atr_type)\n                merge_vars = self.curry_merge(table_key, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n\n        if get_selected:\n            annotated = self.extract_columnwise(var_dict, None)\n        else:\n            annotated = self.extract_columnwise(var_dict, columns)\n\n        return annotated\n\n    def extract_rowwise(self, var_dict, ids=None, selection=False):\n        \"\"\"\n        keys : ['attributes', 'metas', 'class_vars']\n        vals: new_atrs - dictionary where key is old name, val\n            is [is_different:bool, table_keys:list])\n        ids: dict with ids for each table\n        \"\"\"\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n\n        permutations = {}\n        for table_key, dict_ in ids.items():\n            permutations[table_key] = get_perm(list(dict_), all_ids)\n\n        domain = defaultdict(list)\n        values = defaultdict(list)\n        renamed = []\n        for atr_type, vars_dict in var_dict.items():\n            for var_name, var_data in vars_dict.items():\n                different = var_data[0]\n                if different:\n                    # Columns are different, copy and rename them.\n                    # Renaming is done here to mark appropriately the source table.\n                    # Additional strange clashes are checked later in merge_data\n                    for var, table_key in var_data[1]:\n                        temp = self.data[table_key].table\n                        idx = list(self.data).index(table_key) + 1\n                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))\n                        renamed.append(var_name.name)\n                        v = getattr(temp[list(ids[table_key].values()), var_name],\n                                    self.atr_vals[atr_type])\n                        perm = permutations[table_key]\n                        if len(v) < len(all_ids):\n                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))\n                        else:\n                            values[atr_type].append(v[perm].reshape(-1, 1))\n                else:\n                    value = np.full((len(all_ids), 1), np.nan)\n                    domain[atr_type].append(var_data[1][0][0].copy())\n                    for _, table_key in var_data[1]:\n                        #different tables have different part of the same attribute vector\n                        perm = permutations[table_key]\n                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),\n                                                               var_name],\n                                    self.atr_vals[atr_type]).reshape(-1, 1)\n                        value = value.astype(v.dtype, copy=False)\n                        value[perm] = v\n                    values[atr_type].append(value)\n\n        if renamed:\n            self.Warning.renamed_vars(', '.join(renamed))\n        idas = None if self.selected_feature else np.array(all_ids)\n        table = self.merge_data(domain, values, idas)\n        if selection:\n            mask = [idx in self.selected_items for idx in all_ids]\n            return create_annotated_table(table, mask)\n        return table\n\n    def get_indices(self, table, selection):\n        \"\"\"Returns mappings of ids (be it row id or string) to indices in tables\"\"\"\n        if self.selected_feature:\n            if self.output_duplicates and selection:\n                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                           return_inverse=True)\n                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]\n            else:\n                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),\n                                       return_index=True)\n\n        else:\n            items = table.ids\n            ids = range(len(table))\n\n        if selection:\n            return {item: idx for item, idx in zip(items, ids)\n                    if item in self.selected_items}\n\n        return dict(zip(items, ids))\n\n    def get_indices_to_match_by(self, relevant_keys, selection=False):\n        dict_ = {}\n        for key in relevant_keys:\n            table = self.data[key].table\n            dict_[key] = self.get_indices(table, selection)\n        return dict_\n\n    def create_from_rows(self, relevant_ids, selection=False):\n        var_dict = {}\n        for atr_type in self.atr_types:\n            container = {}\n            for table_key in relevant_ids:\n                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)\n                atrs = getattr(self.data[table_key].table.domain, atr_type)\n                container = reduce(merge_vars, atrs, container)\n            var_dict[atr_type] = container\n        if self.output_duplicates and not selection:\n            return self.extract_rowwise_duplicates(var_dict, relevant_ids)\n        return self.extract_rowwise(var_dict, relevant_ids, selection)\n\n    def expand_table(self, table, atrs, metas, cv):\n        exp = []\n        n = 1 if isinstance(table, RowInstance) else len(table)\n        if isinstance(table, RowInstance):\n            ids = table.id.reshape(-1, 1)\n            atr_vals = self.row_vals\n        else:\n            ids = table.ids.reshape(-1, 1)\n            atr_vals = self.atr_vals\n        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):\n            cur_el = getattr(table.domain, atr_type)\n            array = np.full((n, len(all_el)), np.nan)\n            if cur_el:\n                perm = get_perm(cur_el, all_el)\n                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))\n                array = array.astype(b.dtype, copy=False)\n                array[:, perm] = b\n            exp.append(array)\n        return (*exp, ids)\n\n    def extract_rowwise_duplicates(self, var_dict, ids):\n        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))\n        sort_key = attrgetter(\"name\")\n        all_atrs = sorted(var_dict['attributes'], key=sort_key)\n        all_metas = sorted(var_dict['metas'], key=sort_key)\n        all_cv = sorted(var_dict['class_vars'], key=sort_key)\n\n        all_x, all_y, all_m = [], [], []\n        new_table_ids = []\n        for idx in all_ids:\n            #iterate trough tables with same idx\n            for table_key, t_indices in ids.items():\n                if idx not in t_indices:\n                    continue\n                map_ = t_indices[idx]\n                extracted = self.data[table_key].table[map_]\n                # pylint: disable=unbalanced-tuple-unpacking\n                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)\n                all_x.append(x)\n                all_y.append(y)\n                all_m.append(m)\n                new_table_ids.append(t_ids)\n        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}\n        values = {'attributes': [np.vstack(all_x)],\n                  'metas': [np.vstack(all_m)],\n                  'class_vars': [np.vstack(all_y)]}\n        return self.merge_data(domain, values, np.vstack(new_table_ids))\n\n    def commit(self):\n        if not self.vennwidget.vennareas() or not self.data:\n            self.Outputs.selected_data.send(None)\n            self.Outputs.annotated_data.send(None)\n            return\n\n        self.selected_items = reduce(\n            set.union, [self.disjoint[index] for index in self.selection],\n            set()\n        )\n        selected_keys = reduce(\n            set.union, [set(self.area_keys[area]) for area in self.selection],\n            set())\n        selected = None\n\n        if self.rowwise:\n            if self.selected_items:\n                selected_ids = self.get_indices_to_match_by(\n                    selected_keys, bool(self.selection))\n                selected = self.create_from_rows(selected_ids, False)\n            annotated_ids = self.get_indices_to_match_by(self.data)\n            annotated = self.create_from_rows(annotated_ids, True)\n        else:\n            annotated = self.create_from_columns(self.selected_items, self.data, False)\n            if self.selected_items:\n                selected = self.create_from_columns(self.selected_items, selected_keys, True)\n\n        self.Outputs.selected_data.send(selected)\n        self.Outputs.annotated_data.send(annotated)\n\n    def send_report(self):\n        self.report_plot()\n\n    def get_disjoint(self, sets):\n        \"\"\"\n        Return all disjoint subsets.\n        \"\"\"\n        sets = list(sets)\n        n = len(sets)\n        disjoint_sets = [None] * (2 ** n)\n        included_tables = [None] * (2 ** n)\n        for i in range(2 ** n):\n            key = setkey(i, n)\n            included = [s for s, inc in zip(sets, key) if inc]\n            if included:\n                excluded = [s for s, inc in zip(sets, key) if not inc]\n                s = reduce(set.intersection, included)\n                s = reduce(set.difference, excluded, s)\n            else:\n                s = set()\n            disjoint_sets[i] = s\n            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]\n\n        return disjoint_sets, included_tables\n\ndef string_attributes(domain):\n    \"\"\"\n    Return all string attributes from the domain.\n    \"\"\"\n    return [attr for attr in domain.variables + domain.metas if attr.is_string]\n\ndef source_attributes(domain):\n    \"\"\"\n    Return all suitable attributes for the venn diagram.\n    \"\"\"\n    return string_attributes(domain)  # + discrete_attributes(domain)\n\n\ndef disjoint_set_label(i, n, simplify=False):\n    \"\"\"\n    Return a html formated label for a disjoint set indexed by `i`.\n    \"\"\"\n    intersection = unicodedata.lookup(\"INTERSECTION\")\n    # comp = unicodedata.lookup(\"COMPLEMENT\")  #\n    # This depends on the font but the unicode complement in\n    # general does not look nice in a super script so we use\n    # plain c instead.\n    comp = \"c\"\n\n    def label_for_index(i):\n        return chr(ord(\"A\") + i)\n\n    if simplify:\n        return \"\".join(label_for_index(i) for i, b in enumerate(setkey(i, n))\n                       if b)\n    else:\n        return intersection.join(label_for_index(i) +\n                                 (\"\" if b else \"<sup>\" + comp + \"</sup>\")\n                                 for i, b in enumerate(setkey(i, n)))\n\n\nclass VennSetItem(QGraphicsPathItem):\n    def __init__(self, parent=None, text=\"\", informativeText=\"\"):\n        super(VennSetItem, self).__init__(parent)\n        # Plain text title (editable by the VennDiagram)\n        self.text = text\n        # Extra informative text (possibly rich text)\n        self.informativeText = informativeText\n\n# TODO: Use palette's selected/highligted text / background colors to\n# indicate selection\n\n\nclass VennIntersectionArea(QGraphicsPathItem):\n    def __init__(self, parent=None, text=\"\"):\n        super().__init__(parent)\n        self.setAcceptHoverEvents(True)\n        self.setPen(QPen(Qt.NoPen))\n\n        self.text = QGraphicsTextItem(self)\n        layout = self.text.document().documentLayout()\n        layout.documentSizeChanged.connect(self._onLayoutChanged)\n\n        self._text = text\n        self._anchor = QPointF()\n\n    def setText(self, text):\n        if self._text != text:\n            self._text = text\n            self.text.setPlainText(text)\n\n    def setTextAnchor(self, pos):\n        if self._anchor != pos:\n            self._anchor = pos\n            self._updateTextAnchor()\n\n    def hoverEnterEvent(self, event):\n        self.setZValue(self.zValue() + 1)\n        return QGraphicsPathItem.hoverEnterEvent(self, event)\n\n    def hoverLeaveEvent(self, event):\n        self.setZValue(self.zValue() - 1)\n        return QGraphicsPathItem.hoverLeaveEvent(self, event)\n\n    def mousePressEvent(self, event):\n        if event.button() == Qt.LeftButton:\n            if event.modifiers() & Qt.AltModifier:\n                self.setSelected(False)\n            elif event.modifiers() & Qt.ControlModifier:\n                self.setSelected(not self.isSelected())\n            elif event.modifiers() & Qt.ShiftModifier:\n                self.setSelected(True)\n            else:\n                for area in self.parentWidget().vennareas():\n                    area.setSelected(False)\n                self.setSelected(True)\n\n    def mouseReleaseEvent(self, event):\n        pass\n\n    def paint(self, painter, option, _widget=None):\n        painter.save()\n        path = self.path()\n        brush = QBrush(self.brush())\n        pen = QPen(self.pen())\n\n        if option.state & QStyle.State_Selected:\n            pen.setColor(Qt.red)\n            brush.setStyle(Qt.DiagCrossPattern)\n            brush.setColor(QColor(40, 40, 40, 100))\n\n        elif option.state & QStyle.State_MouseOver:\n            pen.setColor(Qt.blue)\n\n        if option.state & QStyle.State_MouseOver:\n            brush.setColor(QColor(100, 100, 100, 100))\n            if brush.style() == Qt.NoBrush:\n                # Make sure the highlight is actually visible.\n                brush.setStyle(Qt.SolidPattern)\n\n        painter.setPen(pen)\n        painter.setBrush(brush)\n        painter.drawPath(path)\n        painter.restore()\n\n    def itemChange(self, change, value):\n        if change == QGraphicsPathItem.ItemSelectedHasChanged:\n            self.setZValue(self.zValue() + (1 if value else -1))\n        return QGraphicsPathItem.itemChange(self, change, value)\n\n    def _updateTextAnchor(self):\n        rect = self.text.boundingRect()\n        pos = anchor_rect(rect, self._anchor)\n        self.text.setPos(pos)\n\n    def _onLayoutChanged(self):\n        self._updateTextAnchor()\n\n\nclass GraphicsTextEdit(QGraphicsTextItem):\n    #: Edit triggers\n    NoEditTriggers, DoubleClicked = 0, 1\n\n    editingFinished = Signal()\n    editingStarted = Signal()\n\n    documentSizeChanged = Signal()\n\n    def __init__(self, *args, **kwargs):\n        super(GraphicsTextEdit, self).__init__(*args, **kwargs)\n        self.setCursor(Qt.IBeamCursor)\n        self.setTabChangesFocus(True)\n        self._edittrigger = GraphicsTextEdit.DoubleClicked\n        self._editing = False\n        self.document().documentLayout().documentSizeChanged.connect(\n            self.documentSizeChanged\n        )\n\n    def mouseDoubleClickEvent(self, event):\n        super(GraphicsTextEdit, self).mouseDoubleClickEvent(event)\n        if self._edittrigger == GraphicsTextEdit.DoubleClicked:\n            self._start()\n\n    def focusOutEvent(self, event):\n        super(GraphicsTextEdit, self).focusOutEvent(event)\n\n        if self._editing:\n            self._end()\n\n    def _start(self):\n        self._editing = True\n        self.setTextInteractionFlags(Qt.TextEditorInteraction)\n        self.setFocus(Qt.MouseFocusReason)\n        self.editingStarted.emit()\n\n    def _end(self):\n        self._editing = False\n        self.setTextInteractionFlags(Qt.NoTextInteraction)\n        self.editingFinished.emit()\n\n\nclass VennDiagram(QGraphicsWidget):\n    # rect and petal are for future work\n    Circle, Ellipse, Rect, Petal = 1, 2, 3, 4\n\n    TitleFormat = \"<center><h4>{0}</h4>{1}</center>\"\n\n    selectionChanged = Signal()\n    itemTextEdited = Signal(int, str)\n\n    def __init__(self, parent=None):\n        super(VennDiagram, self).__init__(parent)\n        self.shapeType = VennDiagram.Circle\n        self._items = []\n        self._vennareas = []\n        self._textitems = []\n        self._subsettextitems = []\n        self._textanchors = []\n\n    def item(self, index):\n        return self._items[index]\n\n    def items(self):\n        return list(self._items)\n\n    def count(self):\n        return len(self._items)\n\n    def setItems(self, items):\n        if self._items:\n            self.clear()\n\n        self._items = list(items)\n\n        for item in self._items:\n            item.setParentItem(self)\n            item.setVisible(True)\n\n        fmt = self.TitleFormat.format\n\n        font = self.font()\n        font.setPixelSize(14)\n\n        for item in items:\n            text = GraphicsTextEdit(self)\n            text.setFont(font)\n            text.setDefaultTextColor(QColor(\"#333\"))\n            text.setHtml(fmt(escape(item.text), item.informativeText))\n            text.adjustSize()\n            text.editingStarted.connect(self._on_editingStarted)\n            text.editingFinished.connect(self._on_editingFinished)\n            text.documentSizeChanged.connect(\n                self._on_itemTextSizeChanged\n            )\n\n            self._textitems.append(text)\n\n        self._vennareas = [\n            VennIntersectionArea(parent=self)\n            for i in range(2 ** len(items))\n        ]\n        self._subsettextitems = [\n            QGraphicsTextItem(parent=self)\n            for i in range(2 ** len(items))\n        ]\n\n        self._updateLayout()\n\n    def clear(self):\n        scene = self.scene()\n        items = self.vennareas() + list(self.items()) + self._textitems\n\n        for item in self._textitems:\n            item.editingStarted.disconnect(self._on_editingStarted)\n            item.editingFinished.disconnect(self._on_editingFinished)\n            item.documentSizeChanged.disconnect(\n                self._on_itemTextSizeChanged\n            )\n\n        self._items = []\n        self._vennareas = []\n        self._textitems = []\n        self._subsettextitems = []\n        self._textanchors = []\n\n        for item in items:\n            item.setVisible(False)\n            item.setParentItem(None)\n            if scene is not None:\n                scene.removeItem(item)\n\n    def vennareas(self):\n        return list(self._vennareas)\n\n    def setFont(self, font):\n        if font != self.font():\n            self.prepareGeometryChange()\n            super().setFont(font)\n\n            for item in self.items():\n                item.setFont(font)\n\n    def _updateLayout(self):\n        rect = self.geometry()\n        n = len(self._items)\n        if not n:\n            return\n\n        regions = venn_diagram(n)\n\n        # The y axis in Qt points downward\n        transform = QTransform().scale(1, -1)\n        regions = list(map(transform.map, regions))\n\n        union_brect = reduce(QRectF.united,\n                             (path.boundingRect() for path in regions))\n\n        scalex = rect.width() / union_brect.width()\n        scaley = rect.height() / union_brect.height()\n        scale = min(scalex, scaley)\n\n        transform = QTransform().scale(scale, scale)\n\n        regions = [transform.map(path) for path in regions]\n\n        center = rect.width() / 2, rect.height() / 2\n        for item, path in zip(self.items(), regions):\n            item.setPath(path)\n            item.setPos(*center)\n\n        intersections = venn_intersections(regions)\n        assert len(intersections) == 2 ** n\n        assert len(self.vennareas()) == 2 ** n\n\n        anchors = [(0, 0)] + subset_anchors(self._items)\n\n        anchor_transform = QTransform().scale(rect.width(), -rect.height())\n        for i, area in enumerate(self.vennareas()):\n            area.setPath(intersections[setkey(i, n)])\n            area.setPos(*center)\n            x, y = anchors[i]\n            anchor = anchor_transform.map(QPointF(x, y))\n            area.setTextAnchor(anchor)\n            area.setZValue(30)\n\n        self._updateTextAnchors()\n\n    def _updateTextAnchors(self):\n        n = len(self._items)\n\n        items = self._items\n        dist = 15\n\n        shape = reduce(QPainterPath.united, [item.path() for item in items])\n        brect = shape.boundingRect()\n        bradius = max(brect.width() / 2, brect.height() / 2)\n\n        center = self.boundingRect().center()\n\n        anchors = _category_anchors(items)\n        self._textanchors = []\n        for angle, anchor_h, anchor_v in anchors:\n            line = QLineF.fromPolar(bradius, angle)\n            ext = QLineF.fromPolar(dist, angle)\n            line = QLineF(line.p1(), line.p2() + ext.p2())\n            line = line.translated(center)\n\n            anchor_pos = line.p2()\n            self._textanchors.append((anchor_pos, anchor_h, anchor_v))\n\n        for i in range(n):\n            self._updateTextItemPos(i)\n\n    def _updateTextItemPos(self, i):\n        item = self._textitems[i]\n        anchor_pos, anchor_h, anchor_v = self._textanchors[i]\n        rect = item.boundingRect()\n        pos = anchor_rect(rect, anchor_pos, anchor_h, anchor_v)\n        item.setPos(pos)\n\n    def setGeometry(self, geometry):\n        super(VennDiagram, self).setGeometry(geometry)\n        self._updateLayout()\n\n    def _on_editingStarted(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        text = self._items[index].text\n        item.setTextWidth(-1)\n        item.setHtml(self.TitleFormat.format(escape(text), \"<br/>\"))\n\n    def _on_editingFinished(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        text = item.toPlainText()\n        if text != self._items[index].text:\n            self._items[index].text = text\n\n            self.itemTextEdited.emit(index, text)\n\n        item.setHtml(\n            self.TitleFormat.format(\n                escape(text), self._items[index].informativeText))\n        item.adjustSize()\n\n    def _on_itemTextSizeChanged(self):\n        item = self.sender()\n        index = self._textitems.index(item)\n        self._updateTextItemPos(index)\n\n\ndef anchor_rect(rect, anchor_pos,\n                anchor_h=Qt.AnchorHorizontalCenter,\n                anchor_v=Qt.AnchorVerticalCenter):\n\n    if anchor_h == Qt.AnchorLeft:\n        x = anchor_pos.x()\n    elif anchor_h == Qt.AnchorHorizontalCenter:\n        x = anchor_pos.x() - rect.width() / 2\n    elif anchor_h == Qt.AnchorRight:\n        x = anchor_pos.x() - rect.width()\n    else:\n        raise ValueError(anchor_h)\n\n    if anchor_v == Qt.AnchorTop:\n        y = anchor_pos.y()\n    elif anchor_v == Qt.AnchorVerticalCenter:\n        y = anchor_pos.y() - rect.height() / 2\n    elif anchor_v == Qt.AnchorBottom:\n        y = anchor_pos.y() - rect.height()\n    else:\n        raise ValueError(anchor_v)\n\n    return QPointF(x, y)\n\n\ndef radians(angle):\n    return 2 * math.pi * angle / 360\n\n\ndef unit_point(x, r=1.0):\n    x = radians(x)\n    return (r * math.cos(x), r * math.sin(x))\n\n\ndef _category_anchors(shapes):\n    n = len(shapes)\n    return _CATEGORY_ANCHORS[n - 1]\n\n\n# (angle, horizontal anchor, vertical anchor)\n_CATEGORY_ANCHORS = (\n    # n == 1\n    ((90, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),),\n    # n == 2\n    ((180, Qt.AnchorRight, Qt.AnchorVerticalCenter),\n     (0, Qt.AnchorLeft, Qt.AnchorVerticalCenter)),\n    # n == 3\n    ((150, Qt.AnchorRight, Qt.AnchorBottom),\n     (30, Qt.AnchorLeft, Qt.AnchorBottom),\n     (270, Qt.AnchorHorizontalCenter, Qt.AnchorTop)),\n    # n == 4\n    ((270 + 45, Qt.AnchorLeft, Qt.AnchorTop),\n     (270 - 45, Qt.AnchorRight, Qt.AnchorTop),\n     (90 - 15, Qt.AnchorLeft, Qt.AnchorBottom),\n     (90 + 15, Qt.AnchorRight, Qt.AnchorBottom)),\n    # n == 5\n    ((90 - 5, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),\n     (18 - 5, Qt.AnchorLeft, Qt.AnchorVerticalCenter),\n     (306 - 5, Qt.AnchorLeft, Qt.AnchorTop),\n     (234 - 5, Qt.AnchorRight, Qt.AnchorTop),\n     (162 - 5, Qt.AnchorRight, Qt.AnchorVerticalCenter),)\n)\n\n\ndef subset_anchors(shapes):\n    n = len(shapes)\n    if n == 1:\n        return [(0, 0)]\n    elif n == 2:\n        return [unit_point(180, r=1/3),\n                unit_point(0, r=1/3),\n                (0, 0)]\n    elif n == 3:\n        return [unit_point(150, r=0.35),  # A\n                unit_point(30, r=0.35),   # B\n                unit_point(90, r=0.27),   # AB\n                unit_point(270, r=0.35),  # C\n                unit_point(210, r=0.27),  # AC\n                unit_point(330, r=0.27),  # BC\n                unit_point(0, r=0),]      # ABC\n    elif n == 4:\n        anchors = [\n            (0.400, 0.110),    # A\n            (-0.400, 0.110),   # B\n            (0.000, -0.285),   # AB\n            (0.180, 0.330),    # C\n            (0.265, 0.205),    # AC\n            (-0.240, -0.110),  # BC\n            (-0.100, -0.190),  # ABC\n            (-0.180, 0.330),   # D\n            (0.240, -0.110),   # AD\n            (-0.265, 0.205),   # BD\n            (0.100, -0.190),   # ABD\n            (0.000, 0.250),    # CD\n            (0.153, 0.090),    # ACD\n            (-0.153, 0.090),   # BCD\n            (0.000, -0.060),   # ABCD\n        ]\n        return anchors\n\n    elif n == 5:\n        anchors = [None] * 32\n        # Base anchors\n        A = (0.033, 0.385)\n        AD = (0.095, 0.250)\n        AE = (-0.100, 0.265)\n        ACE = (-0.130, 0.220)\n        ADE = (0.010, 0.225)\n        ACDE = (-0.095, 0.175)\n        ABCDE = (0.0, 0.0)\n\n        anchors[-1] = ABCDE\n\n        bases = [(0b00001, A),\n                 (0b01001, AD),\n                 (0b10001, AE),\n                 (0b10101, ACE),\n                 (0b11001, ADE),\n                 (0b11101, ACDE)]\n\n        for i in range(5):\n            for index, anchor in bases:\n                index = bit_rot_left(index, i, bits=5)\n                assert anchors[index] is None\n                anchors[index] = rotate_point(anchor, - 72 * i)\n\n        assert all(anchors[1:])\n        return anchors[1:]\n    return None\n\n\ndef bit_rot_left(x, y, bits=32):\n    mask = 2 ** bits - 1\n    x_masked = x & mask\n    return (x << y) & mask | (x_masked >> bits - y)\n\n\ndef rotate_point(p, angle):\n    r = radians(angle)\n    R = np.array([[math.cos(r), -math.sin(r)],\n                  [math.sin(r), math.cos(r)]])\n    x, y = np.dot(R, p)\n    return (float(x), float(y))\n\n\ndef line_extended(line, distance):\n    \"\"\"\n    Return an QLineF extended by `distance` units in the positive direction.\n    \"\"\"\n    angle = line.angle() / 360 * 2 * math.pi\n    dx, dy = unit_point(angle, r=distance)\n    return QLineF(line.p1(), line.p2() + QPointF(dx, dy))\n\n\ndef circle_path(center, r=1.0):\n    return ellipse_path(center, r, r, rotation=0)\n\n\ndef ellipse_path(center, a, b, rotation=0):\n    if not isinstance(center, QPointF):\n        center = QPointF(*center)\n\n    brect = QRectF(-a, -b, 2 * a, 2 * b)\n\n    path = QPainterPath()\n    path.addEllipse(brect)\n\n    if rotation != 0:\n        transform = QTransform().rotate(rotation)\n        path = transform.map(path)\n\n    path.translate(center)\n    return path\n\n\n# TODO: Should include anchors for text layout (both inside and outside).\n# for each item {path: QPainterPath,\n#                text_anchors: [{center}] * (2 ** n)\n#                mayor_axis: QLineF,\n#                boundingRect QPolygonF (with 4 vertices)}\n#\n# Should be a new class with overloads for ellipse/circle, rect, and petal\n# shapes, should store all constructor parameters, rotation, center,\n# mayor/minor axis.\n\n\ndef venn_diagram(n):\n    if n < 1 or n > 5:\n        raise ValueError()\n\n    paths = []\n\n    if n == 1:\n        paths = [circle_path(center=(0, 0), r=0.5)]\n    elif n == 2:\n        angles = [180, 0]\n        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)\n                 for x in angles]\n    elif n == 3:\n        angles = [150 - 120 * i for i in range(3)]\n        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)\n                 for x in angles]\n    elif n == 4:\n        # Constants shamelessly stolen from VennDiagram R package\n        paths = [\n            ellipse_path((0.65 - 0.5, 0.47 - 0.5), 0.35, 0.20, 45),\n            ellipse_path((0.35 - 0.5, 0.47 - 0.5), 0.35, 0.20, 135),\n            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 45),\n            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 134),\n        ]\n    elif n == 5:\n        # Constants shamelessly stolen from VennDiagram R package\n        d = 0.13\n        a, b = 0.24, 0.48\n        a, b = b, a\n        a, b = 0.48, 0.24\n        paths = [ellipse_path(unit_point((1 - i) * 72, r=d),\n                              a, b, rotation=90 - (i * 72))\n                 for i in range(5)]\n\n    return paths\n\n\ndef setkey(intval, n):\n    return tuple(bool(intval & (2 ** i)) for i in range(n))\n\n\ndef keyrange(n):\n    if n < 0:\n        raise ValueError()\n\n    for i in range(2 ** n):\n        yield setkey(i, n)\n\n\ndef venn_intersections(paths):\n    n = len(paths)\n    return {key: venn_intersection(paths, key) for key in keyrange(n)}\n\n\ndef venn_intersection(paths, key):\n    if not any(key):\n        return QPainterPath()\n\n    # first take the intersection of all included paths\n    path = reduce(QPainterPath.intersected,\n                  (path for path, included in zip(paths, key) if included))\n\n    # subtract all the excluded sets (i.e. take the intersection\n    # with the excluded set complements)\n    path = reduce(QPainterPath.subtracted,\n                  (path for path, included in zip(paths, key) if not included),\n                  path)\n\n    return path\n\n\ndef append_column(data, where, variable, column):\n    X, Y, M = data.X, data.Y, data.metas\n    domain = data.domain\n    attr = domain.attributes\n    class_vars = domain.class_vars\n    metas = domain.metas\n    if where == \"X\":\n        attr = attr + (variable,)\n        X = np.hstack((X, column))\n    elif where == \"Y\":\n        class_vars = class_vars + (variable,)\n        Y = np.hstack((Y, column))\n    elif where == \"M\":\n        metas = metas + (variable,)\n        M = np.hstack((M, column))\n    else:\n        raise ValueError\n    domain = Domain(attr, class_vars, metas)\n    new_data = data.transform(domain)\n    new_data[:, variable] = column\n    return new_data\n\ndef arrays_equal(a, b, type_):\n    \"\"\"\n    checks if arrays have nans in same places and if not-nan elements\n    are equal\n    \"\"\"\n    if a is None and b is None:\n        return True\n    if a is None or b is None:\n        return False\n    if type_ is not StringVariable:\n        nana = np.isnan(a)\n        nanb = np.isnan(b)\n        return np.all(nana == nanb) and np.all(a[~nana] == b[~nanb])\n    else:\n        return np.all(a == b)\n\ndef pad_columns(values, mask, l):\n    #inflates columns with nans\n    a = np.full((l, 1), np.nan, dtype=values.dtype)\n    a[mask] = values.reshape(-1, 1)\n    return a\n\ndef get_perm(ids, all_ids):\n    return [all_ids.index(el) for el in ids if el in all_ids]\n\n\ndef main():  # pragma: no cover\n    # pylint: disable=import-outside-toplevel\n    from Orange.evaluation import ShuffleSplit\n\n    data = Table(\"brown-selected\")\n\n    if not \"test_rows\":  # change to `if not \"test_rows\" to test columns\n        data = append_column(data, \"M\", StringVariable(\"Test\"),\n                             (np.arange(len(data)).reshape(-1, 1) % 30).astype(str))\n        res = ShuffleSplit(n_resamples=5, test_size=0.7, stratified=False)\n        indices = iter(res.get_indices(data))\n        datasets = []\n        for i in range(1, 6):\n            sample, _ = next(indices)\n            data1 = data[sample]\n            data1.name = chr(ord(\"A\") + i)\n            datasets.append((data1, i))\n    else:\n        domain = data.domain\n        data1 = data.transform(Domain(domain.attributes[:15], domain.class_var))\n        data2 = data.transform(Domain(domain.attributes[10:], domain.class_var))\n        datasets = [(data1, 1), (data2, 2)]\n\n    WidgetPreview(OWVennDiagram).run(setData=datasets)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    main()\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]

**Title:** Venn Diagram Crashes When Handling Tables Without Attributes

**Description:**
When using the Venn Diagram widget with a table that has no attributes, the application crashes unexpectedly.

**Example Buggy Code:**
```python
def test_no_attributes(self):
    domain = Domain([], class_vars=self.data.domain.attributes)
    n = len(self.data)
    table = Table.from_numpy(domain, np.empty((n, 0)), self.data.X)
    self.widget.rowwise = True
    self.send_signal(self.signal_name, table, 1)
```

In this test scenario, a table is created with no attribute columns. When this table is sent to the Venn Diagram widget, it fails to handle the absence of attributes properly.

**Expected Behavior:**
The Venn Diagram widget should handle tables without attributes gracefully without crashing. It should either display an empty diagram or provide a meaningful message indicating that there are no attributes to display.

**Actual Behavior:**
The application crashes with the following error:
```
TypeError: __init__() missing 1 required positional argument: 'attributes'
```
This indicates that the widget does not properly initialize when there are no attributes in the input table.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/visualize/owvenndiagram.py
"""
Venn Diagram Widget
-------------------

"""

import math
import unicodedata
from collections import namedtuple, defaultdict
from itertools import compress, count
from functools import reduce
from operator import attrgetter
from xml.sax.saxutils import escape

import numpy as np

from AnyQt.QtWidgets import (
    QGraphicsScene, QGraphicsView, QGraphicsWidget,
    QGraphicsPathItem, QGraphicsTextItem, QStyle, QSizePolicy
)
from AnyQt.QtGui import (
    QPainterPath, QPainter, QTransform, QColor, QBrush, QPen, QPalette
)
from AnyQt.QtCore import Qt, QPointF, QRectF, QLineF
from AnyQt.QtCore import pyqtSignal as Signal

from Orange.data import Table, Domain, StringVariable, RowInstance
from Orange.data.util import get_unique_names_duplicates
from Orange.widgets import widget, gui, settings
from Orange.widgets.utils import itemmodels, colorpalettes
from Orange.widgets.utils.annotated_data import (create_annotated_table,
                                                 ANNOTATED_DATA_SIGNAL_NAME)
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output, Msg


_InputData = namedtuple("_InputData", ["key", "name", "table"])
_ItemSet = namedtuple("_ItemSet", ["key", "name", "title", "items"])


class OWVennDiagram(widget.OWWidget):
    name = "Venn Diagram"
    description = "A graphical visualization of the overlap of data instances " \
                  "from a collection of input datasets."
    icon = "icons/VennDiagram.svg"
    priority = 280
    keywords = []
    settings_version = 2

    class Inputs:
        data = Input("Data", Table, multiple=True)

    class Outputs:
        selected_data = Output("Selected Data", Table, default=True)
        annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)

    class Error(widget.OWWidget.Error):
        instances_mismatch = Msg("Data sets do not contain the same instances.")
        too_many_inputs = Msg("Venn diagram accepts at most five datasets.")

    class Warning(widget.OWWidget.Warning):
        renamed_vars = Msg("Some variables have been renamed "
                           "to avoid duplicates.\n{}")

    selection: list

    settingsHandler = settings.DomainContextHandler()
    # Indices of selected disjoint areas
    selection = settings.Setting([], schema_only=True)
    #: Output unique items (one output row for every unique instance `key`)
    #: or preserve all duplicates in the output.
    output_duplicates = settings.Setting(False)
    autocommit = settings.Setting(True)
    rowwise = settings.Setting(True)
    selected_feature = settings.ContextSetting(None)

    want_control_area = False
    graph_name = "scene"
    atr_types = ['attributes', 'metas', 'class_vars']
    atr_vals = {'metas': 'metas', 'attributes': 'X', 'class_vars': 'Y'}
    row_vals = {'attributes': 'x', 'class_vars': 'y', 'metas': 'metas'}

    def __init__(self):
        super().__init__()

        # Diagram update is in progress
        self._updating = False
        # Input update is in progress
        self._inputUpdate = False
        # Input datasets in the order they were 'connected'.
        self.data = {}
        # Extracted input item sets in the order they were 'connected'
        self.itemsets = {}
        # A list with 2 ** len(self.data) elements that store item sets
        # belonging to each area
        self.disjoint = []
        # A list with  2 ** len(self.data) elements that store keys of tables
        # intersected in each area
        self.area_keys = []

        # Main area view
        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)
        self.view.setRenderHint(QPainter.Antialiasing)
        self.view.setBackgroundRole(QPalette.Window)
        self.view.setFrameStyle(QGraphicsView.StyledPanel)

        self.mainArea.layout().addWidget(self.view)
        self.vennwidget = VennDiagram()
        self._resize()
        self.vennwidget.itemTextEdited.connect(self._on_itemTextEdited)
        self.scene.selectionChanged.connect(self._on_selectionChanged)

        self.scene.addItem(self.vennwidget)

        controls = gui.hBox(self.mainArea)
        box = gui.radioButtonsInBox(
            controls, self, 'rowwise',
            ["Columns (features)", "Rows (instances), matched by", ],
            box="Elements", callback=self._on_matching_changed
        )
        gui.comboBox(
            gui.indentedBox(box), self, "selected_feature",
            model=itemmodels.VariableListModel(placeholder="Instance identity"),
            callback=self._on_inputAttrActivated
            )
        box.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)

        self.outputs_box = box = gui.vBox(controls, "Output")
        self.output_duplicates_cb = gui.checkBox(
            box, self, "output_duplicates", "Output duplicates",
            callback=lambda: self.commit())  # pylint: disable=unnecessary-lambda
        gui.auto_send(box, self, "autocommit", box=False)
        self.output_duplicates_cb.setEnabled(bool(self.rowwise))
        self._queue = []

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self._resize()

    def showEvent(self, event):
        super().showEvent(event)
        self._resize()

    def _resize(self):
        # vennwidget draws so that the diagram fits into its geometry,
        # while labels take further 120 pixels, hence -120 in below formula
        size = max(200, min(self.view.width(), self.view.height()) - 120)
        self.vennwidget.resize(size, size)
        self.scene.setSceneRect(self.scene.itemsBoundingRect())

    @Inputs.data
    @check_sql_input
    def setData(self, data, key=None):
        self.Error.too_many_inputs.clear()
        if not self._inputUpdate:
            self._inputUpdate = True
        if key in self.data:
            if data is None:
                # Remove the input
                # Clear possible warnings.
                self.Warning.clear()
                del self.data[key]
            else:
                # Update existing item
                self.data[key] = self.data[key]._replace(name=data.name, table=data)

        elif data is not None:
            # TODO: Allow setting more them 5 inputs and let the user
            # select the 5 to display.
            if len(self.data) == 5:
                self.Error.too_many_inputs()
                return
            # Add a new input
            self.data[key] = _InputData(key, data.name, data)
        self._setInterAttributes()

    def data_equality(self):
        """ Checks if all input datasets have same ids. """
        if not self.data.values():
            return True
        sets = []
        for val in self.data.values():
            sets.append(set(val.table.ids))
        inter = reduce(set.intersection, sets)
        return len(inter) == max(map(len, sets))

    def settings_compatible(self):
        self.Error.instances_mismatch.clear()
        if not self.rowwise:
            if not self.data_equality():
                self.vennwidget.clear()
                self.Error.instances_mismatch()
                self.itemsets = {}
                return False
        return True

    def handleNewSignals(self):
        self._inputUpdate = False
        self.vennwidget.clear()
        if not self.settings_compatible():
            self.invalidateOutput()
            return

        self._createItemsets()
        self._createDiagram()
        # If autocommit is enabled, _createDiagram already outputs data
        # If not, call unconditional_commit from here
        if not self.autocommit:
            self.unconditional_commit()

        self._updateInfo()
        super().handleNewSignals()

    def intersectionStringAttrs(self):
        sets = [set(string_attributes(data_.table.domain)) for data_ in self.data.values()]
        if sets:
            return reduce(set.intersection, sets)
        return set()

    def _setInterAttributes(self):
        model = self.controls.selected_feature.model()
        model[:] = [None] + list(self.intersectionStringAttrs())
        if self.selected_feature:
            names = (var.name for var in model if var)
            if self.selected_feature.name not in names:
                self.selected_feature = model[0]

    def _itemsForInput(self, key):
        """
        Calculates input for venn diagram, according to user's settings.
        """
        table = self.data[key].table
        attr = self.selected_feature
        if attr:
            return [str(inst[attr]) for inst in table
                    if not np.isnan(inst[attr])]
        else:
            return list(table.ids)

    def _createItemsets(self):
        """
        Create itemsets over rows or columns (domains) of input tables.
        """
        olditemsets = dict(self.itemsets)
        self.itemsets.clear()

        for key, input_ in self.data.items():
            if self.rowwise:
                items = self._itemsForInput(key)
            else:
                items = [el.name for el in input_.table.domain.attributes]
            name = input_.name
            if key in olditemsets and olditemsets[key].name == name:
                # Reuse the title (which might have been changed by the user)
                title = olditemsets[key].title
            else:
                title = name

            itemset = _ItemSet(key=key, name=name, title=title, items=items)
            self.itemsets[key] = itemset

    def _createDiagram(self):
        self._updating = True

        oldselection = list(self.selection)

        n = len(self.itemsets)
        self.disjoint, self.area_keys = \
            self.get_disjoint(set(s.items) for s in self.itemsets.values())

        vennitems = []
        colors = colorpalettes.LimitedDiscretePalette(n, force_hsv=True)

        for i, item in enumerate(self.itemsets.values()):
            cnt = len(set(item.items))
            cnt_all = len(item.items)
            if cnt != cnt_all:
                fmt = '{} <i>(all: {})</i>'
            else:
                fmt = '{}'
            counts = fmt.format(cnt, cnt_all)
            gr = VennSetItem(text=item.title, informativeText=counts)
            color = colors[i]
            color.setAlpha(100)
            gr.setBrush(QBrush(color))
            gr.setPen(QPen(Qt.NoPen))
            vennitems.append(gr)

        self.vennwidget.setItems(vennitems)

        for i, area in enumerate(self.vennwidget.vennareas()):
            area_items = list(map(str, list(self.disjoint[i])))
            if i:
                area.setText("{0}".format(len(area_items)))

            label = disjoint_set_label(i, n, simplify=False)
            head = "<h4>|{}| = {}</h4>".format(label, len(area_items))
            if len(area_items) > 32:
                items_str = ", ".join(map(escape, area_items[:32]))
                hidden = len(area_items) - 32
                tooltip = ("{}<span>{}, ...</br>({} items not shown)<span>"
                           .format(head, items_str, hidden))
            elif area_items:
                tooltip = "{}<span>{}</span>".format(
                    head,
                    ", ".join(map(escape, area_items))
                )
            else:
                tooltip = head

            area.setToolTip(tooltip)

            area.setPen(QPen(QColor(10, 10, 10, 200), 1.5))
            area.setFlag(QGraphicsPathItem.ItemIsSelectable, True)
            area.setSelected(i in oldselection)

        self._updating = False
        self._on_selectionChanged()

    def _updateInfo(self):
        # Clear all warnings
        self.warning()

        if self.selected_feature is None:
            no_idx = ["#{}".format(i + 1)
                      for i, key in enumerate(self.data)
                      if not source_attributes(self.data[key].table.domain)]
            if len(no_idx) == 1:
                self.warning("Dataset {} has no suitable identifiers."
                             .format(no_idx[0]))
            elif len(no_idx) > 1:
                self.warning("Datasets {} and {} have no suitable identifiers."
                             .format(", ".join(no_idx[:-1]), no_idx[-1]))

    def _on_selectionChanged(self):
        if self._updating:
            return

        areas = self.vennwidget.vennareas()
        self.selection = [i for i, area in enumerate(areas) if area.isSelected()]
        self.invalidateOutput()

    def _on_matching_changed(self):
        self.output_duplicates_cb.setEnabled(bool(self.rowwise))
        if not self.settings_compatible():
            self.invalidateOutput()
            return
        self._createItemsets()
        self._createDiagram()
        self._updateInfo()

    def _on_inputAttrActivated(self):
        self.rowwise = 1
        self._on_matching_changed()

    def _on_itemTextEdited(self, index, text):
        text = str(text)
        key = list(self.itemsets)[index]
        self.itemsets[key] = self.itemsets[key]._replace(title=text)

    def invalidateOutput(self):
        self.commit()

    def merge_data(self, domain, values, ids=None):
        X, metas, class_vars = None, None, None
        renamed = []
        for val in domain.values():
            names = [var.name for var in val]
            unique_names = get_unique_names_duplicates(names)
            for n, u, idx, var in zip(names, unique_names, count(), val):
                if n != u:
                    val[idx] = var.copy(name=u)
                    renamed.append(n)
        if renamed:
            self.Warning.renamed_vars(', '.join(renamed))
        if 'attributes' in values:
            X = np.hstack(values['attributes'])
        if 'metas' in values:
            metas = np.hstack(values['metas'])
        if 'class_vars' in values:
            class_vars = np.hstack(values['class_vars'])
        table = Table.from_numpy(Domain(**domain), X, class_vars, metas)
        if ids is not None:
            table.ids = ids
        return table

    def extract_columnwise(self, var_dict, columns=None):
        #for columns
        domain = defaultdict(list)
        values = defaultdict(list)
        renamed = []
        for atr_type, vars_dict in var_dict.items():
            for var_name, var_data in vars_dict.items():
                is_selected = bool(columns) and var_name.name in columns
                if var_data[0]:
                    #columns are different, copy all, rename them
                    for var, table_key in var_data[1]:
                        idx = list(self.data).index(table_key) + 1
                        new_atr = var.copy(name=f'{var_name.name} ({idx})')
                        if columns and atr_type == 'attributes':
                            new_atr.attributes['Selected'] = is_selected
                        domain[atr_type].append(new_atr)
                        renamed.append(var_name.name)
                        values[atr_type].append(getattr(self.data[table_key].table[:, var_name],
                                                        self.atr_vals[atr_type])
                                                .reshape(-1, 1))
                else:
                    new_atr = var_data[1][0][0].copy()
                    if columns and atr_type == 'attributes':
                        new_atr.attributes['Selected'] = is_selected
                    domain[atr_type].append(new_atr)
                    values[atr_type].append(getattr(self.data[var_data[1][0][1]].table[:, var_name],
                                                    self.atr_vals[atr_type])
                                            .reshape(-1, 1))
        if renamed:
            self.Warning.renamed_vars(', '.join(renamed))
        return self.merge_data(domain, values)

    def curry_merge(self, table_key, atr_type, ids=None, selection=False):
        if self.rowwise:
            check_equality = self.arrays_equal_rows
        else:
            check_equality = self.arrays_equal_cols

        def inner(new_atrs, atr):
            """
            Atrs - list of variables we wish to merge
            new_atrs - dictionary where key is old var, val
                is [is_different:bool, table_keys:list]), is_different is set to True,
                if we are outputing duplicates, but the value is arbitrary
            """
            if atr in new_atrs:
                if not selection and self.output_duplicates:
                    #if output_duplicates, we just check if compute value is the same
                    new_atrs[atr][0] = True
                elif not new_atrs[atr][0]:
                    for var, key in new_atrs[atr][1]:
                        if not check_equality(table_key,
                                              key,
                                              atr.name,
                                              self.atr_vals[atr_type],
                                              type(var), ids):
                            new_atrs[atr][0] = True
                            break
                new_atrs[atr][1].append((atr, table_key))
            else:
                new_atrs[atr] = [False, [(atr, table_key)]]
            return new_atrs
        return inner

    def arrays_equal_rows(self, key1, key2, name, data_type, type_, ids):
        #gets masks, compares same as cols
        t1 = self.data[key1].table
        t2 = self.data[key2].table
        inter_val = set(ids[key1]) & set(ids[key2])
        t1_inter = [ids[key1][val] for val in inter_val]
        t2_inter = [ids[key2][val] for val in inter_val]
        return arrays_equal(
            getattr(t1[t1_inter, name],
                    data_type).reshape(-1, 1),
            getattr(t2[t2_inter, name],
                    data_type).reshape(-1, 1),
            type_)

    def arrays_equal_cols(self, key1, key2, name, data_type, type_, _ids=None):
        return arrays_equal(
            getattr(self.data[key1].table[:, name],
                    data_type),
            getattr(self.data[key2].table[:, name],
                    data_type),
            type_)

    def create_from_columns(self, columns, relevant_keys, get_selected):
        """
        Columns are duplicated only if values differ (even
        if only in order of values), origin table name and input slot is added to column name.
        """
        var_dict = {}
        for atr_type in self.atr_types:
            container = {}
            for table_key in relevant_keys:
                table = self.data[table_key].table
                if atr_type == 'attributes':
                    if get_selected:
                        atrs = list(compress(table.domain.attributes,
                                             [c.name in columns for c in table.domain.attributes]))
                    else:
                        atrs = getattr(table.domain, atr_type)
                else:
                    atrs = getattr(table.domain, atr_type)
                merge_vars = self.curry_merge(table_key, atr_type)
                container = reduce(merge_vars, atrs, container)
            var_dict[atr_type] = container

        if get_selected:
            annotated = self.extract_columnwise(var_dict, None)
        else:
            annotated = self.extract_columnwise(var_dict, columns)

        return annotated

    def extract_rowwise(self, var_dict, ids=None, selection=False):
        """
        keys : ['attributes', 'metas', 'class_vars']
        vals: new_atrs - dictionary where key is old name, val
            is [is_different:bool, table_keys:list])
        ids: dict with ids for each table
        """
        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))

        permutations = {}
        for table_key, dict_ in ids.items():
            permutations[table_key] = get_perm(list(dict_), all_ids)

        domain = defaultdict(list)
        values = defaultdict(list)
        renamed = []
        for atr_type, vars_dict in var_dict.items():
            for var_name, var_data in vars_dict.items():
                different = var_data[0]
                if different:
                    # Columns are different, copy and rename them.
                    # Renaming is done here to mark appropriately the source table.
                    # Additional strange clashes are checked later in merge_data
                    for var, table_key in var_data[1]:
                        temp = self.data[table_key].table
                        idx = list(self.data).index(table_key) + 1
                        domain[atr_type].append(var.copy(name='{} ({})'.format(var_name, idx)))
                        renamed.append(var_name.name)
                        v = getattr(temp[list(ids[table_key].values()), var_name],
                                    self.atr_vals[atr_type])
                        perm = permutations[table_key]
                        if len(v) < len(all_ids):
                            values[atr_type].append(pad_columns(v, perm, len(all_ids)))
                        else:
                            values[atr_type].append(v[perm].reshape(-1, 1))
                else:
                    value = np.full((len(all_ids), 1), np.nan)
                    domain[atr_type].append(var_data[1][0][0].copy())
                    for _, table_key in var_data[1]:
                        #different tables have different part of the same attribute vector
                        perm = permutations[table_key]
                        v = getattr(self.data[table_key].table[list(ids[table_key].values()),
                                                               var_name],
                                    self.atr_vals[atr_type]).reshape(-1, 1)
                        value = value.astype(v.dtype, copy=False)
                        value[perm] = v
                    values[atr_type].append(value)

        if renamed:
            self.Warning.renamed_vars(', '.join(renamed))
        idas = None if self.selected_feature else np.array(all_ids)
        table = self.merge_data(domain, values, idas)
        if selection:
            mask = [idx in self.selected_items for idx in all_ids]
            return create_annotated_table(table, mask)
        return table

    def get_indices(self, table, selection):
        """Returns mappings of ids (be it row id or string) to indices in tables"""
        if self.selected_feature:
            if self.output_duplicates and selection:
                items, inverse = np.unique(getattr(table[:, self.selected_feature], 'metas'),
                                           return_inverse=True)
                ids = [np.nonzero(inverse == idx)[0] for idx in range(len(items))]
            else:
                items, ids = np.unique(getattr(table[:, self.selected_feature], 'metas'),
                                       return_index=True)

        else:
            items = table.ids
            ids = range(len(table))

        if selection:
            return {item: idx for item, idx in zip(items, ids)
                    if item in self.selected_items}

        return dict(zip(items, ids))

    def get_indices_to_match_by(self, relevant_keys, selection=False):
        dict_ = {}
        for key in relevant_keys:
            table = self.data[key].table
            dict_[key] = self.get_indices(table, selection)
        return dict_

    def create_from_rows(self, relevant_ids, selection=False):
        var_dict = {}
        for atr_type in self.atr_types:
            container = {}
            for table_key in relevant_ids:
                merge_vars = self.curry_merge(table_key, atr_type, relevant_ids, selection)
                atrs = getattr(self.data[table_key].table.domain, atr_type)
                container = reduce(merge_vars, atrs, container)
            var_dict[atr_type] = container
        if self.output_duplicates and not selection:
            return self.extract_rowwise_duplicates(var_dict, relevant_ids)
        return self.extract_rowwise(var_dict, relevant_ids, selection)

    def expand_table(self, table, atrs, metas, cv):
        exp = []
        n = 1 if isinstance(table, RowInstance) else len(table)
        if isinstance(table, RowInstance):
            ids = table.id.reshape(-1, 1)
            atr_vals = self.row_vals
        else:
            ids = table.ids.reshape(-1, 1)
            atr_vals = self.atr_vals
        for all_el, atr_type in zip([atrs, metas, cv], self.atr_types):
            cur_el = getattr(table.domain, atr_type)
            array = np.full((n, len(all_el)), np.nan)
            if cur_el:
                perm = get_perm(cur_el, all_el)
                b = getattr(table, atr_vals[atr_type]).reshape(len(array), len(perm))
                array = array.astype(b.dtype, copy=False)
                array[:, perm] = b
            exp.append(array)
        return (*exp, ids)

    def extract_rowwise_duplicates(self, var_dict, ids):
        all_ids = sorted(reduce(set.union, [set(val) for val in ids.values()], set()))
        sort_key = attrgetter("name")
        all_atrs = sorted(var_dict['attributes'], key=sort_key)
        all_metas = sorted(var_dict['metas'], key=sort_key)
        all_cv = sorted(var_dict['class_vars'], key=sort_key)

        all_x, all_y, all_m = [], [], []
        new_table_ids = []
        for idx in all_ids:
            #iterate trough tables with same idx
            for table_key, t_indices in ids.items():
                if idx not in t_indices:
                    continue
                map_ = t_indices[idx]
                extracted = self.data[table_key].table[map_]
                # pylint: disable=unbalanced-tuple-unpacking
                x, m, y, t_ids = self.expand_table(extracted, all_atrs, all_metas, all_cv)
                all_x.append(x)
                all_y.append(y)
                all_m.append(m)
                new_table_ids.append(t_ids)
        domain = {'attributes': all_atrs, 'metas': all_metas, 'class_vars': all_cv}
        values = {'attributes': [np.vstack(all_x)],
                  'metas': [np.vstack(all_m)],
                  'class_vars': [np.vstack(all_y)]}
        return self.merge_data(domain, values, np.vstack(new_table_ids))

    def commit(self):
        if not self.vennwidget.vennareas() or not self.data:
            self.Outputs.selected_data.send(None)
            self.Outputs.annotated_data.send(None)
            return

        self.selected_items = reduce(
            set.union, [self.disjoint[index] for index in self.selection],
            set()
        )
        selected_keys = reduce(
            set.union, [set(self.area_keys[area]) for area in self.selection],
            set())
        selected = None

        if self.rowwise:
            if self.selected_items:
                selected_ids = self.get_indices_to_match_by(
                    selected_keys, bool(self.selection))
                selected = self.create_from_rows(selected_ids, False)
            annotated_ids = self.get_indices_to_match_by(self.data)
            annotated = self.create_from_rows(annotated_ids, True)
        else:
            annotated = self.create_from_columns(self.selected_items, self.data, False)
            if self.selected_items:
                selected = self.create_from_columns(self.selected_items, selected_keys, True)

        self.Outputs.selected_data.send(selected)
        self.Outputs.annotated_data.send(annotated)

    def send_report(self):
        self.report_plot()

    def get_disjoint(self, sets):
        """
        Return all disjoint subsets.
        """
        sets = list(sets)
        n = len(sets)
        disjoint_sets = [None] * (2 ** n)
        included_tables = [None] * (2 ** n)
        for i in range(2 ** n):
            key = setkey(i, n)
            included = [s for s, inc in zip(sets, key) if inc]
            if included:
                excluded = [s for s, inc in zip(sets, key) if not inc]
                s = reduce(set.intersection, included)
                s = reduce(set.difference, excluded, s)
            else:
                s = set()
            disjoint_sets[i] = s
            included_tables[i] = [k for k, inc in zip(self.data, key) if inc]

        return disjoint_sets, included_tables

def string_attributes(domain):
    """
    Return all string attributes from the domain.
    """
    return [attr for attr in domain.variables + domain.metas if attr.is_string]

def source_attributes(domain):
    """
    Return all suitable attributes for the venn diagram.
    """
    return string_attributes(domain)  # + discrete_attributes(domain)


def disjoint_set_label(i, n, simplify=False):
    """
    Return a html formated label for a disjoint set indexed by `i`.
    """
    intersection = unicodedata.lookup("INTERSECTION")
    # comp = unicodedata.lookup("COMPLEMENT")  #
    # This depends on the font but the unicode complement in
    # general does not look nice in a super script so we use
    # plain c instead.
    comp = "c"

    def label_for_index(i):
        return chr(ord("A") + i)

    if simplify:
        return "".join(label_for_index(i) for i, b in enumerate(setkey(i, n))
                       if b)
    else:
        return intersection.join(label_for_index(i) +
                                 ("" if b else "<sup>" + comp + "</sup>")
                                 for i, b in enumerate(setkey(i, n)))


class VennSetItem(QGraphicsPathItem):
    def __init__(self, parent=None, text="", informativeText=""):
        super(VennSetItem, self).__init__(parent)
        # Plain text title (editable by the VennDiagram)
        self.text = text
        # Extra informative text (possibly rich text)
        self.informativeText = informativeText

# TODO: Use palette's selected/highligted text / background colors to
# indicate selection


class VennIntersectionArea(QGraphicsPathItem):
    def __init__(self, parent=None, text=""):
        super().__init__(parent)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))

        self.text = QGraphicsTextItem(self)
        layout = self.text.document().documentLayout()
        layout.documentSizeChanged.connect(self._onLayoutChanged)

        self._text = text
        self._anchor = QPointF()

    def setText(self, text):
        if self._text != text:
            self._text = text
            self.text.setPlainText(text)

    def setTextAnchor(self, pos):
        if self._anchor != pos:
            self._anchor = pos
            self._updateTextAnchor()

    def hoverEnterEvent(self, event):
        self.setZValue(self.zValue() + 1)
        return QGraphicsPathItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.setZValue(self.zValue() - 1)
        return QGraphicsPathItem.hoverLeaveEvent(self, event)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            if event.modifiers() & Qt.AltModifier:
                self.setSelected(False)
            elif event.modifiers() & Qt.ControlModifier:
                self.setSelected(not self.isSelected())
            elif event.modifiers() & Qt.ShiftModifier:
                self.setSelected(True)
            else:
                for area in self.parentWidget().vennareas():
                    area.setSelected(False)
                self.setSelected(True)

    def mouseReleaseEvent(self, event):
        pass

    def paint(self, painter, option, _widget=None):
        painter.save()
        path = self.path()
        brush = QBrush(self.brush())
        pen = QPen(self.pen())

        if option.state & QStyle.State_Selected:
            pen.setColor(Qt.red)
            brush.setStyle(Qt.DiagCrossPattern)
            brush.setColor(QColor(40, 40, 40, 100))

        elif option.state & QStyle.State_MouseOver:
            pen.setColor(Qt.blue)

        if option.state & QStyle.State_MouseOver:
            brush.setColor(QColor(100, 100, 100, 100))
            if brush.style() == Qt.NoBrush:
                # Make sure the highlight is actually visible.
                brush.setStyle(Qt.SolidPattern)

        painter.setPen(pen)
        painter.setBrush(brush)
        painter.drawPath(path)
        painter.restore()

    def itemChange(self, change, value):
        if change == QGraphicsPathItem.ItemSelectedHasChanged:
            self.setZValue(self.zValue() + (1 if value else -1))
        return QGraphicsPathItem.itemChange(self, change, value)

    def _updateTextAnchor(self):
        rect = self.text.boundingRect()
        pos = anchor_rect(rect, self._anchor)
        self.text.setPos(pos)

    def _onLayoutChanged(self):
        self._updateTextAnchor()


class GraphicsTextEdit(QGraphicsTextItem):
    #: Edit triggers
    NoEditTriggers, DoubleClicked = 0, 1

    editingFinished = Signal()
    editingStarted = Signal()

    documentSizeChanged = Signal()

    def __init__(self, *args, **kwargs):
        super(GraphicsTextEdit, self).__init__(*args, **kwargs)
        self.setCursor(Qt.IBeamCursor)
        self.setTabChangesFocus(True)
        self._edittrigger = GraphicsTextEdit.DoubleClicked
        self._editing = False
        self.document().documentLayout().documentSizeChanged.connect(
            self.documentSizeChanged
        )

    def mouseDoubleClickEvent(self, event):
        super(GraphicsTextEdit, self).mouseDoubleClickEvent(event)
        if self._edittrigger == GraphicsTextEdit.DoubleClicked:
            self._start()

    def focusOutEvent(self, event):
        super(GraphicsTextEdit, self).focusOutEvent(event)

        if self._editing:
            self._end()

    def _start(self):
        self._editing = True
        self.setTextInteractionFlags(Qt.TextEditorInteraction)
        self.setFocus(Qt.MouseFocusReason)
        self.editingStarted.emit()

    def _end(self):
        self._editing = False
        self.setTextInteractionFlags(Qt.NoTextInteraction)
        self.editingFinished.emit()


class VennDiagram(QGraphicsWidget):
    # rect and petal are for future work
    Circle, Ellipse, Rect, Petal = 1, 2, 3, 4

    TitleFormat = "<center><h4>{0}</h4>{1}</center>"

    selectionChanged = Signal()
    itemTextEdited = Signal(int, str)

    def __init__(self, parent=None):
        super(VennDiagram, self).__init__(parent)
        self.shapeType = VennDiagram.Circle
        self._items = []
        self._vennareas = []
        self._textitems = []
        self._subsettextitems = []
        self._textanchors = []

    def item(self, index):
        return self._items[index]

    def items(self):
        return list(self._items)

    def count(self):
        return len(self._items)

    def setItems(self, items):
        if self._items:
            self.clear()

        self._items = list(items)

        for item in self._items:
            item.setParentItem(self)
            item.setVisible(True)

        fmt = self.TitleFormat.format

        font = self.font()
        font.setPixelSize(14)

        for item in items:
            text = GraphicsTextEdit(self)
            text.setFont(font)
            text.setDefaultTextColor(QColor("#333"))
            text.setHtml(fmt(escape(item.text), item.informativeText))
            text.adjustSize()
            text.editingStarted.connect(self._on_editingStarted)
            text.editingFinished.connect(self._on_editingFinished)
            text.documentSizeChanged.connect(
                self._on_itemTextSizeChanged
            )

            self._textitems.append(text)

        self._vennareas = [
            VennIntersectionArea(parent=self)
            for i in range(2 ** len(items))
        ]
        self._subsettextitems = [
            QGraphicsTextItem(parent=self)
            for i in range(2 ** len(items))
        ]

        self._updateLayout()

    def clear(self):
        scene = self.scene()
        items = self.vennareas() + list(self.items()) + self._textitems

        for item in self._textitems:
            item.editingStarted.disconnect(self._on_editingStarted)
            item.editingFinished.disconnect(self._on_editingFinished)
            item.documentSizeChanged.disconnect(
                self._on_itemTextSizeChanged
            )

        self._items = []
        self._vennareas = []
        self._textitems = []
        self._subsettextitems = []
        self._textanchors = []

        for item in items:
            item.setVisible(False)
            item.setParentItem(None)
            if scene is not None:
                scene.removeItem(item)

    def vennareas(self):
        return list(self._vennareas)

    def setFont(self, font):
        if font != self.font():
            self.prepareGeometryChange()
            super().setFont(font)

            for item in self.items():
                item.setFont(font)

    def _updateLayout(self):
        rect = self.geometry()
        n = len(self._items)
        if not n:
            return

        regions = venn_diagram(n)

        # The y axis in Qt points downward
        transform = QTransform().scale(1, -1)
        regions = list(map(transform.map, regions))

        union_brect = reduce(QRectF.united,
                             (path.boundingRect() for path in regions))

        scalex = rect.width() / union_brect.width()
        scaley = rect.height() / union_brect.height()
        scale = min(scalex, scaley)

        transform = QTransform().scale(scale, scale)

        regions = [transform.map(path) for path in regions]

        center = rect.width() / 2, rect.height() / 2
        for item, path in zip(self.items(), regions):
            item.setPath(path)
            item.setPos(*center)

        intersections = venn_intersections(regions)
        assert len(intersections) == 2 ** n
        assert len(self.vennareas()) == 2 ** n

        anchors = [(0, 0)] + subset_anchors(self._items)

        anchor_transform = QTransform().scale(rect.width(), -rect.height())
        for i, area in enumerate(self.vennareas()):
            area.setPath(intersections[setkey(i, n)])
            area.setPos(*center)
            x, y = anchors[i]
            anchor = anchor_transform.map(QPointF(x, y))
            area.setTextAnchor(anchor)
            area.setZValue(30)

        self._updateTextAnchors()

    def _updateTextAnchors(self):
        n = len(self._items)

        items = self._items
        dist = 15

        shape = reduce(QPainterPath.united, [item.path() for item in items])
        brect = shape.boundingRect()
        bradius = max(brect.width() / 2, brect.height() / 2)

        center = self.boundingRect().center()

        anchors = _category_anchors(items)
        self._textanchors = []
        for angle, anchor_h, anchor_v in anchors:
            line = QLineF.fromPolar(bradius, angle)
            ext = QLineF.fromPolar(dist, angle)
            line = QLineF(line.p1(), line.p2() + ext.p2())
            line = line.translated(center)

            anchor_pos = line.p2()
            self._textanchors.append((anchor_pos, anchor_h, anchor_v))

        for i in range(n):
            self._updateTextItemPos(i)

    def _updateTextItemPos(self, i):
        item = self._textitems[i]
        anchor_pos, anchor_h, anchor_v = self._textanchors[i]
        rect = item.boundingRect()
        pos = anchor_rect(rect, anchor_pos, anchor_h, anchor_v)
        item.setPos(pos)

    def setGeometry(self, geometry):
        super(VennDiagram, self).setGeometry(geometry)
        self._updateLayout()

    def _on_editingStarted(self):
        item = self.sender()
        index = self._textitems.index(item)
        text = self._items[index].text
        item.setTextWidth(-1)
        item.setHtml(self.TitleFormat.format(escape(text), "<br/>"))

    def _on_editingFinished(self):
        item = self.sender()
        index = self._textitems.index(item)
        text = item.toPlainText()
        if text != self._items[index].text:
            self._items[index].text = text

            self.itemTextEdited.emit(index, text)

        item.setHtml(
            self.TitleFormat.format(
                escape(text), self._items[index].informativeText))
        item.adjustSize()

    def _on_itemTextSizeChanged(self):
        item = self.sender()
        index = self._textitems.index(item)
        self._updateTextItemPos(index)


def anchor_rect(rect, anchor_pos,
                anchor_h=Qt.AnchorHorizontalCenter,
                anchor_v=Qt.AnchorVerticalCenter):

    if anchor_h == Qt.AnchorLeft:
        x = anchor_pos.x()
    elif anchor_h == Qt.AnchorHorizontalCenter:
        x = anchor_pos.x() - rect.width() / 2
    elif anchor_h == Qt.AnchorRight:
        x = anchor_pos.x() - rect.width()
    else:
        raise ValueError(anchor_h)

    if anchor_v == Qt.AnchorTop:
        y = anchor_pos.y()
    elif anchor_v == Qt.AnchorVerticalCenter:
        y = anchor_pos.y() - rect.height() / 2
    elif anchor_v == Qt.AnchorBottom:
        y = anchor_pos.y() - rect.height()
    else:
        raise ValueError(anchor_v)

    return QPointF(x, y)


def radians(angle):
    return 2 * math.pi * angle / 360


def unit_point(x, r=1.0):
    x = radians(x)
    return (r * math.cos(x), r * math.sin(x))


def _category_anchors(shapes):
    n = len(shapes)
    return _CATEGORY_ANCHORS[n - 1]


# (angle, horizontal anchor, vertical anchor)
_CATEGORY_ANCHORS = (
    # n == 1
    ((90, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),),
    # n == 2
    ((180, Qt.AnchorRight, Qt.AnchorVerticalCenter),
     (0, Qt.AnchorLeft, Qt.AnchorVerticalCenter)),
    # n == 3
    ((150, Qt.AnchorRight, Qt.AnchorBottom),
     (30, Qt.AnchorLeft, Qt.AnchorBottom),
     (270, Qt.AnchorHorizontalCenter, Qt.AnchorTop)),
    # n == 4
    ((270 + 45, Qt.AnchorLeft, Qt.AnchorTop),
     (270 - 45, Qt.AnchorRight, Qt.AnchorTop),
     (90 - 15, Qt.AnchorLeft, Qt.AnchorBottom),
     (90 + 15, Qt.AnchorRight, Qt.AnchorBottom)),
    # n == 5
    ((90 - 5, Qt.AnchorHorizontalCenter, Qt.AnchorBottom),
     (18 - 5, Qt.AnchorLeft, Qt.AnchorVerticalCenter),
     (306 - 5, Qt.AnchorLeft, Qt.AnchorTop),
     (234 - 5, Qt.AnchorRight, Qt.AnchorTop),
     (162 - 5, Qt.AnchorRight, Qt.AnchorVerticalCenter),)
)


def subset_anchors(shapes):
    n = len(shapes)
    if n == 1:
        return [(0, 0)]
    elif n == 2:
        return [unit_point(180, r=1/3),
                unit_point(0, r=1/3),
                (0, 0)]
    elif n == 3:
        return [unit_point(150, r=0.35),  # A
                unit_point(30, r=0.35),   # B
                unit_point(90, r=0.27),   # AB
                unit_point(270, r=0.35),  # C
                unit_point(210, r=0.27),  # AC
                unit_point(330, r=0.27),  # BC
                unit_point(0, r=0),]      # ABC
    elif n == 4:
        anchors = [
            (0.400, 0.110),    # A
            (-0.400, 0.110),   # B
            (0.000, -0.285),   # AB
            (0.180, 0.330),    # C
            (0.265, 0.205),    # AC
            (-0.240, -0.110),  # BC
            (-0.100, -0.190),  # ABC
            (-0.180, 0.330),   # D
            (0.240, -0.110),   # AD
            (-0.265, 0.205),   # BD
            (0.100, -0.190),   # ABD
            (0.000, 0.250),    # CD
            (0.153, 0.090),    # ACD
            (-0.153, 0.090),   # BCD
            (0.000, -0.060),   # ABCD
        ]
        return anchors

    elif n == 5:
        anchors = [None] * 32
        # Base anchors
        A = (0.033, 0.385)
        AD = (0.095, 0.250)
        AE = (-0.100, 0.265)
        ACE = (-0.130, 0.220)
        ADE = (0.010, 0.225)
        ACDE = (-0.095, 0.175)
        ABCDE = (0.0, 0.0)

        anchors[-1] = ABCDE

        bases = [(0b00001, A),
                 (0b01001, AD),
                 (0b10001, AE),
                 (0b10101, ACE),
                 (0b11001, ADE),
                 (0b11101, ACDE)]

        for i in range(5):
            for index, anchor in bases:
                index = bit_rot_left(index, i, bits=5)
                assert anchors[index] is None
                anchors[index] = rotate_point(anchor, - 72 * i)

        assert all(anchors[1:])
        return anchors[1:]
    return None


def bit_rot_left(x, y, bits=32):
    mask = 2 ** bits - 1
    x_masked = x & mask
    return (x << y) & mask | (x_masked >> bits - y)


def rotate_point(p, angle):
    r = radians(angle)
    R = np.array([[math.cos(r), -math.sin(r)],
                  [math.sin(r), math.cos(r)]])
    x, y = np.dot(R, p)
    return (float(x), float(y))


def line_extended(line, distance):
    """
    Return an QLineF extended by `distance` units in the positive direction.
    """
    angle = line.angle() / 360 * 2 * math.pi
    dx, dy = unit_point(angle, r=distance)
    return QLineF(line.p1(), line.p2() + QPointF(dx, dy))


def circle_path(center, r=1.0):
    return ellipse_path(center, r, r, rotation=0)


def ellipse_path(center, a, b, rotation=0):
    if not isinstance(center, QPointF):
        center = QPointF(*center)

    brect = QRectF(-a, -b, 2 * a, 2 * b)

    path = QPainterPath()
    path.addEllipse(brect)

    if rotation != 0:
        transform = QTransform().rotate(rotation)
        path = transform.map(path)

    path.translate(center)
    return path


# TODO: Should include anchors for text layout (both inside and outside).
# for each item {path: QPainterPath,
#                text_anchors: [{center}] * (2 ** n)
#                mayor_axis: QLineF,
#                boundingRect QPolygonF (with 4 vertices)}
#
# Should be a new class with overloads for ellipse/circle, rect, and petal
# shapes, should store all constructor parameters, rotation, center,
# mayor/minor axis.


def venn_diagram(n):
    if n < 1 or n > 5:
        raise ValueError()

    paths = []

    if n == 1:
        paths = [circle_path(center=(0, 0), r=0.5)]
    elif n == 2:
        angles = [180, 0]
        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)
                 for x in angles]
    elif n == 3:
        angles = [150 - 120 * i for i in range(3)]
        paths = [circle_path(center=unit_point(x, r=1/6), r=1/3)
                 for x in angles]
    elif n == 4:
        # Constants shamelessly stolen from VennDiagram R package
        paths = [
            ellipse_path((0.65 - 0.5, 0.47 - 0.5), 0.35, 0.20, 45),
            ellipse_path((0.35 - 0.5, 0.47 - 0.5), 0.35, 0.20, 135),
            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 45),
            ellipse_path((0.5 - 0.5, 0.57 - 0.5), 0.35, 0.20, 134),
        ]
    elif n == 5:
        # Constants shamelessly stolen from VennDiagram R package
        d = 0.13
        a, b = 0.24, 0.48
        a, b = b, a
        a, b = 0.48, 0.24
        paths = [ellipse_path(unit_point((1 - i) * 72, r=d),
                              a, b, rotation=90 - (i * 72))
                 for i in range(5)]

    return paths


def setkey(intval, n):
    return tuple(bool(intval & (2 ** i)) for i in range(n))


def keyrange(n):
    if n < 0:
        raise ValueError()

    for i in range(2 ** n):
        yield setkey(i, n)


def venn_intersections(paths):
    n = len(paths)
    return {key: venn_intersection(paths, key) for key in keyrange(n)}


def venn_intersection(paths, key):
    if not any(key):
        return QPainterPath()

    # first take the intersection of all included paths
    path = reduce(QPainterPath.intersected,
                  (path for path, included in zip(paths, key) if included))

    # subtract all the excluded sets (i.e. take the intersection
    # with the excluded set complements)
    path = reduce(QPainterPath.subtracted,
                  (path for path, included in zip(paths, key) if not included),
                  path)

    return path


def append_column(data, where, variable, column):
    X, Y, M = data.X, data.Y, data.metas
    domain = data.domain
    attr = domain.attributes
    class_vars = domain.class_vars
    metas = domain.metas
    if where == "X":
        attr = attr + (variable,)
        X = np.hstack((X, column))
    elif where == "Y":
        class_vars = class_vars + (variable,)
        Y = np.hstack((Y, column))
    elif where == "M":
        metas = metas + (variable,)
        M = np.hstack((M, column))
    else:
        raise ValueError
    domain = Domain(attr, class_vars, metas)
    new_data = data.transform(domain)
    new_data[:, variable] = column
    return new_data

def arrays_equal(a, b, type_):
    """
    checks if arrays have nans in same places and if not-nan elements
    are equal
    """
    if a is None and b is None:
        return True
    if a is None or b is None:
        return False
    if type_ is not StringVariable:
        nana = np.isnan(a)
        nanb = np.isnan(b)
        return np.all(nana == nanb) and np.all(a[~nana] == b[~nanb])
    else:
        return np.all(a == b)

def pad_columns(values, mask, l):
    #inflates columns with nans
    a = np.full((l, 1), np.nan, dtype=values.dtype)
    a[mask] = values.reshape(-1, 1)
    return a

def get_perm(ids, all_ids):
    return [all_ids.index(el) for el in ids if el in all_ids]


def main():  # pragma: no cover
    # pylint: disable=import-outside-toplevel
    from Orange.evaluation import ShuffleSplit

    data = Table("brown-selected")

    if not "test_rows":  # change to `if not "test_rows" to test columns
        data = append_column(data, "M", StringVariable("Test"),
                             (np.arange(len(data)).reshape(-1, 1) % 30).astype(str))
        res = ShuffleSplit(n_resamples=5, test_size=0.7, stratified=False)
        indices = iter(res.get_indices(data))
        datasets = []
        for i in range(1, 6):
            sample, _ = next(indices)
            data1 = data[sample]
            data1.name = chr(ord("A") + i)
            datasets.append((data1, i))
    else:
        domain = data.domain
        data1 = data.transform(Domain(domain.attributes[:15], domain.class_var))
        data2 = data.transform(Domain(domain.attributes[10:], domain.class_var))
        datasets = [(data1, 1), (data2, 2)]

    WidgetPreview(OWVennDiagram).run(setData=datasets)


if __name__ == "__main__":  # pragma: no cover
    main()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user