{
    "file_diffs": [
        {
            "old_file_content": "# A binary morphology add-on for the Python Imaging Library\n#\n# History:\n#   2014-06-04 Initial version.\n#\n# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>\n\nfrom __future__ import print_function\n\nfrom . import Image, _imagingmorph\nimport re\n\nLUT_SIZE = 1 << 9\n\n\nclass LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut\n\n\nclass MorphOp(object):\n    \"\"\"A class for binary morphological operators\"\"\"\n\n    def __init__(self,\n                 lut=None,\n                 op_name=None,\n                 patterns=None):\n        \"\"\"Create a binary morphological operator\"\"\"\n        self.lut = lut\n        if op_name is not None:\n            self.lut = LutBuilder(op_name=op_name).build_lut()\n        elif patterns is not None:\n            self.lut = LutBuilder(patterns=patterns).build_lut()\n\n    def apply(self, image):\n        \"\"\"Run a single morphological operation on an image\n\n        Returns a tuple of the number of changed pixels and the\n        morphed image\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        outimage = Image.new(image.mode, image.size, None)\n        count = _imagingmorph.apply(\n            bytes(self.lut), image.im.id, outimage.im.id)\n        return count, outimage\n\n    def match(self, image):\n        \"\"\"Get a list of coordinates matching the morphological operation on\n        an image.\n\n        Returns a list of tuples of (x,y) coordinates\n        of all matching pixels.\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        return _imagingmorph.match(bytes(self.lut), image.im.id)\n\n    def get_on_pixels(self, image):\n        \"\"\"Get a list of all turned on pixels in a binary image\n\n        Returns a list of tuples of (x,y) coordinates\n        of all matching pixels.\"\"\"\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        return _imagingmorph.get_on_pixels(image.im.id)\n\n    def load_lut(self, filename):\n        \"\"\"Load an operator from an mrl file\"\"\"\n        with open(filename, 'rb') as f:\n            self.lut = bytearray(f.read())\n\n        if len(self.lut) != LUT_SIZE:\n            self.lut = None\n            raise Exception('Wrong size operator file!')\n\n    def save_lut(self, filename):\n        \"\"\"Save an operator to an mrl file\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n        with open(filename, 'wb') as f:\n            f.write(self.lut)\n\n    def set_lut(self, lut):\n        \"\"\"Set the lut from an external source\"\"\"\n        self.lut = lut\n",
            "new_file_content": "# A binary morphology add-on for the Python Imaging Library\n#\n# History:\n#   2014-06-04 Initial version.\n#\n# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>\n\nfrom __future__ import print_function\n\nfrom . import Image, _imagingmorph\nimport re\n\nLUT_SIZE = 1 << 9\n\n\nclass LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = 1-int(res)\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p, r in patterns:\n#            print(p, r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut\n\n\nclass MorphOp(object):\n    \"\"\"A class for binary morphological operators\"\"\"\n\n    def __init__(self,\n                 lut=None,\n                 op_name=None,\n                 patterns=None):\n        \"\"\"Create a binary morphological operator\"\"\"\n        self.lut = lut\n        if op_name is not None:\n            self.lut = LutBuilder(op_name=op_name).build_lut()\n        elif patterns is not None:\n            self.lut = LutBuilder(patterns=patterns).build_lut()\n\n    def apply(self, image):\n        \"\"\"Run a single morphological operation on an image\n\n        Returns a tuple of the number of changed pixels and the\n        morphed image\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        outimage = Image.new(image.mode, image.size, None)\n        count = _imagingmorph.apply(\n            bytes(self.lut), image.im.id, outimage.im.id)\n        return count, outimage\n\n    def match(self, image):\n        \"\"\"Get a list of coordinates matching the morphological operation on\n        an image.\n\n        Returns a list of tuples of (x,y) coordinates\n        of all matching pixels.\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        return _imagingmorph.match(bytes(self.lut), image.im.id)\n\n    def get_on_pixels(self, image):\n        \"\"\"Get a list of all turned on pixels in a binary image\n\n        Returns a list of tuples of (x,y) coordinates\n        of all matching pixels.\"\"\"\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        return _imagingmorph.get_on_pixels(image.im.id)\n\n    def load_lut(self, filename):\n        \"\"\"Load an operator from an mrl file\"\"\"\n        with open(filename, 'rb') as f:\n            self.lut = bytearray(f.read())\n\n        if len(self.lut) != LUT_SIZE:\n            self.lut = None\n            raise Exception('Wrong size operator file!')\n\n    def save_lut(self, filename):\n        \"\"\"Save an operator to an mrl file\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n        with open(filename, 'wb') as f:\n            f.write(self.lut)\n\n    def set_lut(self, lut):\n        \"\"\"Set the lut from an external source\"\"\"\n        self.lut = lut\n",
            "header": {
                "file": {
                    "path": "PIL/ImageMorph.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "dcce63fa4",
                "new_commit_hash": "d2367737f",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/PIL/ImageMorph.py"
            },
            "plus_file": {
                "path": "b/PIL/ImageMorph.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 123,
                            "length": 7
                        },
                        "new_range": {
                            "start": 123,
                            "length": 7
                        },
                        "section": "class LutBuilder(object):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                           .replace('0', 'Z')",
                                "type": "context"
                            },
                            {
                                "content": "                           .replace('1', '0')",
                                "type": "context"
                            },
                            {
                                "content": "                           .replace('Z', '1'))",
                                "type": "context"
                            },
                            {
                                "content": "                res = '%d' % (1-int(res))",
                                "type": "deleted"
                            },
                            {
                                "content": "                res = 1-int(res)",
                                "type": "added"
                            },
                            {
                                "content": "                patterns.append((pattern, res))",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        return patterns",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/ImageMorph.py",
                            "type": "class",
                            "name": "LutBuilder",
                            "content": "class LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 16,
                            "end_lineno": 177,
                            "parent": null
                        },
                        {
                            "file_name": "PIL/ImageMorph.py",
                            "type": "method",
                            "name": "LutBuilder._pattern_permute",
                            "content": "class LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 94,
                            "end_lineno": 129,
                            "parent": {
                                "file_name": "PIL/ImageMorph.py",
                                "type": "class",
                                "name": "LutBuilder",
                                "content": "class LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 16,
                                "end_lineno": 177,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 152,
                            "length": 8
                        },
                        "new_range": {
                            "start": 152,
                            "length": 8
                        },
                        "section": "class LutBuilder(object):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            patterns += self._pattern_permute(pattern, options, result)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "#        # Debugging",
                                "type": "context"
                            },
                            {
                                "content": "#        for p,r in patterns:",
                                "type": "deleted"
                            },
                            {
                                "content": "#            print(p,r)",
                                "type": "deleted"
                            },
                            {
                                "content": "#        for p, r in patterns:",
                                "type": "added"
                            },
                            {
                                "content": "#            print(p, r)",
                                "type": "added"
                            },
                            {
                                "content": "#        print('--')",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        # compile the patterns into regular expressions for speed",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "PIL/ImageMorph.py",
                            "type": "class",
                            "name": "LutBuilder",
                            "content": "class LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 16,
                            "end_lineno": 177,
                            "parent": null
                        },
                        {
                            "file_name": "PIL/ImageMorph.py",
                            "type": "method",
                            "name": "LutBuilder.build_lut",
                            "content": "class LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 131,
                            "end_lineno": 177,
                            "parent": {
                                "file_name": "PIL/ImageMorph.py",
                                "type": "class",
                                "name": "LutBuilder",
                                "content": "class LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 16,
                                "end_lineno": 177,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# Test the ImageMorphology functionality\nfrom helper import unittest, PillowTestCase, hopper\n\nfrom PIL import Image\nfrom PIL import ImageMorph\n\n\nclass MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))\n\n\nif __name__ == '__main__':\n    unittest.main()\n",
            "new_file_content": "# Test the ImageMorphology functionality\nfrom helper import unittest, PillowTestCase, hopper\n\nfrom PIL import Image\nfrom PIL import ImageMorph\n\n\nclass MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_negate(self):\n        # Test 'N' for negate\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'N:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1....\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))\n\n\nif __name__ == '__main__':\n    unittest.main()\n",
            "header": {
                "file": {
                    "path": "Tests/test_imagemorph.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "c0f6448e6",
                "new_commit_hash": "dac1ae150",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Tests/test_imagemorph.py"
            },
            "plus_file": {
                "path": "b/Tests/test_imagemorph.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 184,
                            "length": 7
                        },
                        "new_range": {
                            "start": 184,
                            "length": 6
                        },
                        "section": "class MorphTests(PillowTestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.assertEqual(count, 7)",
                                "type": "context"
                            },
                            {
                                "content": "        self.assert_img_equal_img_string(Aout,",
                                "type": "context"
                            },
                            {
                                "content": "                                         \"\"\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "                                         .......",
                                "type": "context"
                            },
                            {
                                "content": "                                         .......",
                                "type": "context"
                            },
                            {
                                "content": "                                         ..1.1..",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Tests/test_imagemorph.py",
                            "type": "class",
                            "name": "MorphTests",
                            "content": "class MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 8,
                            "end_lineno": 203,
                            "parent": null
                        },
                        {
                            "file_name": "Tests/test_imagemorph.py",
                            "type": "method",
                            "name": "MorphTests.test_mirroring",
                            "content": "class MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 179,
                            "end_lineno": 195,
                            "parent": {
                                "file_name": "Tests/test_imagemorph.py",
                                "type": "class",
                                "name": "MorphTests",
                                "content": "class MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 8,
                                "end_lineno": 203,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 194,
                            "length": 6
                        },
                        "new_range": {
                            "start": 193,
                            "length": 23
                        },
                        "section": "class MorphTests(PillowTestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                                         .......",
                                "type": "context"
                            },
                            {
                                "content": "                                         \"\"\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_negate(self):",
                                "type": "added"
                            },
                            {
                                "content": "        # Test 'N' for negate",
                                "type": "added"
                            },
                            {
                                "content": "        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',",
                                "type": "added"
                            },
                            {
                                "content": "                                           'N:(00. 01. ...)->1'])",
                                "type": "added"
                            },
                            {
                                "content": "        count, Aout = mop.apply(self.A)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(count, 8)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assert_img_equal_img_string(Aout,",
                                "type": "added"
                            },
                            {
                                "content": "                                         \"\"\"",
                                "type": "added"
                            },
                            {
                                "content": "                                         .......",
                                "type": "added"
                            },
                            {
                                "content": "                                         .......",
                                "type": "added"
                            },
                            {
                                "content": "                                         ..1....",
                                "type": "added"
                            },
                            {
                                "content": "                                         .......",
                                "type": "added"
                            },
                            {
                                "content": "                                         .......",
                                "type": "added"
                            },
                            {
                                "content": "                                         .......",
                                "type": "added"
                            },
                            {
                                "content": "                                         .......",
                                "type": "added"
                            },
                            {
                                "content": "                                         \"\"\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_non_binary_images(self):",
                                "type": "context"
                            },
                            {
                                "content": "        im = hopper('RGB')",
                                "type": "context"
                            },
                            {
                                "content": "        mop = ImageMorph.MorphOp(op_name=\"erosion8\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Tests/test_imagemorph.py",
                            "type": "class",
                            "name": "MorphTests",
                            "content": "class MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_negate(self):\n        # Test 'N' for negate\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'N:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1....\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 8,
                            "end_lineno": 219,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_imagemorph.py",
                            "type": "method",
                            "name": "MorphTests.test_negate",
                            "content": "class MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_negate(self):\n        # Test 'N' for negate\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'N:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1....\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 196,
                            "end_lineno": 211,
                            "parent": {
                                "file_name": "Tests/test_imagemorph.py",
                                "type": "class",
                                "name": "MorphTests",
                                "content": "class MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_negate(self):\n        # Test 'N' for negate\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'N:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1....\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 8,
                                "end_lineno": 219,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "04f28b691e961bff44b269652421f447add04c5c^",
    "new_commit_hash": "04f28b691e961bff44b269652421f447add04c5c",
    "commit_message": "Test negate and fix a TypeError: list indices must be integers, not str",
    "commit_date": "2017-05-30T18:02:20+03:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "04f28b691e961bff44b269652421f447add04c5c",
    "test_file_codes": [
        "# Test the ImageMorphology functionality\nfrom helper import unittest, PillowTestCase, hopper\n\nfrom PIL import Image\nfrom PIL import ImageMorph\n\n\nclass MorphTests(PillowTestCase):\n\n    def setUp(self):\n        self.A = self.string_to_img(\n            \"\"\"\n            .......\n            .......\n            ..111..\n            ..111..\n            ..111..\n            .......\n            .......\n            \"\"\"\n            )\n\n    def img_to_string(self, im):\n        \"\"\"Turn a (small) binary image into a string representation\"\"\"\n        chars = '.1'\n        width, height = im.size\n        return '\\n'.join(\n            ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width))\n            for r in range(height))\n\n    def string_to_img(self, image_string):\n        \"\"\"Turn a string image representation into a binary image\"\"\"\n        rows = [s for s in image_string.replace(' ', '').split('\\n')\n                if len(s)]\n        height = len(rows)\n        width = len(rows[0])\n        im = Image.new('L', (width, height))\n        for i in range(width):\n            for j in range(height):\n                c = rows[j][i]\n                v = c in 'X1'\n                im.putpixel((i, j), v)\n\n        return im\n\n    def img_string_normalize(self, im):\n        return self.img_to_string(self.string_to_img(im))\n\n    def assert_img_equal(self, A, B):\n        self.assertEqual(self.img_to_string(A), self.img_to_string(B))\n\n    def assert_img_equal_img_string(self, A, Bstring):\n        self.assertEqual(\n            self.img_to_string(A),\n            self.img_string_normalize(Bstring))\n\n    def test_str_to_img(self):\n        im = Image.open('Tests/images/morph_a.png')\n        self.assert_image_equal(self.A, im)\n\n    def create_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'wb') as f:\n                f.write(lut)\n\n    # create_lut()\n    def test_lut(self):\n        for op in (\n                'corner', 'dilation4', 'dilation8',\n                'erosion4', 'erosion8', 'edge'):\n            lb = ImageMorph.LutBuilder(op_name=op)\n            self.assertIsNone(lb.get_lut())\n\n            lut = lb.build_lut()\n            with open('Tests/images/%s.lut' % op, 'rb') as f:\n                self.assertEqual(lut, bytearray(f.read()))\n\n    def test_no_operator_loaded(self):\n        mop = ImageMorph.MorphOp()\n        self.assertRaises(Exception, lambda: mop.apply(None))\n        self.assertRaises(Exception, lambda: mop.match(None))\n        self.assertRaises(Exception, lambda: mop.save_lut(None))\n\n    # Test the named patterns\n    def test_erosion8(self):\n        # erosion8\n        mop = ImageMorph.MorphOp(op_name='erosion8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         .......\n                                         ...1...\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_dialation8(self):\n        # dialation8\n        mop = ImageMorph.MorphOp(op_name='dilation8')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 16)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         .......\n                                         \"\"\")\n\n    def test_erosion4(self):\n        # erosion4\n        mop = ImageMorph.MorphOp(op_name='dilation4')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 12)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         ..111..\n                                         .11111.\n                                         .11111.\n                                         .11111.\n                                         ..111..\n                                         .......\n                                         \"\"\")\n\n    def test_edge(self):\n        # edge\n        mop = ImageMorph.MorphOp(op_name='edge')\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 1)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..111..\n                                         ..1.1..\n                                         ..111..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_corner(self):\n        # Create a corner detector pattern\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           '4:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 5)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         \"\"\")\n\n        # Test the coordinate counting with the same operator\n        coords = mop.match(self.A)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n        coords = mop.get_on_pixels(Aout)\n        self.assertEqual(len(coords), 4)\n        self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4)))\n\n    def test_mirroring(self):\n        # Test 'M' for mirroring\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'M:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 7)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1.1..\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_negate(self):\n        # Test 'N' for negate\n        mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n                                           'N:(00. 01. ...)->1'])\n        count, Aout = mop.apply(self.A)\n        self.assertEqual(count, 8)\n        self.assert_img_equal_img_string(Aout,\n                                         \"\"\"\n                                         .......\n                                         .......\n                                         ..1....\n                                         .......\n                                         .......\n                                         .......\n                                         .......\n                                         \"\"\")\n\n    def test_non_binary_images(self):\n        im = hopper('RGB')\n        mop = ImageMorph.MorphOp(op_name=\"erosion8\")\n\n        self.assertRaises(Exception, lambda: mop.apply(im))\n        self.assertRaises(Exception, lambda: mop.match(im))\n        self.assertRaises(Exception, lambda: mop.get_on_pixels(im))\n\n\nif __name__ == '__main__':\n    unittest.main()\n",
        "#!/usr/bin/env python\nimport unittest\nimport sys\nimport time\nimport traceback\nimport platform\nimport os\n\n\nclass PytestLikeResult(unittest.TestResult):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.start_time = None\n        # Store (test, outcome, info) for each test:\n        #   outcome ∈ { \"passed\", \"failed\", \"error\", \"skipped\" }\n        #   info can hold the exception or skip reason, etc.\n        self.results = []\n\n    def startTestRun(self):\n        \"\"\"Called once before all tests.\"\"\"\n        self.start_time = time.time()\n        # Print a header similar to pytest\n        print(\"=\" * 29 + \" test session starts \" + \"=\" * 29)\n        # You can customize the platform/versions/paths as you like:\n        print(\n            f\"platform {platform.system().lower()} -- Python {platform.python_version()}\"\n        )\n        print(f\"rootdir: {os.path.abspath(os.curdir)}\")\n\n    def stopTestRun(self):\n        \"\"\"Called once after all tests are done.\"\"\"\n        elapsed = time.time() - self.start_time\n        print()  # blank line after final dot\n\n        # Summarize\n        ntests = len(self.results)\n        print(f\"collected {ntests} items\")\n\n        # Show details of failures/errors\n        failures = [r for r in self.results if r[1] == \"failed\"]\n        errors = [r for r in self.results if r[1] == \"error\"]\n\n        if failures:\n            print(\"\\n\" + \"=\" * 35 + \" FAILURES \" + \"=\" * 35)\n            for test, outcome, err_info in failures:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n        if errors:\n            print(\"\\n\" + \"=\" * 35 + \" ERRORS \" + \"=\" * 37)\n            for test, outcome, err_info in errors:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n\n        # Optional short summary lines for passes/fails/skips\n        print(\"\\n==================== short test summary info ====================\")\n        for test, outcome, info in self.results:\n            if outcome in (\"passed\", \"failed\", \"error\", \"skipped\"):\n                print(f\"{outcome.upper()} {test}\")\n\n        # Final one-line summary: “1 failed, 1 errors, 10 passed in 2.04s”\n        passed_count = sum(1 for r in self.results if r[1] == \"passed\")\n        failed_count = len(failures)\n        error_count = len(errors)\n        skipped_count = sum(1 for r in self.results if r[1] == \"skipped\")\n\n        summary_bits = []\n        if failed_count:\n            summary_bits.append(f\"{failed_count} failed\")\n        if error_count:\n            summary_bits.append(f\"{error_count} error{'s' if error_count != 1 else ''}\")\n        if passed_count:\n            summary_bits.append(f\"{passed_count} passed\")\n        if skipped_count:\n            summary_bits.append(f\"{skipped_count} skipped\")\n\n        summary_str = \", \".join(summary_bits) if summary_bits else \"no tests run\"\n        print(\n            f\"=================== {summary_str} in {elapsed:.2f}s ===================\"\n        )\n\n    def startTest(self, test):\n        \"\"\"Called right before each test method.\"\"\"\n        super().startTest(test)\n        # Print a dot or some indicator\n        print(\".\", end=\"\", flush=True)\n\n    def addSuccess(self, test):\n        super().addSuccess(test)\n        self.results.append((self._test_id(test), \"passed\", None))\n\n    def addFailure(self, test, err):\n        super().addFailure(test, err)\n        self.results.append((self._test_id(test), \"failed\", err))\n\n    def addError(self, test, err):\n        super().addError(test, err)\n        self.results.append((self._test_id(test), \"error\", err))\n\n    def addSkip(self, test, reason):\n        super().addSkip(test, reason)\n        self.results.append((self._test_id(test), \"skipped\", reason))\n\n    def _print_traceback(self, err_info):\n        \"\"\"Helper to print the traceback like pytest does.\"\"\"\n        if isinstance(err_info, tuple) and len(err_info) == 3:\n            exc_type, exc_value, tb = err_info\n            traceback.print_exception(exc_type, exc_value, tb, file=sys.stdout)\n        else:\n            print(str(err_info))\n\n    def _test_id(self, test):\n        try:\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}::{test._testMethodName}\"\n        except AttributeError:\n            # Fallback for _ErrorHolder objects\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}\"\n\n\nclass PytestLikeRunner(unittest.TextTestRunner):\n    \"\"\"A custom TextTestRunner that uses the PytestLikeResult.\"\"\"\n\n    resultclass = PytestLikeResult\n\n    def run(self, test):\n        result = self._makeResult()\n        # Overriding run to ensure we call startTestRun/stopTestRun\n        result.startTestRun()\n        test(result)\n        result.stopTestRun()\n        return result\n\n\ndef main():\n    loader = unittest.TestLoader()\n    suite = loader.discover(\"r2e_tests\")  # discover tests in r2e_tests/\n    runner = PytestLikeRunner(verbosity=0)\n    result = runner.run(suite)\n    # Exit with code 0 if all tests passed/skipped, 1 if there were fails/errors\n    sys.exit(not result.wasSuccessful())\n\n\nif __name__ == \"__main__\":\n    main()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "unittest_custom_runner.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Pillow installation attempts...\nVerifying Pillow installation...\n✅ Pillow installation successful!\nSuccessfully installed Pillow\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 12 packages in 9ms\nInstalled 12 packages in 267ms\n + coverage==7.6.10\n + exceptiongroup==1.2.2\n + iniconfig==2.0.0\n + packaging==24.2\n + pluggy==1.5.0\n + pyqt5==5.15.11\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.16.1\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + setuptools==75.8.0\n + tomli==2.2.1\nResolved 2 packages in 804ms\nPrepared 1 package in 14.39s\nInstalled 2 packages in 2ms\n + olefile==0.47\n + pillow==4.2.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_04f28b691e961bff44b269652421f447add04c5c)\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_04f28b691e961bff44b269652421f447add04c5c\n...........\ncollected 11 items\n\n==================== short test summary info ====================\nPASSED test_1::MorphTests::test_corner\nPASSED test_1::MorphTests::test_dialation8\nPASSED test_1::MorphTests::test_edge\nPASSED test_1::MorphTests::test_erosion4\nPASSED test_1::MorphTests::test_erosion8\nPASSED test_1::MorphTests::test_lut\nPASSED test_1::MorphTests::test_mirroring\nPASSED test_1::MorphTests::test_negate\nPASSED test_1::MorphTests::test_no_operator_loaded\nPASSED test_1::MorphTests::test_non_binary_images\nPASSED test_1::MorphTests::test_str_to_img\n=================== 11 passed in 0.12s ===================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_04f28b691e961bff44b269652421f447add04c5c\n...........\ncollected 11 items\n\n=================================== ERRORS =====================================\n__________ MorphTests.test_negate __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_04f28b691e961bff44b269652421f447add04c5c/r2e_tests/test_1.py\", line 198, in test_negate\n    mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0',\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_04f28b691e961bff44b269652421f447add04c5c/PIL/ImageMorph.py\", line 192, in __init__\n    self.lut = LutBuilder(patterns=patterns).build_lut()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_04f28b691e961bff44b269652421f447add04c5c/PIL/ImageMorph.py\", line 175, in build_lut\n    self.lut[i] = [0, 1][r]\nTypeError: list indices must be integers or slices, not str\n\n==================== short test summary info ====================\nPASSED test_1::MorphTests::test_corner\nPASSED test_1::MorphTests::test_dialation8\nPASSED test_1::MorphTests::test_edge\nPASSED test_1::MorphTests::test_erosion4\nPASSED test_1::MorphTests::test_erosion8\nPASSED test_1::MorphTests::test_lut\nPASSED test_1::MorphTests::test_mirroring\nERROR test_1::MorphTests::test_negate\nPASSED test_1::MorphTests::test_no_operator_loaded\nPASSED test_1::MorphTests::test_non_binary_images\nPASSED test_1::MorphTests::test_str_to_img\n=================== 1 error, 10 passed in 0.03s ===================\n",
    "old_commit_res_stderr": ""
}
PIL/ImageMorph.py Tests/test_imagemorph.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** TypeError When Applying Negate Operation: List Indices Must Be Integers, Not Str

**Description:**
When using the negate operation in `ImageMorph`, a `TypeError` is raised indicating that list indices must be integers, not strings. This error occurs because the code attempts to use a string as an index for a list, which is not allowed in Python.

**Example Code:**
```python
def test_negate():
    mop = ImageMorph.MorphOp(patterns=[
        '1:(... ... ...)->0',
        'N:(00. 01. ...)->1'
    ])
    count, Aout = mop.apply(some_image)
```
In this example, applying the negate operation with the specified patterns results in a `TypeError`.

**Expected Behavior:**
The negate operation should process the image correctly without raising any errors, producing the expected transformed image.

**Actual Behavior:**
A `TypeError` is raised with the message:
```
TypeError: list indices must be integers or slices, not str
```
This prevents the negate operation from completing successfully.

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

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

--- BEGIN FILE ---
```
### PIL/ImageMorph.py
# A binary morphology add-on for the Python Imaging Library
#
# History:
#   2014-06-04 Initial version.
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>

from __future__ import print_function

from . import Image, _imagingmorph
import re

LUT_SIZE = 1 << 9


class LutBuilder(object):
    """A class for building a MorphLut from a descriptive language

      The input patterns is a list of a strings sequences like these::

          4:(...
             .1.
             111)->1

      (whitespaces including linebreaks are ignored). The option 4
      describes a series of symmetry operations (in this case a
      4-rotation), the pattern is described by:

      - . or X - Ignore
      - 1 - Pixel is on
      - 0 - Pixel is off

      The result of the operation is described after "->" string.

      The default is to return the current pixel value, which is
      returned if no other match is found.

      Operations:

      - 4 - 4 way rotation
      - N - Negate
      - 1 - Dummy op for no other operation (an op must always be given)
      - M - Mirroring

      Example::

          lb = LutBuilder(patterns = ["4:(... .1. 111)->1"])
          lut = lb.build_lut()

    """
    def __init__(self, patterns=None, op_name=None):
        if patterns is not None:
            self.patterns = patterns
        else:
            self.patterns = []
        self.lut = None
        if op_name is not None:
            known_patterns = {
                'corner': ['1:(... ... ...)->0',
                           '4:(00. 01. ...)->1'],
                'dilation4': ['4:(... .0. .1.)->1'],
                'dilation8': ['4:(... .0. .1.)->1',
                              '4:(... .0. ..1)->1'],
                'erosion4': ['4:(... .1. .0.)->0'],
                'erosion8': ['4:(... .1. .0.)->0',
                             '4:(... .1. ..0)->0'],
                'edge': ['1:(... ... ...)->0',
                         '4:(.0. .1. ...)->1',
                         '4:(01. .1. ...)->1']
            }
            if op_name not in known_patterns:
                raise Exception('Unknown pattern '+op_name+'!')

            self.patterns = known_patterns[op_name]

    def add_patterns(self, patterns):
        self.patterns += patterns

    def build_default_lut(self):
        symbols = [0, 1]
        m = 1 << 4  # pos of current pixel
        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))

    def get_lut(self):
        return self.lut

    def _string_permute(self, pattern, permutation):
        """string_permute takes a pattern and a permutation and returns the
        string permuted according to the permutation list.
        """
        assert(len(permutation) == 9)
        return ''.join(pattern[p] for p in permutation)

    def _pattern_permute(self, basic_pattern, options, basic_result):
        """pattern_permute takes a basic pattern and its result and clones
        the pattern according to the modifications described in the $options
        parameter. It returns a list of all cloned patterns."""
        patterns = [(basic_pattern, basic_result)]

        # rotations
        if '4' in options:
            res = patterns[-1][1]
            for i in range(4):
                patterns.append(
                    (self._string_permute(patterns[-1][0], [6, 3, 0,
                                                            7, 4, 1,
                                                            8, 5, 2]), res))
        # mirror
        if 'M' in options:
            n = len(patterns)
            for pattern, res in patterns[0:n]:
                patterns.append(
                    (self._string_permute(pattern, [2, 1, 0,
                                                    5, 4, 3,
                                                    8, 7, 6]), res))

        # negate
        if 'N' in options:
            n = len(patterns)
            for pattern, res in patterns[0:n]:
                # Swap 0 and 1
                pattern = (pattern
                           .replace('0', 'Z')
                           .replace('1', '0')
                           .replace('Z', '1'))
                res = '%d' % (1-int(res))
                patterns.append((pattern, res))

        return patterns

    def build_lut(self):
        """Compile all patterns into a morphology lut.

        TBD :Build based on (file) morphlut:modify_lut
        """
        self.build_default_lut()
        patterns = []

        # Parse and create symmetries of the patterns strings
        for p in self.patterns:
            m = re.search(
                r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n', ''))
            if not m:
                raise Exception('Syntax error in pattern "'+p+'"')
            options = m.group(1)
            pattern = m.group(2)
            result = int(m.group(3))

            # Get rid of spaces
            pattern = pattern.replace(' ', '').replace('\n', '')

            patterns += self._pattern_permute(pattern, options, result)

#        # Debugging
#        for p,r in patterns:
#            print(p,r)
#        print('--')

        # compile the patterns into regular expressions for speed
        for i, pattern in enumerate(patterns):
            p = pattern[0].replace('.', 'X').replace('X', '[01]')
            p = re.compile(p)
            patterns[i] = (p, pattern[1])

        # Step through table and find patterns that match.
        # Note that all the patterns are searched. The last one
        # caught overrides
        for i in range(LUT_SIZE):
            # Build the bit pattern
            bitpattern = bin(i)[2:]
            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]

            for p, r in patterns:
                if p.match(bitpattern):
                    self.lut[i] = [0, 1][r]

        return self.lut


class MorphOp(object):
    """A class for binary morphological operators"""

    def __init__(self,
                 lut=None,
                 op_name=None,
                 patterns=None):
        """Create a binary morphological operator"""
        self.lut = lut
        if op_name is not None:
            self.lut = LutBuilder(op_name=op_name).build_lut()
        elif patterns is not None:
            self.lut = LutBuilder(patterns=patterns).build_lut()

    def apply(self, image):
        """Run a single morphological operation on an image

        Returns a tuple of the number of changed pixels and the
        morphed image"""
        if self.lut is None:
            raise Exception('No operator loaded')

        if image.mode != 'L':
            raise Exception('Image must be binary, meaning it must use mode L')
        outimage = Image.new(image.mode, image.size, None)
        count = _imagingmorph.apply(
            bytes(self.lut), image.im.id, outimage.im.id)
        return count, outimage

    def match(self, image):
        """Get a list of coordinates matching the morphological operation on
        an image.

        Returns a list of tuples of (x,y) coordinates
        of all matching pixels."""
        if self.lut is None:
            raise Exception('No operator loaded')

        if image.mode != 'L':
            raise Exception('Image must be binary, meaning it must use mode L')
        return _imagingmorph.match(bytes(self.lut), image.im.id)

    def get_on_pixels(self, image):
        """Get a list of all turned on pixels in a binary image

        Returns a list of tuples of (x,y) coordinates
        of all matching pixels."""

        if image.mode != 'L':
            raise Exception('Image must be binary, meaning it must use mode L')
        return _imagingmorph.get_on_pixels(image.im.id)

    def load_lut(self, filename):
        """Load an operator from an mrl file"""
        with open(filename, 'rb') as f:
            self.lut = bytearray(f.read())

        if len(self.lut) != LUT_SIZE:
            self.lut = None
            raise Exception('Wrong size operator file!')

    def save_lut(self, filename):
        """Save an operator to an mrl file"""
        if self.lut is None:
            raise Exception('No operator loaded')
        with open(filename, 'wb') as f:
            f.write(self.lut)

    def set_lut(self, lut):
        """Set the lut from an external source"""
        self.lut = lut

```
--- 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:** TypeError When Applying Negate Operation: List Indices Must Be Integers, Not Str

**Description:**
When using the negate operation in `ImageMorph`, a `TypeError` is raised indicating that list indices must be integers, not strings. This error occurs because the code attempts to use a string as an index for a list, which is not allowed in Python.

**Example Code:**
```python
def test_negate():
    mop = ImageMorph.MorphOp(patterns=[
        '1:(... ... ...)->0',
        'N:(00. 01. ...)->1'
    ])
    count, Aout = mop.apply(some_image)
```
In this example, applying the negate operation with the specified patterns results in a `TypeError`.

**Expected Behavior:**
The negate operation should process the image correctly without raising any errors, producing the expected transformed image.

**Actual Behavior:**
A `TypeError` is raised with the message:
```
TypeError: list indices must be integers or slices, not str
```
This prevents the negate operation from completing successfully.

[/ISSUE]
{
    "MorphTests.test_corner": "PASSED",
    "MorphTests.test_dialation8": "PASSED",
    "MorphTests.test_edge": "PASSED",
    "MorphTests.test_erosion4": "PASSED",
    "MorphTests.test_erosion8": "PASSED",
    "MorphTests.test_lut": "PASSED",
    "MorphTests.test_mirroring": "PASSED",
    "MorphTests.test_negate": "PASSED",
    "MorphTests.test_no_operator_loaded": "PASSED",
    "MorphTests.test_non_binary_images": "PASSED",
    "MorphTests.test_str_to_img": "PASSED"
}
diff --git a/PIL/ImageMorph.py b/PIL/ImageMorph.py
index dcce63fa4..d2367737f 100644
--- a/PIL/ImageMorph.py
+++ b/PIL/ImageMorph.py
@@ -123,7 +123,7 @@ class LutBuilder(object):
                            .replace('0', 'Z')
                            .replace('1', '0')
                            .replace('Z', '1'))
-                res = '%d' % (1-int(res))
+                res = 1-int(res)
                 patterns.append((pattern, res))
 
         return patterns
@@ -152,8 +152,8 @@ class LutBuilder(object):
             patterns += self._pattern_permute(pattern, options, result)
 
 #        # Debugging
-#        for p,r in patterns:
-#            print(p,r)
+#        for p, r in patterns:
+#            print(p, r)
 #        print('--')
 
         # compile the patterns into regular expressions for speed

{"PIL/ImageMorph.py": "# A binary morphology add-on for the Python Imaging Library\n#\n# History:\n#   2014-06-04 Initial version.\n#\n# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>\n\nfrom __future__ import print_function\n\nfrom . import Image, _imagingmorph\nimport re\n\nLUT_SIZE = 1 << 9\n\n\nclass LutBuilder(object):\n    \"\"\"A class for building a MorphLut from a descriptive language\n\n      The input patterns is a list of a strings sequences like these::\n\n          4:(...\n             .1.\n             111)->1\n\n      (whitespaces including linebreaks are ignored). The option 4\n      describes a series of symmetry operations (in this case a\n      4-rotation), the pattern is described by:\n\n      - . or X - Ignore\n      - 1 - Pixel is on\n      - 0 - Pixel is off\n\n      The result of the operation is described after \"->\" string.\n\n      The default is to return the current pixel value, which is\n      returned if no other match is found.\n\n      Operations:\n\n      - 4 - 4 way rotation\n      - N - Negate\n      - 1 - Dummy op for no other operation (an op must always be given)\n      - M - Mirroring\n\n      Example::\n\n          lb = LutBuilder(patterns = [\"4:(... .1. 111)->1\"])\n          lut = lb.build_lut()\n\n    \"\"\"\n    def __init__(self, patterns=None, op_name=None):\n        if patterns is not None:\n            self.patterns = patterns\n        else:\n            self.patterns = []\n        self.lut = None\n        if op_name is not None:\n            known_patterns = {\n                'corner': ['1:(... ... ...)->0',\n                           '4:(00. 01. ...)->1'],\n                'dilation4': ['4:(... .0. .1.)->1'],\n                'dilation8': ['4:(... .0. .1.)->1',\n                              '4:(... .0. ..1)->1'],\n                'erosion4': ['4:(... .1. .0.)->0'],\n                'erosion8': ['4:(... .1. .0.)->0',\n                             '4:(... .1. ..0)->0'],\n                'edge': ['1:(... ... ...)->0',\n                         '4:(.0. .1. ...)->1',\n                         '4:(01. .1. ...)->1']\n            }\n            if op_name not in known_patterns:\n                raise Exception('Unknown pattern '+op_name+'!')\n\n            self.patterns = known_patterns[op_name]\n\n    def add_patterns(self, patterns):\n        self.patterns += patterns\n\n    def build_default_lut(self):\n        symbols = [0, 1]\n        m = 1 << 4  # pos of current pixel\n        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))\n\n    def get_lut(self):\n        return self.lut\n\n    def _string_permute(self, pattern, permutation):\n        \"\"\"string_permute takes a pattern and a permutation and returns the\n        string permuted according to the permutation list.\n        \"\"\"\n        assert(len(permutation) == 9)\n        return ''.join(pattern[p] for p in permutation)\n\n    def _pattern_permute(self, basic_pattern, options, basic_result):\n        \"\"\"pattern_permute takes a basic pattern and its result and clones\n        the pattern according to the modifications described in the $options\n        parameter. It returns a list of all cloned patterns.\"\"\"\n        patterns = [(basic_pattern, basic_result)]\n\n        # rotations\n        if '4' in options:\n            res = patterns[-1][1]\n            for i in range(4):\n                patterns.append(\n                    (self._string_permute(patterns[-1][0], [6, 3, 0,\n                                                            7, 4, 1,\n                                                            8, 5, 2]), res))\n        # mirror\n        if 'M' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                patterns.append(\n                    (self._string_permute(pattern, [2, 1, 0,\n                                                    5, 4, 3,\n                                                    8, 7, 6]), res))\n\n        # negate\n        if 'N' in options:\n            n = len(patterns)\n            for pattern, res in patterns[0:n]:\n                # Swap 0 and 1\n                pattern = (pattern\n                           .replace('0', 'Z')\n                           .replace('1', '0')\n                           .replace('Z', '1'))\n                res = '%d' % (1-int(res))\n                patterns.append((pattern, res))\n\n        return patterns\n\n    def build_lut(self):\n        \"\"\"Compile all patterns into a morphology lut.\n\n        TBD :Build based on (file) morphlut:modify_lut\n        \"\"\"\n        self.build_default_lut()\n        patterns = []\n\n        # Parse and create symmetries of the patterns strings\n        for p in self.patterns:\n            m = re.search(\n                r'(\\w*):?\\s*\\((.+?)\\)\\s*->\\s*(\\d)', p.replace('\\n', ''))\n            if not m:\n                raise Exception('Syntax error in pattern \"'+p+'\"')\n            options = m.group(1)\n            pattern = m.group(2)\n            result = int(m.group(3))\n\n            # Get rid of spaces\n            pattern = pattern.replace(' ', '').replace('\\n', '')\n\n            patterns += self._pattern_permute(pattern, options, result)\n\n#        # Debugging\n#        for p,r in patterns:\n#            print(p,r)\n#        print('--')\n\n        # compile the patterns into regular expressions for speed\n        for i, pattern in enumerate(patterns):\n            p = pattern[0].replace('.', 'X').replace('X', '[01]')\n            p = re.compile(p)\n            patterns[i] = (p, pattern[1])\n\n        # Step through table and find patterns that match.\n        # Note that all the patterns are searched. The last one\n        # caught overrides\n        for i in range(LUT_SIZE):\n            # Build the bit pattern\n            bitpattern = bin(i)[2:]\n            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]\n\n            for p, r in patterns:\n                if p.match(bitpattern):\n                    self.lut[i] = [0, 1][r]\n\n        return self.lut\n\n\nclass MorphOp(object):\n    \"\"\"A class for binary morphological operators\"\"\"\n\n    def __init__(self,\n                 lut=None,\n                 op_name=None,\n                 patterns=None):\n        \"\"\"Create a binary morphological operator\"\"\"\n        self.lut = lut\n        if op_name is not None:\n            self.lut = LutBuilder(op_name=op_name).build_lut()\n        elif patterns is not None:\n            self.lut = LutBuilder(patterns=patterns).build_lut()\n\n    def apply(self, image):\n        \"\"\"Run a single morphological operation on an image\n\n        Returns a tuple of the number of changed pixels and the\n        morphed image\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        outimage = Image.new(image.mode, image.size, None)\n        count = _imagingmorph.apply(\n            bytes(self.lut), image.im.id, outimage.im.id)\n        return count, outimage\n\n    def match(self, image):\n        \"\"\"Get a list of coordinates matching the morphological operation on\n        an image.\n\n        Returns a list of tuples of (x,y) coordinates\n        of all matching pixels.\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        return _imagingmorph.match(bytes(self.lut), image.im.id)\n\n    def get_on_pixels(self, image):\n        \"\"\"Get a list of all turned on pixels in a binary image\n\n        Returns a list of tuples of (x,y) coordinates\n        of all matching pixels.\"\"\"\n\n        if image.mode != 'L':\n            raise Exception('Image must be binary, meaning it must use mode L')\n        return _imagingmorph.get_on_pixels(image.im.id)\n\n    def load_lut(self, filename):\n        \"\"\"Load an operator from an mrl file\"\"\"\n        with open(filename, 'rb') as f:\n            self.lut = bytearray(f.read())\n\n        if len(self.lut) != LUT_SIZE:\n            self.lut = None\n            raise Exception('Wrong size operator file!')\n\n    def save_lut(self, filename):\n        \"\"\"Save an operator to an mrl file\"\"\"\n        if self.lut is None:\n            raise Exception('No operator loaded')\n        with open(filename, 'wb') as f:\n            f.write(self.lut)\n\n    def set_lut(self, lut):\n        \"\"\"Set the lut from an external source\"\"\"\n        self.lut = lut\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:** TypeError When Applying Negate Operation: List Indices Must Be Integers, Not Str

**Description:**
When using the negate operation in `ImageMorph`, a `TypeError` is raised indicating that list indices must be integers, not strings. This error occurs because the code attempts to use a string as an index for a list, which is not allowed in Python.

**Example Code:**
```python
def test_negate():
    mop = ImageMorph.MorphOp(patterns=[
        '1:(... ... ...)->0',
        'N:(00. 01. ...)->1'
    ])
    count, Aout = mop.apply(some_image)
```
In this example, applying the negate operation with the specified patterns results in a `TypeError`.

**Expected Behavior:**
The negate operation should process the image correctly without raising any errors, producing the expected transformed image.

**Actual Behavior:**
A `TypeError` is raised with the message:
```
TypeError: list indices must be integers or slices, not str
```
This prevents the negate operation from completing successfully.

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

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

--- BEGIN FILE ---
```
### PIL/ImageMorph.py
# A binary morphology add-on for the Python Imaging Library
#
# History:
#   2014-06-04 Initial version.
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>

from __future__ import print_function

from . import Image, _imagingmorph
import re

LUT_SIZE = 1 << 9


class LutBuilder(object):
    """A class for building a MorphLut from a descriptive language

      The input patterns is a list of a strings sequences like these::

          4:(...
             .1.
             111)->1

      (whitespaces including linebreaks are ignored). The option 4
      describes a series of symmetry operations (in this case a
      4-rotation), the pattern is described by:

      - . or X - Ignore
      - 1 - Pixel is on
      - 0 - Pixel is off

      The result of the operation is described after "->" string.

      The default is to return the current pixel value, which is
      returned if no other match is found.

      Operations:

      - 4 - 4 way rotation
      - N - Negate
      - 1 - Dummy op for no other operation (an op must always be given)
      - M - Mirroring

      Example::

          lb = LutBuilder(patterns = ["4:(... .1. 111)->1"])
          lut = lb.build_lut()

    """
    def __init__(self, patterns=None, op_name=None):
        if patterns is not None:
            self.patterns = patterns
        else:
            self.patterns = []
        self.lut = None
        if op_name is not None:
            known_patterns = {
                'corner': ['1:(... ... ...)->0',
                           '4:(00. 01. ...)->1'],
                'dilation4': ['4:(... .0. .1.)->1'],
                'dilation8': ['4:(... .0. .1.)->1',
                              '4:(... .0. ..1)->1'],
                'erosion4': ['4:(... .1. .0.)->0'],
                'erosion8': ['4:(... .1. .0.)->0',
                             '4:(... .1. ..0)->0'],
                'edge': ['1:(... ... ...)->0',
                         '4:(.0. .1. ...)->1',
                         '4:(01. .1. ...)->1']
            }
            if op_name not in known_patterns:
                raise Exception('Unknown pattern '+op_name+'!')

            self.patterns = known_patterns[op_name]

    def add_patterns(self, patterns):
        self.patterns += patterns

    def build_default_lut(self):
        symbols = [0, 1]
        m = 1 << 4  # pos of current pixel
        self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))

    def get_lut(self):
        return self.lut

    def _string_permute(self, pattern, permutation):
        """string_permute takes a pattern and a permutation and returns the
        string permuted according to the permutation list.
        """
        assert(len(permutation) == 9)
        return ''.join(pattern[p] for p in permutation)

    def _pattern_permute(self, basic_pattern, options, basic_result):
        """pattern_permute takes a basic pattern and its result and clones
        the pattern according to the modifications described in the $options
        parameter. It returns a list of all cloned patterns."""
        patterns = [(basic_pattern, basic_result)]

        # rotations
        if '4' in options:
            res = patterns[-1][1]
            for i in range(4):
                patterns.append(
                    (self._string_permute(patterns[-1][0], [6, 3, 0,
                                                            7, 4, 1,
                                                            8, 5, 2]), res))
        # mirror
        if 'M' in options:
            n = len(patterns)
            for pattern, res in patterns[0:n]:
                patterns.append(
                    (self._string_permute(pattern, [2, 1, 0,
                                                    5, 4, 3,
                                                    8, 7, 6]), res))

        # negate
        if 'N' in options:
            n = len(patterns)
            for pattern, res in patterns[0:n]:
                # Swap 0 and 1
                pattern = (pattern
                           .replace('0', 'Z')
                           .replace('1', '0')
                           .replace('Z', '1'))
                res = '%d' % (1-int(res))
                patterns.append((pattern, res))

        return patterns

    def build_lut(self):
        """Compile all patterns into a morphology lut.

        TBD :Build based on (file) morphlut:modify_lut
        """
        self.build_default_lut()
        patterns = []

        # Parse and create symmetries of the patterns strings
        for p in self.patterns:
            m = re.search(
                r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n', ''))
            if not m:
                raise Exception('Syntax error in pattern "'+p+'"')
            options = m.group(1)
            pattern = m.group(2)
            result = int(m.group(3))

            # Get rid of spaces
            pattern = pattern.replace(' ', '').replace('\n', '')

            patterns += self._pattern_permute(pattern, options, result)

#        # Debugging
#        for p,r in patterns:
#            print(p,r)
#        print('--')

        # compile the patterns into regular expressions for speed
        for i, pattern in enumerate(patterns):
            p = pattern[0].replace('.', 'X').replace('X', '[01]')
            p = re.compile(p)
            patterns[i] = (p, pattern[1])

        # Step through table and find patterns that match.
        # Note that all the patterns are searched. The last one
        # caught overrides
        for i in range(LUT_SIZE):
            # Build the bit pattern
            bitpattern = bin(i)[2:]
            bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]

            for p, r in patterns:
                if p.match(bitpattern):
                    self.lut[i] = [0, 1][r]

        return self.lut


class MorphOp(object):
    """A class for binary morphological operators"""

    def __init__(self,
                 lut=None,
                 op_name=None,
                 patterns=None):
        """Create a binary morphological operator"""
        self.lut = lut
        if op_name is not None:
            self.lut = LutBuilder(op_name=op_name).build_lut()
        elif patterns is not None:
            self.lut = LutBuilder(patterns=patterns).build_lut()

    def apply(self, image):
        """Run a single morphological operation on an image

        Returns a tuple of the number of changed pixels and the
        morphed image"""
        if self.lut is None:
            raise Exception('No operator loaded')

        if image.mode != 'L':
            raise Exception('Image must be binary, meaning it must use mode L')
        outimage = Image.new(image.mode, image.size, None)
        count = _imagingmorph.apply(
            bytes(self.lut), image.im.id, outimage.im.id)
        return count, outimage

    def match(self, image):
        """Get a list of coordinates matching the morphological operation on
        an image.

        Returns a list of tuples of (x,y) coordinates
        of all matching pixels."""
        if self.lut is None:
            raise Exception('No operator loaded')

        if image.mode != 'L':
            raise Exception('Image must be binary, meaning it must use mode L')
        return _imagingmorph.match(bytes(self.lut), image.im.id)

    def get_on_pixels(self, image):
        """Get a list of all turned on pixels in a binary image

        Returns a list of tuples of (x,y) coordinates
        of all matching pixels."""

        if image.mode != 'L':
            raise Exception('Image must be binary, meaning it must use mode L')
        return _imagingmorph.get_on_pixels(image.im.id)

    def load_lut(self, filename):
        """Load an operator from an mrl file"""
        with open(filename, 'rb') as f:
            self.lut = bytearray(f.read())

        if len(self.lut) != LUT_SIZE:
            self.lut = None
            raise Exception('Wrong size operator file!')

    def save_lut(self, filename):
        """Save an operator to an mrl file"""
        if self.lut is None:
            raise Exception('No operator loaded')
        with open(filename, 'wb') as f:
            f.write(self.lut)

    def set_lut(self, lut):
        """Set the lut from an external source"""
        self.lut = lut

```
--- 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